diff --git a/.changeset/warm-dancers-battle.md b/.changeset/warm-dancers-battle.md new file mode 100644 index 0000000000..8aa3f765e8 --- /dev/null +++ b/.changeset/warm-dancers-battle.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Fixed catalog filter "all" not appearing as selected when set as the initially selected filter. diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx index 8ef1707bb1..f7090f330b 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx @@ -498,6 +498,29 @@ describe('', () => { }); }); + it('applies frontend-only filters without refetching', async () => { + const { result } = renderHook(() => useEntityList(), { + wrapper: createWrapper({ pagination }), + }); + + await waitFor(() => { + expect(result.current.backendEntities.length).toBe(2); + expect(result.current.filters.kind?.value).toBe('component'); + }); + + act(() => + result.current.updateFilters({ + user: EntityUserFilter.all(), + }), + ); + + await waitFor(() => { + expect(result.current.filters.user?.value).toBe('all'); + expect(result.current.entities.length).toBe(2); + }); + expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(1); + }); + it('resolves query param filter values', async () => { const query = qs.stringify({ filters: { kind: 'component', type: 'service' }, @@ -801,6 +824,29 @@ describe(``, () => { }); }); + it('applies frontend-only filters without refetching', async () => { + const { result } = renderHook(() => useEntityList(), { + wrapper: createWrapper({ pagination }), + }); + + await waitFor(() => { + expect(result.current.backendEntities.length).toBe(2); + expect(result.current.filters.kind?.value).toBe('component'); + }); + + act(() => + result.current.updateFilters({ + user: EntityUserFilter.all(), + }), + ); + + await waitFor(() => { + expect(result.current.filters.user?.value).toBe('all'); + expect(result.current.entities.length).toBe(2); + }); + expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(1); + }); + it('resolves query param filter values', async () => { const query = qs.stringify({ filters: { kind: 'component', type: 'service' }, diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx index 319740e70e..8c4927ad0a 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx @@ -249,11 +249,11 @@ export const EntityListProvider = ( ? { ...requestedFilters, owners: undefined } : requestedFilters; const compacted = compact(Object.values(adjustedFilters)); + const entityFilter = reduceEntityFilters(compacted); if (paginationMode !== 'none') { if (cursor) { if (cursor !== outputState.appliedCursor) { - const entityFilter = reduceEntityFilters(compacted); const response = await catalogApi.queryEntities({ cursor, limit, @@ -267,58 +267,42 @@ export const EntityListProvider = ( totalItems: response.totalItems, }; } - } else { - const entityFilter = reduceEntityFilters(compacted); - const backendFilter = reduceCatalogFilters(compacted); - const previousBackendFilter = reduceCatalogFilters( - compact(Object.values(outputState.appliedFilters)), - ); - - if ( - (paginationMode === 'offset' && - (outputState.limit !== limit || outputState.offset !== offset)) || - !isEqual(previousBackendFilter, backendFilter) - ) { - const response = await catalogApi.queryEntities({ - ...backendFilter, - limit, - offset, - }); - return { - appliedFilters: requestedFilters, - backendEntities: response.items, - entities: response.items.filter(entityFilter), - pageInfo: response.pageInfo, - totalItems: response.totalItems, - limit, - offset, - }; - } + const entities = outputState.backendEntities.filter(entityFilter); + return { + appliedFilters: requestedFilters, + appliedCursor: outputState.appliedCursor, + backendEntities: outputState.backendEntities, + entities, + pageInfo: outputState.pageInfo, + totalItems: outputState.totalItems, + limit: outputState.limit, + offset: outputState.offset, + }; } - } else { - const entityFilter = reduceEntityFilters(compacted); - const backendFilter = reduceBackendCatalogFilters(compacted); - const { orderFields } = reduceCatalogFilters(compacted); - const previousBackendFilter = reduceBackendCatalogFilters( + + const backendFilter = reduceCatalogFilters(compacted); + const previousBackendFilter = reduceCatalogFilters( compact(Object.values(outputState.appliedFilters)), ); - // TODO(mtlewis): currently entities will never be requested unless - // there's at least one filter, we should allow an initial request - // to happen with no filters. - if (!isEqual(previousBackendFilter, backendFilter)) { - // TODO(timbonicus): should limit fields here, but would need filter - // fields + table columns - const response = await catalogApi.getEntities({ - filter: backendFilter, - order: orderFields, + if ( + (paginationMode === 'offset' && + (outputState.limit !== limit || outputState.offset !== offset)) || + !isEqual(previousBackendFilter, backendFilter) + ) { + const response = await catalogApi.queryEntities({ + ...backendFilter, + limit, + offset, }); - const entities = response.items.filter(entityFilter); return { appliedFilters: requestedFilters, backendEntities: response.items, - entities, - totalItems: entities.length, + entities: response.items.filter(entityFilter), + pageInfo: response.pageInfo, + totalItems: response.totalItems, + limit, + offset, }; } const entities = outputState.backendEntities.filter(entityFilter); @@ -326,10 +310,44 @@ export const EntityListProvider = ( appliedFilters: requestedFilters, backendEntities: outputState.backendEntities, entities, + pageInfo: outputState.pageInfo, + totalItems: outputState.totalItems, + limit: outputState.limit, + offset: outputState.offset, + }; + } + + const backendFilter = reduceBackendCatalogFilters(compacted); + const { orderFields } = reduceCatalogFilters(compacted); + const previousBackendFilter = reduceBackendCatalogFilters( + compact(Object.values(outputState.appliedFilters)), + ); + + // TODO(mtlewis): currently entities will never be requested unless + // there's at least one filter, we should allow an initial request + // to happen with no filters. + if (!isEqual(previousBackendFilter, backendFilter)) { + // TODO(timbonicus): should limit fields here, but would need filter + // fields + table columns + const response = await catalogApi.getEntities({ + filter: backendFilter, + order: orderFields, + }); + const entities = response.items.filter(entityFilter); + return { + appliedFilters: requestedFilters, + backendEntities: response.items, + entities, totalItems: entities.length, }; } - return undefined; + const entities = outputState.backendEntities.filter(entityFilter); + return { + appliedFilters: requestedFilters, + backendEntities: outputState.backendEntities, + entities, + totalItems: entities.length, + }; }, [ catalogApi,