Provides advanced search functionality - user-friendly ability to construct a complex query with boolean conditions.
import { AdvancedSearch } from '@folio/stripes/components';
// in component body
const searchOptions = [{
id: 'keyword',
label: 'Keyword',
value: 'keyword',
}, {
id: 'name',
label: 'Name',
value: 'name',
}];
const keywordOption = 'keyword';
const firstRowInitialSearch = {};
<AdvancedSearch
open={isOpen}
searchOptions={searchOptions}
defaultSearchOptionValue={keywordOption}
firstRowInitialSearch={firstRowInitialSearch}
onSearch={handleSearch}
onCancel={handleCancel}
/>
In some cases users may want to do custom formatting of query string. This can be done with the use of rowFormatter
or queryBuilder
.
rowFormatter
can be used to combine the boolean operator, searched term and search option. For example, if you'd like to change comparison operator from default ==
to =
.
queryBuilder
gives users complete control over query construction. AdvancedSearch
will call this function with an array of object representing rows and users can use that to meet their requirements.
More about rowFormatter
and queryBuilder
in Props.
Note: if you change default comparison operator, or default boolean operator symbols - you'll also need to provide splitRows
prop for custom parsing of queries into rows.
AdvancedSearch
also accepts a function as children and will call it with object containing resetRows
function - it will clear internal state. It may be used with "Reset all" buttons on search pages to also clear AdvancedSearch
rows.
import { AdvancedSearch } from '@folio/stripes/components';
// in component body
const queryBuilder = (rows, rowFormatter) => {
const formatRowCondition = (row) => {
// use default row formatter, but wrap each search term with parentheses
return `(${rowFormatter(row.searchOption, row.query, '==')})`;
};
return rows.reduce((formattedQuery, row, index) => {
const rowCondition = formatRowCondition(row);
const boolMap = {
and: '&&',
or: '||',
not: '!',
};
return `${formattedQuery} ${boolMap[row.bool]} ${rowCondition}`;
}, '');
};
<AdvancedSearch
open={isOpen}
searchOptions={searchOptions}
defaultSearchOptionValue={keywordOption}
firstRowInitialSearch={firstRowInitialSearch}
queryBuilder={queryBuilder}
rowFormatter={rowFormatter}
onSearch={handleSearch}
onCancel={handleCancel}
>
{({ resetRows}) => (
<Button
onClick={resetRows}
>
Reset all
</Button>
)}
</AdvancedSearch>
To handle queries that a user typed in, as opposed to what was generated by <AdvancedSearch>
you may need to provide custom queryToRow
prop.
For example, user types in keyword exactPhrase test or id containsAll 123-456
into <SearchAndSort>
query input and opens Advanced Search.
To parse this query string into rows you must pass queryToRow
where you define how you'd like to parse it:
// in component body
const queryToRow = (row) => {
const queryString = row.query; // this in our example will be equal to `"keyword exactPhrase test or id containsAll 123-456"`
const splitIntoRowsRegex = /regex-to-split-into-rows/g;
const matches = [...queryString.matchAll(splitIntoRowsRegex)];
// let's assume `matches` is `["keyword exactPhrase test", "or id containsAll 123-456"]`
return matches.map((match) => {
const { operator, option, _match, value } = parseSingleRow(match);
return {
[FIELD_NAMES.QUERY]: value,
[FIELD_NAMES.BOOL]: operator,
[FIELD_NAMES.SEARCH_OPTION]: option,
[FIELD_NAMES.MATCH]: _match,
};
});
};
<AdvancedSearch
...
queryToRow={queryToRow}
>
...
</AdvancedSearch>
In the example above, custom rowFormatter
will wrap each search term with parentheses. And queryBuilder
is written so that boolean conditions will be written as symbols (&&
, ||
, !
) instead of default text representation (and
, or
, not
).
Name | Type | Description | Required |
---|---|---|---|
children | function | Pass any a function that will accept { resetRows } . resetRows can be used to clear AdvancedSearch state. |
false |
open | boolean | Controls visibility of AdvancedSearch modal |
false |
searchOptions | array | Array of search options. Format: [{ label, value, id }] id is an optional property. AdvancedSearch will add Query as first option. |
true |
onSearch | func | Callback fired when search is performed. Called with two arguments: query - formatted query string and rows - array of non-empty rows with shape { bool, query, searchOption } |
true |
onCancel | func | Callback fired when the user clicks the cancel button. | true |
defaultSearchOptionValue | string | One of the options in searchOptions that will be selected by default in all rows |
false |
firstRowInitialSearch | object | Object with shape { query, option } - will be used to populate first row with default values |
false |
hasMatchSelection | boolean | Show/hide search match option dropdown | false |
hasQueryOption | boolean | Controls whether Query search option should be appended to search options list |
false |
rowFormatter | func | Function that will be used to combine boolean, query and search option of each row. Signature: (searchOption, query, bool, comparator) => {...} . Returned values will be used by queryBuilder to join them together. Note: no need to add bool to resulting string here - it will be added by queryBuilder . |
false |
queryBuilder | func | Function that will be used to construct the search query. Signature: (rows, rowFormatter) => {...} . rows - array of shapes { query, searchOption, query } , rowFormatter - the prop. Returned value will be passed as the first argument to onSearch . |
false |
queryToRow | func | Function that will be used to parse a query string into a row object. | false |
stripes-components
also provides the useAdvancedSearch
hook. It has the same API as the <AdvancedSearch>
component, but doesn't render the UI. The hook can be used to get formatted advanced search rows and query outside of <AdvancedSearch>
component. For example, when you need to run advanced search on page load with initial query from URL
/// SearchForm.js
const SearchForm = (...) => {
...
return (
...
<AdvancedSearch // regular use of AdvancedSearch that shows the modal
open
searchOptions={advancedSearchOptions}
defaultSearchOptionValue={searchableIndexesValues.KEYWORD}
firstRowInitialSearch={advancedSearchDefaultSearch}
onSearch={handleAdvancedSearch}
onCancel={() => setIsAdvancedSearchOpen(false)}
>
...
</AdvancedSearch>
);
}
/// SearchRoute.js
const SearchRoute = (...) => {
const { query, option } = queryString.parse(location.search);
const { filledRows, query: formattedQuery } = useAdvancedSearch({
defaultSearchOptionValue: 'keyword',
firstRowInitialSearch: { query, option },
});
// here `formattedQuery` can be used to make a request to BE
useEffect(() => {
fetch(`/some-url?query=${formattedQuery}`);
}, []);
return (...);
}