Skip to content

Commit

Permalink
feat(custom): update onDropdownInputSelect prop to replace default be…
Browse files Browse the repository at this point in the history
…havior, add documentation

specify a type for the props passed into onDropdownInputSelect and pass back the searchFields used
in the FilterSearch component so we can properly remove/keep the right filters

J=BACK-2765
TEST=manual
  • Loading branch information
Jeffrey-Rhoads17 committed Jan 18, 2024
1 parent ac20087 commit 5545f1f
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 28 deletions.
2 changes: 1 addition & 1 deletion docs/search-ui-react.filtersearchprops.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface FilterSearchProps
| --- | --- | --- | --- |
| [customCssClasses?](./search-ui-react.filtersearchprops.customcssclasses.md) | | [FilterSearchCssClasses](./search-ui-react.filtersearchcssclasses.md) | _(Optional)_ CSS classes for customizing the component styling. |
| [label?](./search-ui-react.filtersearchprops.label.md) | | string | _(Optional)_ The display label for the component. |
| [onDropdownInputChange?](./search-ui-react.filtersearchprops.ondropdowninputchange.md) | | (value: string) => void | _(Optional)_ A function which is called when the input's value changes. This does not replace executeFilterSearch, but gets called just before it |
| [onDropdownInputChange?](./search-ui-react.filtersearchprops.ondropdowninputchange.md) | | (params: [OnDropdownInputChangeProps](./search-ui-react.ondropdowninputchangeprops.md)<!-- -->) =&gt; void | _(Optional)_ A function which is called everytime the input element's value changes. Helpful for removing static filters from the static filter state if a user clears the search input completely. Replaces the default behavior of calling executeFilterSearch whenever the input changes. |
| [onSelect?](./search-ui-react.filtersearchprops.onselect.md) | | (params: [OnSelectParams](./search-ui-react.onselectparams.md)<!-- -->) =&gt; void | _(Optional)_ A function which is called when a filter is selected. |
| [placeholder?](./search-ui-react.filtersearchprops.placeholder.md) | | string | _(Optional)_ The search input's placeholder text when no text has been entered by the user. Defaults to "Search here...". |
| [searchFields](./search-ui-react.filtersearchprops.searchfields.md) | | Omit&lt;SearchParameterField, 'fetchEntities'&gt;\[\] | An array of fieldApiName and entityType which indicates what to perform the filter search against. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

## FilterSearchProps.onDropdownInputChange property

A function which is called when the input's value changes. This does not replace executeFilterSearch, but gets called just before it
A function which is called everytime the input element's value changes. Helpful for removing static filters from the static filter state if a user clears the search input completely. Replaces the default behavior of calling executeFilterSearch whenever the input changes.

**Signature:**

```typescript
onDropdownInputChange?: (value: string) => void;
onDropdownInputChange?: (params: OnDropdownInputChangeProps) => void;
```
1 change: 1 addition & 0 deletions docs/search-ui-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
| [NumericalFacetProps](./search-ui-react.numericalfacetprops.md) | Props for the [StandardFacet()](./search-ui-react.standardfacet.md) component. |
| [NumericalFacetsCssClasses](./search-ui-react.numericalfacetscssclasses.md) | The CSS class interface for [NumericalFacets()](./search-ui-react.numericalfacets.md)<!-- -->. |
| [NumericalFacetsProps](./search-ui-react.numericalfacetsprops.md) | Props for the [NumericalFacets()](./search-ui-react.numericalfacets.md) component. |
| [OnDropdownInputChangeProps](./search-ui-react.ondropdowninputchangeprops.md) | The parameters that are passed into [FilterSearchProps.onDropdownInputChange](./search-ui-react.filtersearchprops.ondropdowninputchange.md)<!-- -->. |
| [OnSelectParams](./search-ui-react.onselectparams.md) | The parameters that are passed into [FilterSearchProps.onSelect](./search-ui-react.filtersearchprops.onselect.md)<!-- -->. |
| [PaginationCssClasses](./search-ui-react.paginationcssclasses.md) | The CSS classes used for pagination. |
| [PaginationProps](./search-ui-react.paginationprops.md) | Props for [Pagination()](./search-ui-react.pagination.md) component |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/search-ui-react](./search-ui-react.md) &gt; [OnDropdownInputChangeProps](./search-ui-react.ondropdowninputchangeprops.md) &gt; [executeFilterSearch](./search-ui-react.ondropdowninputchangeprops.executefiltersearch.md)

## OnDropdownInputChangeProps.executeFilterSearch property

A function that executes a filter search and updates the input and dropdown options with the response.

**Signature:**

```typescript
executeFilterSearch: (query?: string) => Promise<FilterSearchResponse | undefined>;
```
22 changes: 22 additions & 0 deletions docs/search-ui-react.ondropdowninputchangeprops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/search-ui-react](./search-ui-react.md) &gt; [OnDropdownInputChangeProps](./search-ui-react.ondropdowninputchangeprops.md)

## OnDropdownInputChangeProps interface

The parameters that are passed into [FilterSearchProps.onDropdownInputChange](./search-ui-react.filtersearchprops.ondropdowninputchange.md)<!-- -->.

**Signature:**

```typescript
interface OnDropdownInputChangeProps
```

## Properties

| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [executeFilterSearch](./search-ui-react.ondropdowninputchangeprops.executefiltersearch.md) | | (query?: string) =&gt; Promise&lt;FilterSearchResponse \| undefined&gt; | A function that executes a filter search and updates the input and dropdown options with the response. |
| [searchFields](./search-ui-react.ondropdowninputchangeprops.searchfields.md) | | Omit&lt;SearchParameterField, 'fetchEntities'&gt;\[\] | |
| [value](./search-ui-react.ondropdowninputchangeprops.value.md) | | string | The new input element's new value after the change |

11 changes: 11 additions & 0 deletions docs/search-ui-react.ondropdowninputchangeprops.searchfields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/search-ui-react](./search-ui-react.md) &gt; [OnDropdownInputChangeProps](./search-ui-react.ondropdowninputchangeprops.md) &gt; [searchFields](./search-ui-react.ondropdowninputchangeprops.searchfields.md)

## OnDropdownInputChangeProps.searchFields property

**Signature:**

```typescript
searchFields: Omit<SearchParameterField, 'fetchEntities'>[];
```
13 changes: 13 additions & 0 deletions docs/search-ui-react.ondropdowninputchangeprops.value.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/search-ui-react](./search-ui-react.md) &gt; [OnDropdownInputChangeProps](./search-ui-react.ondropdowninputchangeprops.md) &gt; [value](./search-ui-react.ondropdowninputchangeprops.value.md)

## OnDropdownInputChangeProps.value property

The new input element's new value after the change

**Signature:**

```typescript
value: string;
```
10 changes: 9 additions & 1 deletion etc/search-ui-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ export interface FilterSearchCssClasses extends AutocompleteResultCssClasses {
export interface FilterSearchProps {
customCssClasses?: FilterSearchCssClasses;
label?: string;
onDropdownInputChange?: (value: string) => void;
onDropdownInputChange?: (params: OnDropdownInputChangeProps) => void;
onSelect?: (params: OnSelectParams) => void;
placeholder?: string;
searchFields: Omit<SearchParameterField, 'fetchEntities'>[];
Expand Down Expand Up @@ -462,6 +462,14 @@ export interface NumericalFacetsProps extends Omit<StandardFacetsProps, 'exclude
// @public
export type OnDragHandler = (center: mapboxgl_2.LngLat, bounds: mapboxgl_2.LngLatBounds) => void;

// @public
export interface OnDropdownInputChangeProps {
executeFilterSearch: (query?: string) => Promise<FilterSearchResponse | undefined>;
// (undocumented)
searchFields: Omit<SearchParameterField, 'fetchEntities'>[];
value: string;
}

// @public
export type onSearchFunc = (searchEventData: {
verticalKey?: string;
Expand Down
43 changes: 36 additions & 7 deletions src/components/FilterSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,22 @@ export interface OnSelectParams {
executeFilterSearch: (query?: string) => Promise<FilterSearchResponse | undefined>
}

/**
* The parameters that are passed into {@link FilterSearchProps.onDropdownInputChange}.
*
* @public
*/
export interface OnDropdownInputChangeProps {
/** The new input element's new value after the change */
value: string,
searchFields: Omit<SearchParameterField, 'fetchEntities'>[],
/**
* A function that executes a filter search and updates the input and dropdown options
* with the response.
*/
executeFilterSearch: (query?: string) => Promise<FilterSearchResponse | undefined>
}

/**
* The props for the {@link FilterSearch} component.
*
Expand All @@ -78,8 +94,11 @@ export interface FilterSearchProps {
searchOnSelect?: boolean,
/** A function which is called when a filter is selected. */
onSelect?: (params: OnSelectParams) => void,
/** A function which is called when the input's value changes. This does not replace executeFilterSearch, but gets called just before it */
onDropdownInputChange?: (value: string) => void,
/** A function which is called everytime the input element's value changes.
* Helpful for removing static filters from the static filter state if a user clears the search input completely.
* Replaces the default behavior of calling executeFilterSearch whenever the input changes.
*/
onDropdownInputChange?: (params: OnDropdownInputChangeProps) => void,
/** Determines whether or not the results of the filter search are separated by field. Defaults to false. */
sectioned?: boolean,
/** CSS classes for customizing the component styling. */
Expand Down Expand Up @@ -227,6 +246,21 @@ export function FilterSearch({
matchingFieldIds
]);

const handleInputChange = useCallback((value) => {
if (onDropdownInputChange) {
onDropdownInputChange({
value,
searchFields,
executeFilterSearch
});
} else {
executeFilterSearch(value);
}
}, [
onDropdownInputChange,
executeFilterSearch
]);

const meetsSubmitCritera = useCallback(index => index >= 0, []);

const itemDataMatrix = useMemo(() => {
Expand Down Expand Up @@ -270,11 +304,6 @@ export function FilterSearch({
}
}, [executeFilterSearch]);

const handleInputChange = onDropdownInputChange ? (value: string) => {
onDropdownInputChange(value);
executeFilterSearch(value);
} : executeFilterSearch;

return (
<div className={cssClasses.filterSearchContainer}>
{label && <h1 className={cssClasses.label}>{label}</h1>}
Expand Down
3 changes: 2 additions & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ export {
FilterSearch,
FilterSearchCssClasses,
FilterSearchProps,
OnSelectParams
OnSelectParams,
OnDropdownInputChangeProps
} from './FilterSearch';

export {
Expand Down
50 changes: 36 additions & 14 deletions test-site/src/pages/PeoplePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ import {
NumericalFacets,
AlternativeVerticals,
StandardFacet,
NumericalFacet
NumericalFacet,
OnDropdownInputChangeProps
} from '@yext/search-ui-react';
// import { CustomCard } from '../components/CustomCard';

const hierarchicalFacetFieldIds = ['c_hierarchicalFacet'];
const filterSearchFields = [{ fieldApiName: 'name', entityType: 'ce_person' }];
const employeeFilterSearchFields = [{fieldApiName: 'c_employeeDepartment', entityType: 'ce_person'}];
const employeeFilterConfigs = [
{ value: 'Consulting' },
{ value: 'Technology' }
Expand All @@ -43,6 +45,31 @@ export function PeoplePage() {
searchActions.executeVerticalQuery();
});

/**
* This example function that's being used for onDropdownInputChange allows for clearing the filter in the search state when the input is empty.
* This is especially useful for implementations that have multiple FilterSearch components.
* Ex. a user can search using both inputs initially, but then wants to clear one of the FilterSearch inputs and re-run a search.
*/
const removeAssociatedFilterWhenInputIsEmpty = (params: OnDropdownInputChangeProps) => {
const { value, searchFields, executeFilterSearch } = params;
// If there is still an input value, execute the filter search as normal
if (value !== "") {
executeFilterSearch(value);
}
// When the input is empty, remove the associated filter from the search state while keeping any other filters that are applied.
else {
const fieldIds = searchFields.map((field: {fieldApiName: string, entityType: string}) => field.fieldApiName);
const filtersToKeep: SelectableStaticFilter[] = [];
searchActions.state.filters.static?.forEach((staticFilter) => {
const filter = staticFilter.filter as FieldValueStaticFilter;
if (!fieldIds.includes(filter.fieldId)) {
filtersToKeep.push(staticFilter);
}
});
searchActions.setStaticFilters(filtersToKeep);
}
}

return (
<div>
<SearchBar />
Expand All @@ -51,19 +78,14 @@ export function PeoplePage() {
<FilterSearch
searchFields={filterSearchFields}
searchOnSelect={true}
label='Filters'
onDropdownInputChange={(value: string) => {
if (value !== "") return;
const fieldIds = filterSearchFields.map(field => field.fieldApiName);
const filtersToKeep: SelectableStaticFilter[] = [];
searchActions.state.filters.static?.forEach((staticFilter) => {
const filter = staticFilter.filter as FieldValueStaticFilter;
if (!fieldIds.includes(filter.fieldId)) {
filtersToKeep.push(staticFilter);
}
});
searchActions.setStaticFilters(filtersToKeep);
}}
label='FilterSearch Name Filter'
onDropdownInputChange={removeAssociatedFilterWhenInputIsEmpty}
/>
<FilterSearch
searchFields={employeeFilterSearchFields}
searchOnSelect={true}
label='FilterSearch Department Filter'
onDropdownInputChange={removeAssociatedFilterWhenInputIsEmpty}
/>
<FilterDivider />
<StaticFilters
Expand Down
2 changes: 0 additions & 2 deletions tests/components/FilterSearch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -672,10 +672,8 @@ describe('search without section labels', () => {
it('when an onDropdownInputChange prop is specified, it gets called each time after the input changes', async () => {
const mockedOnDropdownInputChange = jest.fn();
renderFilterSearch({ searchFields: searchFieldsProp, onDropdownInputChange: mockedOnDropdownInputChange});
const mockedFilterSearch = jest.spyOn(SearchHeadless.prototype, 'executeFilterSearch');
userEvent.type(screen.getByRole('textbox'), 'a');
await waitFor(() => expect(mockedOnDropdownInputChange).toHaveBeenCalledTimes(1));
await waitFor(() => expect(mockedFilterSearch).toHaveBeenCalled());
})
});

Expand Down

0 comments on commit 5545f1f

Please sign in to comment.