Review feedback.
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
An example instance of the new `<SearchFilter.Autocomplete />` was added to the composed `SearchPage.tsx`, allowing searches bound to the `techdocs` type to be filtered by entity name.
|
||||
An example instance of a `<SearchFilter.Select />` with asynchronously loaded values was added to the composed `SearchPage.tsx`, allowing searches bound to the `techdocs` type to be filtered by entity name.
|
||||
|
||||
This is an entirely optional change; if you wish to adopt it, you can make the following (or similar) changes to your search page layout:
|
||||
|
||||
@@ -50,12 +50,12 @@ This is an entirely optional change; if you wish to adopt it, you can make the f
|
||||
/>
|
||||
<Paper className={classes.filters}>
|
||||
+ {types.includes('techdocs') && (
|
||||
+ <SearchFilter.Autocomplete
|
||||
+ <SearchFilter.Select
|
||||
+ className={classes.filter}
|
||||
+ label="Entity"
|
||||
+ name="name"
|
||||
+ asyncValues={async partial => {
|
||||
+ // Return a list of entitis which are documented.
|
||||
+ values={async () => {
|
||||
+ // Return a list of entities which are documented.
|
||||
+ const { items } = await catalogApi.getEntities({
|
||||
+ fields: ['metadata.name'],
|
||||
+ filter: {
|
||||
@@ -64,9 +64,9 @@ This is an entirely optional change; if you wish to adopt it, you can make the f
|
||||
+ },
|
||||
+ });
|
||||
+
|
||||
+ return items
|
||||
+ .map(entity => entity.metadata.name)
|
||||
+ .filter(name => name.includes(partial));
|
||||
+ const names = items.map(entity => entity.metadata.name);
|
||||
+ names.sort();
|
||||
+ return names;
|
||||
+ }}
|
||||
+ />
|
||||
+ )}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
|
||||
Introduces a `<SearchFilter.Autocomplete />` variant, which can be used as either a single- or multi-select autocomplete filter.
|
||||
|
||||
This variant, as well as `<SearchFilter.Select />`, now also supports loading allowed values asynchronously with a new `asyncValues` prop, which takes an asynchronous function that resolves to the list of values (an optional `asyncDebounce` prop may also be provided).
|
||||
This variant, as well as `<SearchFilter.Select />`, now also supports loading allowed values asynchronously by passing a function that resolves the list of values to the `values` prop. (An optional `valuesDebounceMs` prop may also be provided to control the debounce time).
|
||||
|
||||
Check the [search plugin storybook](https://backstage.io/storybook/?path=/story/plugins-search-searchfilter) to see how to leverage these new additions.
|
||||
|
||||
@@ -93,12 +93,12 @@ const SearchPage = () => {
|
||||
/>
|
||||
<Paper className={classes.filters}>
|
||||
{types.includes('techdocs') && (
|
||||
<SearchFilter.Autocomplete
|
||||
<SearchFilter.Select
|
||||
className={classes.filter}
|
||||
label="Entity"
|
||||
name="name"
|
||||
asyncValues={async partial => {
|
||||
// Return a list of entitis which are documented.
|
||||
values={async () => {
|
||||
// Return a list of entities which are documented.
|
||||
const { items } = await catalogApi.getEntities({
|
||||
fields: ['metadata.name'],
|
||||
filter: {
|
||||
@@ -107,9 +107,9 @@ const SearchPage = () => {
|
||||
},
|
||||
});
|
||||
|
||||
return items
|
||||
.map(entity => entity.metadata.name)
|
||||
.filter(name => name.includes(partial));
|
||||
const names = items.map(entity => entity.metadata.name);
|
||||
names.sort();
|
||||
return names;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
+5
-5
@@ -74,11 +74,11 @@ const SearchPage = () => {
|
||||
/>
|
||||
<Paper className={classes.filters}>
|
||||
{types.includes('techdocs') && (
|
||||
<SearchFilter.Autocomplete
|
||||
<SearchFilter.Select
|
||||
className={classes.filter}
|
||||
label="Entity"
|
||||
name="name"
|
||||
asyncValues={async partial => {
|
||||
values={async () => {
|
||||
// Return a list of entities which are documented.
|
||||
const { items } = await catalogApi.getEntities({
|
||||
fields: ['metadata.name'],
|
||||
@@ -88,9 +88,9 @@ const SearchPage = () => {
|
||||
},
|
||||
});
|
||||
|
||||
return items
|
||||
.map(entity => entity.metadata.name)
|
||||
.filter(name => name.includes(partial));
|
||||
const names = items.map(entity => entity.metadata.name);
|
||||
names.sort();
|
||||
return names;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -94,8 +94,9 @@ export const searchApiRef: ApiRef<SearchApi>;
|
||||
|
||||
// @public (undocumented)
|
||||
export type SearchAutocompleteFilterProps = SearchFilterComponentProps & {
|
||||
multiple?: boolean;
|
||||
filterSelectedOptions?: boolean;
|
||||
limitTags?: number;
|
||||
multiple?: boolean;
|
||||
};
|
||||
|
||||
// @public
|
||||
@@ -166,10 +167,9 @@ export type SearchFilterComponentProps = {
|
||||
className?: string;
|
||||
name: string;
|
||||
label?: string;
|
||||
values?: string[];
|
||||
asyncValues?: (partial: string) => Promise<string[]>;
|
||||
asyncDebounce?: number;
|
||||
values?: string[] | ((partial: string) => Promise<string[]>);
|
||||
defaultValue?: string[] | string | null;
|
||||
valuesDebounceMs?: number;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "SearchFilterNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { TestApiRegistry } from '@backstage/test-utils';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { screen, render, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
@@ -35,7 +34,6 @@ const SearchContextFilterSpy = ({ name }: { name: string }) => {
|
||||
|
||||
describe('SearchFilter.Autocomplete', () => {
|
||||
const query = jest.fn().mockResolvedValue({});
|
||||
const mockApis = TestApiRegistry.from([searchApiRef, { query }]);
|
||||
const emptySearchContext = {
|
||||
term: '',
|
||||
types: [],
|
||||
@@ -47,11 +45,11 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
|
||||
it('renders as expected', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider>
|
||||
<SearchFilter.Autocomplete name={name} values={values} />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
const autocomplete = screen.getByRole('combobox');
|
||||
@@ -68,14 +66,11 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
|
||||
it('renders as expected with async values', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider>
|
||||
<SearchFilter.Autocomplete
|
||||
name={name}
|
||||
asyncValues={async () => values}
|
||||
/>
|
||||
<SearchFilter.Autocomplete name={name} values={async () => values} />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
const autocomplete = screen.getByRole('combobox');
|
||||
@@ -92,7 +87,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
|
||||
it('does not affect unrelated filter state', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider
|
||||
initialState={{
|
||||
...emptySearchContext,
|
||||
@@ -103,7 +98,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
<SearchContextFilterSpy name={name} />
|
||||
<SearchContextFilterSpy name="unrelated" />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
// The spy should show the initial value.
|
||||
@@ -136,7 +131,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
describe('single', () => {
|
||||
it('renders as expected with defaultValue', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider>
|
||||
<SearchFilter.Autocomplete
|
||||
name={name}
|
||||
@@ -145,7 +140,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
/>
|
||||
<SearchContextFilterSpy name={name} />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
const autocomplete = screen.getByRole('combobox');
|
||||
@@ -161,7 +156,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
|
||||
it('renders as expected with initial context', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider
|
||||
initialState={{
|
||||
...emptySearchContext,
|
||||
@@ -171,7 +166,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
<SearchFilter.Autocomplete name={name} values={values} />
|
||||
<SearchContextFilterSpy name={name} />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
const autocomplete = screen.getByRole('combobox');
|
||||
@@ -187,12 +182,12 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
|
||||
it('sets filter state when selecting a value', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider>
|
||||
<SearchFilter.Autocomplete name={name} values={values} />
|
||||
<SearchContextFilterSpy name={name} />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
// Select the first option in the autocomplete.
|
||||
@@ -225,7 +220,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
describe('multiple', () => {
|
||||
it('renders as expected with defaultValue', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider>
|
||||
<SearchFilter.Autocomplete
|
||||
multiple
|
||||
@@ -235,7 +230,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
/>
|
||||
<SearchContextFilterSpy name={name} />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -249,7 +244,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
|
||||
it('renders as expected with initial context', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider
|
||||
initialState={{
|
||||
...emptySearchContext,
|
||||
@@ -259,7 +254,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
<SearchFilter.Autocomplete multiple name={name} values={values} />
|
||||
<SearchContextFilterSpy name={name} />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -273,7 +268,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
|
||||
it('respects tag limit configuration', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider>
|
||||
<SearchFilter.Autocomplete
|
||||
multiple
|
||||
@@ -282,7 +277,7 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
values={values}
|
||||
/>
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
const autocomplete = screen.getByRole('combobox');
|
||||
@@ -322,12 +317,12 @@ describe('SearchFilter.Autocomplete', () => {
|
||||
|
||||
it('sets filter state when selecting a value', async () => {
|
||||
render(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<TestApiProvider apis={[[searchApiRef, { query }]]}>
|
||||
<SearchContextProvider>
|
||||
<SearchFilter.Autocomplete multiple name={name} values={values} />
|
||||
<SearchContextFilterSpy name={name} />
|
||||
</SearchContextProvider>
|
||||
</ApiProvider>,
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
const autocomplete = screen.getByRole('combobox');
|
||||
|
||||
@@ -29,29 +29,34 @@ import { SearchFilterComponentProps } from './SearchFilter';
|
||||
* @public
|
||||
*/
|
||||
export type SearchAutocompleteFilterProps = SearchFilterComponentProps & {
|
||||
multiple?: boolean;
|
||||
filterSelectedOptions?: boolean;
|
||||
limitTags?: number;
|
||||
multiple?: boolean;
|
||||
};
|
||||
|
||||
export const AutocompleteFilter = (props: SearchAutocompleteFilterProps) => {
|
||||
const {
|
||||
asyncValues,
|
||||
asyncDebounce,
|
||||
className,
|
||||
defaultValue,
|
||||
multiple,
|
||||
name,
|
||||
values: givenValues,
|
||||
valuesDebounceMs,
|
||||
label,
|
||||
filterSelectedOptions,
|
||||
limitTags,
|
||||
multiple,
|
||||
} = props;
|
||||
const [inputValue, setInputValue] = useState<string>('');
|
||||
useDefaultFilterValue(name, defaultValue);
|
||||
const asyncValues =
|
||||
typeof givenValues === 'function' ? givenValues : undefined;
|
||||
const defaultValues =
|
||||
typeof givenValues === 'function' ? undefined : givenValues;
|
||||
const { value: values, loading } = useAsyncFilterValues(
|
||||
asyncValues,
|
||||
inputValue,
|
||||
givenValues,
|
||||
asyncDebounce,
|
||||
defaultValues,
|
||||
valuesDebounceMs,
|
||||
);
|
||||
const { filters, setFilters } = useSearch();
|
||||
const filterValue =
|
||||
@@ -94,12 +99,13 @@ export const AutocompleteFilter = (props: SearchAutocompleteFilterProps) => {
|
||||
|
||||
return (
|
||||
<Autocomplete
|
||||
filterSelectedOptions={filterSelectedOptions}
|
||||
limitTags={limitTags}
|
||||
multiple={multiple}
|
||||
className={className}
|
||||
id={`${multiple ? 'multi-' : ''}select-filter-${name}--select`}
|
||||
options={values || []}
|
||||
loading={loading}
|
||||
limitTags={limitTags}
|
||||
value={filterValue}
|
||||
onChange={handleChange}
|
||||
onInputChange={(_, newValue) => setInputValue(newValue)}
|
||||
|
||||
@@ -50,7 +50,8 @@ export const SelectFilter = () => {
|
||||
return (
|
||||
<Paper style={{ padding: 10 }}>
|
||||
<SearchFilter.Select
|
||||
name="Search Select Filter"
|
||||
label="Search Select Filter"
|
||||
name="select_filter"
|
||||
values={['value1', 'value2']}
|
||||
/>
|
||||
</Paper>
|
||||
@@ -61,8 +62,9 @@ export const AsyncSelectFilter = () => {
|
||||
return (
|
||||
<Paper style={{ padding: 10 }}>
|
||||
<SearchFilter.Select
|
||||
name="Asynchronous Values"
|
||||
asyncValues={async () => {
|
||||
label="Asynchronous Values"
|
||||
name="async_values"
|
||||
values={async () => {
|
||||
const response = await fetch('https://swapi.dev/api/planets');
|
||||
const json: { results: Array<{ name: string }> } =
|
||||
await response.json();
|
||||
@@ -79,7 +81,7 @@ export const Autocomplete = () => {
|
||||
<SearchFilter.Autocomplete
|
||||
name="autocomplete"
|
||||
label="Single-Select Autocomplete Filter"
|
||||
values={['abba', 'cadaver']}
|
||||
values={['value1', 'value2']}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
@@ -92,7 +94,7 @@ export const MultiSelectAutocomplete = () => {
|
||||
multiple
|
||||
name="autocomplete"
|
||||
label="Multi-Select Autocomplete Filter"
|
||||
values={['abba', 'cadaver']}
|
||||
values={['value1', 'value2']}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
@@ -105,10 +107,12 @@ export const AsyncMultiSelectAutocomplete = () => {
|
||||
multiple
|
||||
name="starwarsPerson"
|
||||
label="Starwars Character"
|
||||
asyncValues={async partial => {
|
||||
values={async partial => {
|
||||
if (partial === '') return [];
|
||||
const response = await fetch(
|
||||
`https://swapi.dev/api/people?search=${partial}`,
|
||||
`https://swapi.dev/api/people?search=${encodeURIComponent(
|
||||
partial,
|
||||
)}`,
|
||||
);
|
||||
const json: { results: Array<{ name: string }> } =
|
||||
await response.json();
|
||||
|
||||
@@ -215,7 +215,7 @@ describe('SearchFilter', () => {
|
||||
<SearchFilter.Select
|
||||
label={label}
|
||||
name={name}
|
||||
asyncValues={async () => values}
|
||||
values={async () => values}
|
||||
/>
|
||||
</SearchContextProvider>,
|
||||
);
|
||||
|
||||
@@ -46,18 +46,19 @@ export type SearchFilterComponentProps = {
|
||||
className?: string;
|
||||
name: string;
|
||||
label?: string;
|
||||
values?: string[];
|
||||
/**
|
||||
* An async function to return a list of values to be used in the filter. In
|
||||
* the autocomplete filter, the last input value is provided as an input to
|
||||
* allow values to be filtered. This function is debounced and values cached.
|
||||
* Either an array of values directly, or an async function to return a list
|
||||
* of values to be used in the filter. In the autocomplete filter, the last
|
||||
* input value is provided as an input to allow values to be filtered. This
|
||||
* function is debounced and values cached.
|
||||
*/
|
||||
asyncValues?: (partial: string) => Promise<string[]>;
|
||||
/**
|
||||
* Debounce time (ms) used by the asyncValues callback. Defaults to 250ms.
|
||||
*/
|
||||
asyncDebounce?: number;
|
||||
values?: string[] | ((partial: string) => Promise<string[]>);
|
||||
defaultValue?: string[] | string | null;
|
||||
/**
|
||||
* Debounce time in milliseconds, used when values is an async callback.
|
||||
* Defaults to 250ms.
|
||||
*/
|
||||
valuesDebounceMs?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -69,10 +70,27 @@ export type SearchFilterWrapperProps = SearchFilterComponentProps & {
|
||||
};
|
||||
|
||||
const CheckboxFilter = (props: SearchFilterComponentProps) => {
|
||||
const { className, defaultValue, label, name, values = [] } = props;
|
||||
const {
|
||||
className,
|
||||
defaultValue,
|
||||
label,
|
||||
name,
|
||||
values: givenValues = [],
|
||||
valuesDebounceMs,
|
||||
} = props;
|
||||
const classes = useStyles();
|
||||
const { filters, setFilters } = useSearch();
|
||||
useDefaultFilterValue(name, defaultValue);
|
||||
const asyncValues =
|
||||
typeof givenValues === 'function' ? givenValues : undefined;
|
||||
const defaultValues =
|
||||
typeof givenValues === 'function' ? undefined : givenValues;
|
||||
const { value: values = [], loading } = useAsyncFilterValues(
|
||||
asyncValues,
|
||||
'',
|
||||
defaultValues,
|
||||
valuesDebounceMs,
|
||||
);
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const {
|
||||
@@ -90,6 +108,7 @@ const CheckboxFilter = (props: SearchFilterComponentProps) => {
|
||||
return (
|
||||
<FormControl
|
||||
className={className}
|
||||
disabled={loading}
|
||||
fullWidth
|
||||
data-testid="search-checkboxfilter-next"
|
||||
>
|
||||
@@ -117,21 +136,24 @@ const CheckboxFilter = (props: SearchFilterComponentProps) => {
|
||||
|
||||
const SelectFilter = (props: SearchFilterComponentProps) => {
|
||||
const {
|
||||
asyncValues,
|
||||
asyncDebounce,
|
||||
className,
|
||||
defaultValue,
|
||||
label,
|
||||
name,
|
||||
values: givenValues,
|
||||
valuesDebounceMs,
|
||||
} = props;
|
||||
const classes = useStyles();
|
||||
useDefaultFilterValue(name, defaultValue);
|
||||
const asyncValues =
|
||||
typeof givenValues === 'function' ? givenValues : undefined;
|
||||
const defaultValues =
|
||||
typeof givenValues === 'function' ? undefined : givenValues;
|
||||
const { value: values = [], loading } = useAsyncFilterValues(
|
||||
asyncValues,
|
||||
'',
|
||||
givenValues,
|
||||
asyncDebounce,
|
||||
defaultValues,
|
||||
valuesDebounceMs,
|
||||
);
|
||||
const { filters, setFilters } = useSearch();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export const useAsyncFilterValues = (
|
||||
defaultValues: string[] = [],
|
||||
debounce: number = 250,
|
||||
) => {
|
||||
const valuesMemo = useRef<Record<string, string[]>>({});
|
||||
const valuesMemo = useRef<Record<string, string[] | Promise<string[]>>>({});
|
||||
const definiteFn = fn || (() => Promise.resolve([]));
|
||||
|
||||
const [state, callback] = useAsyncFn(definiteFn, [inputValue], {
|
||||
@@ -42,8 +42,10 @@ export const useAsyncFilterValues = (
|
||||
// Performance optimization: only invoke the callback once per inputValue
|
||||
// for the lifetime of the hook/component.
|
||||
if (valuesMemo.current[inputValue] === undefined) {
|
||||
callback(inputValue).then(values => {
|
||||
valuesMemo.current[inputValue] = callback(inputValue).then(values => {
|
||||
// Overrite the value for future immediate returns.
|
||||
valuesMemo.current[inputValue] = values;
|
||||
return values;
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -59,11 +61,12 @@ export const useAsyncFilterValues = (
|
||||
};
|
||||
}
|
||||
|
||||
// Immediately return a memoized value if it is set.
|
||||
if (valuesMemo.current[inputValue] !== undefined) {
|
||||
// Immediately return a memoized value if it is set (and not a promise).
|
||||
const possibleValue = valuesMemo.current[inputValue];
|
||||
if (Array.isArray(possibleValue)) {
|
||||
return {
|
||||
loading: false,
|
||||
value: valuesMemo.current[inputValue],
|
||||
value: possibleValue,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -80,11 +83,7 @@ export const useDefaultFilterValue = (
|
||||
const { setFilters } = useSearch();
|
||||
|
||||
useEffect(() => {
|
||||
const defaultIsEmpty = !defaultValue;
|
||||
const defaultIsEmptyArray =
|
||||
Array.isArray(defaultValue) && defaultValue.length === 0;
|
||||
|
||||
if (!defaultIsEmpty && !defaultIsEmptyArray) {
|
||||
if (defaultValue && [defaultValue].flat().length > 0) {
|
||||
setFilters(prevFilters => ({
|
||||
...prevFilters,
|
||||
[name]: defaultValue,
|
||||
|
||||
Reference in New Issue
Block a user