From 9795a132aab7624a5d92e1c0231f46c43ff5abb1 Mon Sep 17 00:00:00 2001 From: cyril-ui-developer Date: Tue, 16 Jun 2026 11:20:45 -0400 Subject: [PATCH] OCPBUGS-88739: Filter projects by display name in ConsoleDataView Restore display-name matching in useConsoleDataViewFilters to match legacy project-name filter behavior after Projects list migration to ConsoleDataView. Co-authored-by: Cursor --- .../useConsoleDataViewFilters.spec.tsx | 93 +++++++++++++++++++ .../data-view/useConsoleDataViewFilters.ts | 13 ++- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/frontend/packages/console-app/src/components/data-view/__tests__/useConsoleDataViewFilters.spec.tsx b/frontend/packages/console-app/src/components/data-view/__tests__/useConsoleDataViewFilters.spec.tsx index 7c62c94daa5..cff3b8b2272 100644 --- a/frontend/packages/console-app/src/components/data-view/__tests__/useConsoleDataViewFilters.spec.tsx +++ b/frontend/packages/console-app/src/components/data-view/__tests__/useConsoleDataViewFilters.spec.tsx @@ -40,6 +40,30 @@ const mockData: K8sResourceCommon[] = [ const initialFilters: ResourceFilters = { name: '', label: '' }; +const projectMockData: K8sResourceCommon[] = [ + { + metadata: { + name: 'test-proj', + annotations: { 'openshift.io/display-name': 'My Test Project' }, + }, + kind: 'Project', + apiVersion: 'v1', + }, + { + metadata: { + name: 'other-proj', + annotations: { 'openshift.io/display-name': 'Other Project' }, + }, + kind: 'Project', + apiVersion: 'v1', + }, + { + metadata: { name: 'no-display-name' }, + kind: 'Project', + apiVersion: 'v1', + }, +]; + const createWrapper = (initialEntries: string[] = ['/']): FC<{ children: ReactNode }> => { const Wrapper: FC<{ children: ReactNode }> = ({ children }) => ( {children} @@ -100,6 +124,75 @@ describe('useConsoleDataViewFilters', () => { expect(result.current.filteredData[0].metadata.name).toBe('web-frontend'); }); + it('should filter by openshift.io/display-name using fuzzy matching', () => { + const { result } = renderHook( + () => useConsoleDataViewFilters({ data: projectMockData, initialFilters }), + { wrapper: createWrapper(['/?name=My%20Test']) }, + ); + + expect(result.current.filteredData).toHaveLength(1); + expect(result.current.filteredData[0].metadata.name).toBe('test-proj'); + }); + + it('should filter by metadata.name when display name differs', () => { + const { result } = renderHook( + () => useConsoleDataViewFilters({ data: projectMockData, initialFilters }), + { wrapper: createWrapper(['/?name=test-proj']) }, + ); + + expect(result.current.filteredData).toHaveLength(1); + expect(result.current.filteredData[0].metadata.name).toBe('test-proj'); + }); + + it('should match project by display name case-insensitively in fuzzy mode', () => { + // Default fuzzy mode - no mock override + const { result } = renderHook( + () => useConsoleDataViewFilters({ data: projectMockData, initialFilters }), + { wrapper: createWrapper(['/?name=other%20project']) }, // lowercase search + ); + + // Matches "Other Project" even though we searched "other project" + expect(result.current.filteredData).toHaveLength(1); + expect(result.current.filteredData[0].metadata.name).toBe('other-proj'); + }); + + it('should not match resources without display-name annotation when searching by display name', () => { + const { result } = renderHook( + () => useConsoleDataViewFilters({ data: projectMockData, initialFilters }), + { wrapper: createWrapper(['/?name=Project']) }, + ); + + // "Project" appears in display names of test-proj and other-proj, but not in "no-display-name" + expect(result.current.filteredData).toHaveLength(2); + expect(result.current.filteredData.map((d) => d.metadata.name)).toEqual([ + 'test-proj', + 'other-proj', + ]); + }); + + it('should filter by openshift.io/display-name using exact matching when exact search is enabled', () => { + useExactSearch.mockReturnValue([true, true]); + + const { result } = renderHook( + () => useConsoleDataViewFilters({ data: projectMockData, initialFilters }), + { wrapper: createWrapper(['/?name=My%20Test%20Project']) }, + ); + + expect(result.current.filteredData).toHaveLength(1); + expect(result.current.filteredData[0].metadata.name).toBe('test-proj'); + }); + + it('should require case-sensitive match for display-name in exact search mode', () => { + useExactSearch.mockReturnValue([true, true]); + + const { result } = renderHook( + () => useConsoleDataViewFilters({ data: projectMockData, initialFilters }), + { wrapper: createWrapper(['/?name=my%20test']) }, + ); + + expect(result.current.filteredData).toHaveLength(0); + }); + it('should filter by name using exact matching when exact search is enabled', () => { useExactSearch.mockReturnValue([true, true]); diff --git a/frontend/packages/console-app/src/components/data-view/useConsoleDataViewFilters.ts b/frontend/packages/console-app/src/components/data-view/useConsoleDataViewFilters.ts index 19f2c6e5ae7..5b1e65ac09d 100644 --- a/frontend/packages/console-app/src/components/data-view/useConsoleDataViewFilters.ts +++ b/frontend/packages/console-app/src/components/data-view/useConsoleDataViewFilters.ts @@ -15,6 +15,9 @@ const getK8sResourceMetadata = (obj: K8sResourceCommon): ResourceMetadata => ({ labels: obj.metadata?.labels, }); +const getOpenShiftDisplayName = (resource: K8sResourceCommon): string | undefined => + resource.metadata?.annotations?.['openshift.io/display-name']; + export const useConsoleDataViewFilters = < TData, TFilters extends ResourceFilters = ResourceFilters @@ -75,12 +78,14 @@ export const useConsoleDataViewFilters = < () => data?.filter((resource) => { const { name: resourceName, labels } = getObjectMetadata(resource); + const displayName = getOpenShiftDisplayName(resource as K8sResourceCommon); - // Filter by K8s resource name + // Filter by K8s resource name or display name + const matchFn = isExactSearch ? exactMatch : fuzzyCaseInsensitive; const matchesName = - !filters.name || isExactSearch - ? exactMatch(filters.name, resourceName) - : fuzzyCaseInsensitive(filters.name, resourceName); + !filters.name || + matchFn(filters.name, resourceName) || + matchFn(filters.name, displayName); const resourceLabels = mapLabelsToStrings(labels); const filterLabelsArray = filters.label?.split(',') ?? [];