Merge pull request #22570 from pativa/autocomplete-picker-filters
Autocomplete picker: add argument for which filters should be applied
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Adds an argument for which filters should be applied when fetching/counting available values
|
||||
@@ -204,6 +204,7 @@ export type EntityAutocompletePickerProps<
|
||||
};
|
||||
InputProps?: TextFieldProps;
|
||||
initialSelectedOptions?: string[];
|
||||
filtersForAvailableValues?: Array<keyof T>;
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
+78
-3
@@ -24,6 +24,7 @@ import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { DefaultEntityFilters } from '../../hooks';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityFilter } from '../../types';
|
||||
import { EntityKindFilter, EntityTypeFilter } from '../../filters';
|
||||
|
||||
interface EntityFilters extends DefaultEntityFilters {
|
||||
options?: EntityOptionFilter;
|
||||
@@ -46,13 +47,17 @@ class EntityOptionFilter implements EntityFilter {
|
||||
}
|
||||
|
||||
describe('<EntityAutocompletePicker/>', () => {
|
||||
const mockCatalogApi = {
|
||||
getEntityFacets: async () => ({
|
||||
const mockCatalogApi: Partial<jest.Mocked<CatalogApi>> = {
|
||||
getEntityFacets: jest.fn().mockResolvedValue({
|
||||
facets: {
|
||||
'spec.options': options.map((value, idx) => ({ value, count: idx })),
|
||||
},
|
||||
}),
|
||||
} as unknown as CatalogApi;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders all options', async () => {
|
||||
render(
|
||||
@@ -71,6 +76,12 @@ describe('<EntityAutocompletePicker/>', () => {
|
||||
expect(screen.getByText('Options')).toBeInTheDocument(),
|
||||
);
|
||||
|
||||
// should have called catalog backend without any filters applied
|
||||
expect(mockCatalogApi.getEntityFacets).toHaveBeenCalledWith({
|
||||
facets: ['spec.options'],
|
||||
filter: {},
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('options-picker-expand'));
|
||||
options.forEach(option => {
|
||||
expect(screen.getByText(option)).toBeInTheDocument();
|
||||
@@ -267,4 +278,68 @@ describe('<EntityAutocompletePicker/>', () => {
|
||||
options: new EntityOptionFilter(['option2']),
|
||||
});
|
||||
});
|
||||
|
||||
it('filters available values by kind as default', async () => {
|
||||
render(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApi]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
filters: {
|
||||
kind: new EntityKindFilter('Component'),
|
||||
type: new EntityTypeFilter(['service']),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<EntityAutocompletePicker<EntityFilters>
|
||||
label="Options"
|
||||
path="spec.options"
|
||||
name="options"
|
||||
Filter={EntityOptionFilter}
|
||||
/>
|
||||
</MockEntityListContextProvider>
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.getEntityFacets).toHaveBeenCalledWith({
|
||||
facets: ['spec.options'],
|
||||
filter: {
|
||||
kind: 'Component',
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('can be supplied with filters for available values', async () => {
|
||||
render(
|
||||
<TestApiProvider apis={[[catalogApiRef, mockCatalogApi]]}>
|
||||
<MockEntityListContextProvider
|
||||
value={{
|
||||
filters: {
|
||||
kind: new EntityKindFilter('Component'),
|
||||
type: new EntityTypeFilter(['service']),
|
||||
},
|
||||
}}
|
||||
>
|
||||
<EntityAutocompletePicker<EntityFilters>
|
||||
label="Options"
|
||||
path="spec.options"
|
||||
name="options"
|
||||
Filter={EntityOptionFilter}
|
||||
filtersForAvailableValues={['kind', 'type']}
|
||||
/>
|
||||
</MockEntityListContextProvider>
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockCatalogApi.getEntityFacets).toHaveBeenCalledWith({
|
||||
facets: ['spec.options'],
|
||||
filter: {
|
||||
kind: 'Component',
|
||||
'spec.type': ['service'],
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+10
-2
@@ -28,6 +28,7 @@ import {
|
||||
useEntityList,
|
||||
} from '../../hooks/useEntityListProvider';
|
||||
import { EntityFilter } from '../../types';
|
||||
import { reduceBackendCatalogFilters } from '../../utils';
|
||||
|
||||
/** @public */
|
||||
export type AllowedEntityFilters<T extends DefaultEntityFilters> = {
|
||||
@@ -50,6 +51,7 @@ export type EntityAutocompletePickerProps<
|
||||
Filter: { new (values: string[]): NonNullable<T[Name]> };
|
||||
InputProps?: TextFieldProps;
|
||||
initialSelectedOptions?: string[];
|
||||
filtersForAvailableValues?: Array<keyof T>;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
@@ -76,6 +78,7 @@ export function EntityAutocompletePicker<
|
||||
Filter,
|
||||
InputProps,
|
||||
initialSelectedOptions = [],
|
||||
filtersForAvailableValues = ['kind'],
|
||||
} = props;
|
||||
|
||||
const classes = useStyles();
|
||||
@@ -87,17 +90,22 @@ export function EntityAutocompletePicker<
|
||||
} = useEntityList<T>();
|
||||
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const availableValuesFilters = filtersForAvailableValues.map(
|
||||
f => filters[f] as EntityFilter | undefined,
|
||||
);
|
||||
const { value: availableValues } = useAsync(async () => {
|
||||
const facet = path;
|
||||
const { facets } = await catalogApi.getEntityFacets({
|
||||
facets: [facet],
|
||||
filter: filters.kind?.getCatalogFilters(),
|
||||
filter: reduceBackendCatalogFilters(
|
||||
availableValuesFilters.filter(Boolean) as EntityFilter[],
|
||||
),
|
||||
});
|
||||
|
||||
return Object.fromEntries(
|
||||
facets[facet].map(({ value, count }) => [value, count]),
|
||||
);
|
||||
}, [filters.kind]);
|
||||
}, [...availableValuesFilters]);
|
||||
|
||||
const queryParameters = useMemo(
|
||||
() => [queryParameter].flat().filter(Boolean) as string[],
|
||||
|
||||
Reference in New Issue
Block a user