diff --git a/.changeset/ninety-experts-cheat.md b/.changeset/ninety-experts-cheat.md
new file mode 100644
index 0000000000..093145ecc6
--- /dev/null
+++ b/.changeset/ninety-experts-cheat.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog-react': patch
+---
+
+update EntityAutocompletePicker selected options when filter value is changed externally
diff --git a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.test.tsx b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.test.tsx
index f025c14011..5fd8b549ce 100644
--- a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.test.tsx
+++ b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.test.tsx
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-import { fireEvent, render, waitFor, screen } from '@testing-library/react';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import {
- MockEntityListContextProvider,
catalogApiMock,
+ MockEntityListContextProvider,
} from '@backstage/plugin-catalog-react/testUtils';
import { EntityAutocompletePicker } from './EntityAutocompletePicker';
import { TestApiProvider } from '@backstage/test-utils';
import { catalogApiRef } from '../../api';
-import { DefaultEntityFilters } from '../../hooks';
+import { DefaultEntityFilters, useEntityList } from '../../hooks';
import { Entity } from '@backstage/catalog-model';
import { EntityFilter } from '../../types';
import { EntityKindFilter, EntityTypeFilter } from '../../filters';
@@ -281,15 +281,28 @@ describe('', () => {
});
});
- it('responds to external queryParameters changes', async () => {
+ it('responds to external filter changes', async () => {
const mockCatalogApi = makeMockCatalogApi();
- const updateFilters = jest.fn();
- const rendered = render(
+ const ChangeFilterButton = () => {
+ const { updateFilters } = useEntityList();
+
+ return (
+
+ );
+ };
+
+ render(
value={{
- updateFilters,
- queryParameters: { options: ['option1'] },
+ filters: { options: new EntityOptionFilter(['option2']) },
}}
>
@@ -298,34 +311,21 @@ describe('', () => {
name="options"
Filter={EntityOptionFilter}
/>
+
,
);
+
await waitFor(() =>
- expect(updateFilters).toHaveBeenLastCalledWith({
- options: new EntityOptionFilter(['option1']),
- }),
+ expect(screen.queryByText('Options')).toBeInTheDocument(),
);
- rendered.rerender(
-
-
- value={{
- updateFilters,
- queryParameters: { options: ['option2'] },
- }}
- >
-
- label="Options"
- path="spec.options"
- name="options"
- Filter={EntityOptionFilter}
- />
-
- ,
+ expect(screen.queryByText('option2')).toBeInTheDocument();
+
+ screen.getByTestId('external-filter-change-button').click();
+ await waitFor(() =>
+ expect(screen.queryByText('option3')).toBeInTheDocument(),
);
- expect(updateFilters).toHaveBeenLastCalledWith({
- options: new EntityOptionFilter(['option2']),
- });
+ expect(screen.queryByText('option2')).not.toBeInTheDocument();
});
it('filters available values by kind as default', async () => {
diff --git a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx
index 09b5c1e908..c134446d27 100644
--- a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx
+++ b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx
@@ -29,6 +29,7 @@ import {
import { EntityFilter } from '../../types';
import { reduceBackendCatalogFilters } from '../../utils/filters';
import { CatalogAutocomplete } from '../CatalogAutocomplete';
+import { isEqual } from 'lodash';
/** @public */
export type AllowedEntityFilters = {
@@ -114,11 +115,13 @@ export function EntityAutocompletePicker<
[queryParameter],
);
+ const filteredOptions = (filters[name] as unknown as { values: string[] })
+ ?.values;
+
const [selectedOptions, setSelectedOptions] = useState(
queryParameters.length
? queryParameters
- : (filters[name] as unknown as { values: string[] })?.values ??
- initialSelectedOptions,
+ : filteredOptions ?? initialSelectedOptions,
);
// Set selected options on query parameter updates; this happens at initial page load and from
@@ -132,12 +135,26 @@ export function EntityAutocompletePicker<
const availableOptions = Object.keys(availableValues ?? {});
const shouldAddFilter = selectedOptions.length && availableOptions.length;
+ // Update filter value when selectedOptions change
useEffect(() => {
updateFilters({
[name]: shouldAddFilter ? new Filter(selectedOptions) : undefined,
} as Partial);
}, [name, shouldAddFilter, selectedOptions, Filter, updateFilters]);
+ // Update selected options when filter value changes
+ useEffect(() => {
+ if (!shouldAddFilter) return;
+
+ const newSelectedOptions = filteredOptions ?? [];
+
+ // Check value is actually different (not just a different reference) to prevent selectedOptions <> filters loop
+ if (!isEqual(newSelectedOptions, selectedOptions)) {
+ setSelectedOptions(newSelectedOptions);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- Don't re-set filter value when selectedOptions changes
+ }, [filteredOptions]);
+
const filter = filters[name];
if (
(filter && typeof filter === 'object' && !('values' in filter)) ||