From cf3a2601be5d43c7d70a37fcf73c16c91799d1cc Mon Sep 17 00:00:00 2001 From: Saif Chaudhry Date: Thu, 12 Mar 2026 10:09:21 -0700 Subject: [PATCH 1/5] ROX-33406: filter search entities by active workflow view The compound search filter on the Violations page showed all 7 entity categories regardless of which workflow view was active, causing users to see irrelevant filter options (e.g. Node filters in Applications view). The search filter config is now driven by the active filteredWorkflowView so only contextually relevant entities appear. Additionally, switching workflow views now clears all active search filters and resets pagination to page 1, preventing orphaned filters from invisible entities from affecting query results. Signed-off-by: Saif Chaudhry --- .../Violations/ViolationsTablePage.tsx | 6 ++ .../Violations/ViolationsTablePanel.tsx | 4 ++ .../ViolationsTableSearchFilter.test.ts | 61 +++++++++++++++++++ .../ViolationsTableSearchFilter.tsx | 29 ++++++++- 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTablePage.tsx b/ui/apps/platform/src/Containers/Violations/ViolationsTablePage.tsx index 49a3f873d16a5..9b3c86a394f66 100644 --- a/ui/apps/platform/src/Containers/Violations/ViolationsTablePage.tsx +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTablePage.tsx @@ -167,6 +167,11 @@ function ViolationsTablePage(): ReactElement { setSearchFilter(updateSearchFilter(searchFilter, payload)); }; + useEffectAfterFirstRender(() => { + setSearchFilter({}); + setPage(1); + }, [filteredWorkflowView, setSearchFilter, setPage]); + useEffectAfterFirstRender(() => { if (hasExecutableFilter && !isViewFiltered) { // If the user applies a filter to a previously unfiltered table, return to page 1 @@ -350,6 +355,7 @@ function ViolationsTablePage(): ReactElement { onFilterChange={setSearchFilter} onSearch={onSearch} additionalContextFilter={additionalContextFilter} + filteredWorkflowView={filteredWorkflowView} hasActiveViolations={selectedViolationStateTab === 'ACTIVE'} isTableDataUpdating={isTableDataUpdating} /> diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTablePanel.tsx b/ui/apps/platform/src/Containers/Violations/ViolationsTablePanel.tsx index b09c137bead55..855b240642637 100644 --- a/ui/apps/platform/src/Containers/Violations/ViolationsTablePanel.tsx +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTablePanel.tsx @@ -36,6 +36,7 @@ import type { ListAlert } from 'types/alert.proto'; import { getAxiosErrorMessage } from 'utils/responseErrorUtils'; import type { SearchFilter } from 'types/search'; import type { OnSearchCallback } from 'Components/CompoundSearchFilter/types'; +import type { FilteredWorkflowView } from 'Components/FilteredWorkflowViewSelector/types'; import ResolveConfirmation from './Modals/ResolveConfirmation'; import ExcludeConfirmation from './Modals/ExcludeConfirmation'; import ViolationsTableSearchFilter from './ViolationsTableSearchFilter'; @@ -81,6 +82,7 @@ type ViolationsTablePanelProps = { onFilterChange: (newFilter: SearchFilter) => void; onSearch: OnSearchCallback; additionalContextFilter: SearchFilter; + filteredWorkflowView: FilteredWorkflowView; hasActiveViolations: boolean; isTableDataUpdating: boolean; }; @@ -100,6 +102,7 @@ function ViolationsTablePanel({ onFilterChange, onSearch, additionalContextFilter, + filteredWorkflowView, hasActiveViolations, isTableDataUpdating, }: ViolationsTablePanelProps): ReactElement { @@ -240,6 +243,7 @@ function ViolationsTablePanel({ onFilterChange={onFilterChange} onSearch={onSearch} additionalContextFilter={additionalContextFilter} + filteredWorkflowView={filteredWorkflowView} /> diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts new file mode 100644 index 0000000000000..b1a5975926140 --- /dev/null +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts @@ -0,0 +1,61 @@ +import type { FilteredWorkflowView } from 'Components/FilteredWorkflowViewSelector/types'; +import { getSearchFilterConfig } from './ViolationsTableSearchFilter'; + +function getEntityNames(view: FilteredWorkflowView): string[] { + return getSearchFilterConfig(view).map((entity) => entity.displayName); +} + +describe('getSearchFilterConfig', () => { + it('should return only application-relevant entities for "Applications view"', () => { + expect(getEntityNames('Applications view')).toEqual([ + 'Cluster', + 'Deployment', + 'Namespace', + 'Policy', + 'Policy violation', + ]); + }); + + it('should return only platform-relevant entities for "Platform view"', () => { + expect(getEntityNames('Platform view')).toEqual([ + 'Cluster', + 'Deployment', + 'Namespace', + 'Policy', + 'Policy violation', + ]); + }); + + it('should return only node-relevant entities for "Node view"', () => { + expect(getEntityNames('Node view')).toEqual([ + 'Cluster', + 'Policy', + 'Policy violation', + 'Node', + ]); + }); + + it('should return all entities for "Full view"', () => { + expect(getEntityNames('Full view')).toEqual([ + 'Cluster', + 'Deployment', + 'Namespace', + 'Policy', + 'Policy violation', + 'Node', + 'Resource', + ]); + }); + + it('should always include "Policy" as a valid entity for all views', () => { + const views: FilteredWorkflowView[] = [ + 'Applications view', + 'Platform view', + 'Node view', + 'Full view', + ]; + views.forEach((view) => { + expect(getEntityNames(view)).toContain('Policy'); + }); + }); +}); diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx index 82cb465a51466..8625f94a19291 100644 --- a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx @@ -43,8 +43,9 @@ import { Label as NodeLabel, Name as NodeName, } from 'Components/CompoundSearchFilter/attributes/node'; +import type { FilteredWorkflowView } from 'Components/FilteredWorkflowViewSelector/types'; -const searchFilterConfig: CompoundSearchFilterConfig = [ +const allSearchFilterEntities: CompoundSearchFilterConfig = [ { displayName: 'Cluster', searchCategory: 'ALERTS', @@ -88,11 +89,34 @@ const searchFilterConfig: CompoundSearchFilterConfig = [ }, ]; +const allowedEntitiesByView: Record = { + 'Applications view': ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'], + 'Platform view': ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'], + 'Node view': ['Cluster', 'Policy', 'Policy violation', 'Node'], + 'Full view': [ + 'Cluster', + 'Deployment', + 'Namespace', + 'Policy', + 'Policy violation', + 'Node', + 'Resource', + ], +}; + +export function getSearchFilterConfig( + filteredWorkflowView: FilteredWorkflowView +): CompoundSearchFilterConfig { + const allowed = allowedEntitiesByView[filteredWorkflowView]; + return allSearchFilterEntities.filter((entity) => allowed.includes(entity.displayName)); +} + export type ViolationsTableSearchFilterProps = { searchFilter: SearchFilter; onFilterChange: (newFilter: SearchFilter) => void; onSearch: OnSearchCallback; additionalContextFilter: SearchFilter; + filteredWorkflowView: FilteredWorkflowView; }; function ViolationsTableSearchFilter({ @@ -100,10 +124,13 @@ function ViolationsTableSearchFilter({ onFilterChange, onSearch, additionalContextFilter, + filteredWorkflowView, }: ViolationsTableSearchFilterProps) { const { analyticsTrack } = useAnalytics(); const trackAppliedFilter = createFilterTracker(analyticsTrack); + const searchFilterConfig = getSearchFilterConfig(filteredWorkflowView); + const onSearchHandler: OnSearchCallback = (payload) => { onSearch(payload); trackAppliedFilter('Policy Violations Filter Applied', payload); From 306f74ca78bba774144aa5c45de8f1e8bec8c948 Mon Sep 17 00:00:00 2001 From: Saif Chaudhry Date: Thu, 12 Mar 2026 10:40:28 -0700 Subject: [PATCH 2/5] fix(violations): use empty array for Full view to avoid duplicating entity list Full view should show all entities, so use an empty array as a signal to return allSearchFilterEntities directly. This avoids maintaining a duplicate list that must be updated whenever new entities are added. Signed-off-by: Saif Chaudhry --- .../Violations/ViolationsTableSearchFilter.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx index 8625f94a19291..52025b4334be2 100644 --- a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx @@ -93,21 +93,16 @@ const allowedEntitiesByView: Record = { 'Applications view': ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'], 'Platform view': ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'], 'Node view': ['Cluster', 'Policy', 'Policy violation', 'Node'], - 'Full view': [ - 'Cluster', - 'Deployment', - 'Namespace', - 'Policy', - 'Policy violation', - 'Node', - 'Resource', - ], + 'Full view': [], }; export function getSearchFilterConfig( filteredWorkflowView: FilteredWorkflowView ): CompoundSearchFilterConfig { const allowed = allowedEntitiesByView[filteredWorkflowView]; + if (allowed.length === 0) { + return allSearchFilterEntities; + } return allSearchFilterEntities.filter((entity) => allowed.includes(entity.displayName)); } From 4b02d2ea2752095ebd5803f4b4dcd4e49e696c7e Mon Sep 17 00:00:00 2001 From: Saif Chaudhry Date: Thu, 12 Mar 2026 10:48:44 -0700 Subject: [PATCH 3/5] test(violations): assert irrelevant entities are excluded per view Rewrite tests to verify the actual filtering behavior rather than mirroring the config values. Each view test asserts that entities irrelevant to that workflow context are excluded. Signed-off-by: Saif Chaudhry --- .../ViolationsTableSearchFilter.test.ts | 72 ++++++------------- 1 file changed, 23 insertions(+), 49 deletions(-) diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts index b1a5975926140..479d1f077b207 100644 --- a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts @@ -1,61 +1,35 @@ -import type { FilteredWorkflowView } from 'Components/FilteredWorkflowViewSelector/types'; import { getSearchFilterConfig } from './ViolationsTableSearchFilter'; -function getEntityNames(view: FilteredWorkflowView): string[] { - return getSearchFilterConfig(view).map((entity) => entity.displayName); -} - describe('getSearchFilterConfig', () => { - it('should return only application-relevant entities for "Applications view"', () => { - expect(getEntityNames('Applications view')).toEqual([ - 'Cluster', - 'Deployment', - 'Namespace', - 'Policy', - 'Policy violation', - ]); - }); + it('should exclude Node and Resource entities from "Applications view"', () => { + const config = getSearchFilterConfig('Applications view'); + const names = config.map((e) => e.displayName); - it('should return only platform-relevant entities for "Platform view"', () => { - expect(getEntityNames('Platform view')).toEqual([ - 'Cluster', - 'Deployment', - 'Namespace', - 'Policy', - 'Policy violation', - ]); + expect(names).not.toContain('Node'); + expect(names).not.toContain('Resource'); }); - it('should return only node-relevant entities for "Node view"', () => { - expect(getEntityNames('Node view')).toEqual([ - 'Cluster', - 'Policy', - 'Policy violation', - 'Node', - ]); + it('should exclude Node and Resource entities from "Platform view"', () => { + const config = getSearchFilterConfig('Platform view'); + const names = config.map((e) => e.displayName); + + expect(names).not.toContain('Node'); + expect(names).not.toContain('Resource'); }); - it('should return all entities for "Full view"', () => { - expect(getEntityNames('Full view')).toEqual([ - 'Cluster', - 'Deployment', - 'Namespace', - 'Policy', - 'Policy violation', - 'Node', - 'Resource', - ]); + it('should exclude Deployment, Namespace, and Resource entities from "Node view"', () => { + const config = getSearchFilterConfig('Node view'); + const names = config.map((e) => e.displayName); + + expect(names).not.toContain('Deployment'); + expect(names).not.toContain('Namespace'); + expect(names).not.toContain('Resource'); + expect(names).toContain('Node'); }); - it('should always include "Policy" as a valid entity for all views', () => { - const views: FilteredWorkflowView[] = [ - 'Applications view', - 'Platform view', - 'Node view', - 'Full view', - ]; - views.forEach((view) => { - expect(getEntityNames(view)).toContain('Policy'); - }); + it('should include all entities in "Full view"', () => { + const config = getSearchFilterConfig('Full view'); + + expect(config).toHaveLength(7); }); }); From dc03c297543864336e99162f5422f0d4f6681dcb Mon Sep 17 00:00:00 2001 From: Saif Chaudhry Date: Thu, 12 Mar 2026 11:01:35 -0700 Subject: [PATCH 4/5] fix(violations): alphabetize search filter entity dropdown order Reorder allSearchFilterEntities so the entity selector dropdown displays entries alphabetically: Cluster, Deployment, Namespace, Node, Policy, Policy violation, Resource. Signed-off-by: Saif Chaudhry --- .../Violations/ViolationsTableSearchFilter.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx index 52025b4334be2..0c8cf76ed94aa 100644 --- a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx @@ -67,6 +67,11 @@ const allSearchFilterEntities: CompoundSearchFilterConfig = [ searchCategory: 'ALERTS', attributes: [NamespaceAnnotation, NamespaceID, NamespaceLabel, NamespaceName], }, + { + displayName: 'Node', + searchCategory: 'ALERTS', + attributes: [NodeAnnotation, NodeLabel, NodeName], + }, { displayName: 'Policy', searchCategory: 'ALERTS', @@ -77,11 +82,6 @@ const allSearchFilterEntities: CompoundSearchFilterConfig = [ searchCategory: 'ALERTS', attributes: [AlertViolationTime, AlertEntityType], // non-alphabetical because no Name }, - { - displayName: 'Node', - searchCategory: 'ALERTS', - attributes: [NodeAnnotation, NodeLabel, NodeName], - }, { displayName: 'Resource', searchCategory: 'ALERTS', From ff3b8dcb0a131e73abdee2f2cef6c43615c44fe2 Mon Sep 17 00:00:00 2001 From: Saif Chaudhry Date: Fri, 13 Mar 2026 11:05:25 -0700 Subject: [PATCH 5/5] refactor(violations): extract search filter config to utils and strengthen types Address PR review feedback: add ViolationSearchEntityName union type to prevent typos in entity name strings, use exact list assertions in tests instead of negative checks, and extract config/logic to a dedicated utils file to separate pure data from the React component. Signed-off-by: Saif Chaudhry --- .../ViolationsTableSearchFilter.test.ts | 35 ------ .../ViolationsTableSearchFilter.tsx | 100 +--------------- .../ViolationsTableSearchFilter.utils.test.ts | 45 ++++++++ .../ViolationsTableSearchFilter.utils.ts | 108 ++++++++++++++++++ 4 files changed, 155 insertions(+), 133 deletions(-) delete mode 100644 ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts create mode 100644 ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.utils.test.ts create mode 100644 ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.utils.ts diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts deleted file mode 100644 index 479d1f077b207..0000000000000 --- a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { getSearchFilterConfig } from './ViolationsTableSearchFilter'; - -describe('getSearchFilterConfig', () => { - it('should exclude Node and Resource entities from "Applications view"', () => { - const config = getSearchFilterConfig('Applications view'); - const names = config.map((e) => e.displayName); - - expect(names).not.toContain('Node'); - expect(names).not.toContain('Resource'); - }); - - it('should exclude Node and Resource entities from "Platform view"', () => { - const config = getSearchFilterConfig('Platform view'); - const names = config.map((e) => e.displayName); - - expect(names).not.toContain('Node'); - expect(names).not.toContain('Resource'); - }); - - it('should exclude Deployment, Namespace, and Resource entities from "Node view"', () => { - const config = getSearchFilterConfig('Node view'); - const names = config.map((e) => e.displayName); - - expect(names).not.toContain('Deployment'); - expect(names).not.toContain('Namespace'); - expect(names).not.toContain('Resource'); - expect(names).toContain('Node'); - }); - - it('should include all entities in "Full view"', () => { - const config = getSearchFilterConfig('Full view'); - - expect(config).toHaveLength(7); - }); -}); diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx index 0c8cf76ed94aa..c75333421079d 100644 --- a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.tsx @@ -3,108 +3,12 @@ import { Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from '@patternfly/ import type { SearchFilter } from 'types/search'; import useAnalytics from 'hooks/useAnalytics'; import { createFilterTracker } from 'utils/analyticsEventTracking'; -import type { - CompoundSearchFilterConfig, - OnSearchCallback, -} from 'Components/CompoundSearchFilter/types'; +import type { OnSearchCallback } from 'Components/CompoundSearchFilter/types'; import CompoundSearchFilter from 'Components/CompoundSearchFilter/components/CompoundSearchFilter'; import CompoundSearchFilterLabels from 'Components/CompoundSearchFilter/components/CompoundSearchFilterLabels'; -import { - Category as PolicyCategory, - LifecycleStage as PolicyLifecycleStage, - Name as PolicyName, - Severity as PolicySeverity, -} from 'Components/CompoundSearchFilter/attributes/policy'; -import { - EntityType as AlertEntityType, - ViolationTime as AlertViolationTime, -} from 'Components/CompoundSearchFilter/attributes/alert'; -import { - clusterIdAttribute, - clusterLabelAttribute, - clusterNameAttribute, -} from 'Components/CompoundSearchFilter/attributes/cluster'; -import { - Annotation as NamespaceAnnotation, - ID as NamespaceID, - Label as NamespaceLabel, - Name as NamespaceName, -} from 'Components/CompoundSearchFilter/attributes/namespace'; -import { - Annotation as DeploymentAnnotation, - ID as DeploymentID, - Inactive as DeploymentInactive, - Label as DeploymentLabel, - Name as DeploymentName, -} from 'Components/CompoundSearchFilter/attributes/deployment'; -import { Name as ResourceName } from 'Components/CompoundSearchFilter/attributes/resource'; -import { - Annotation as NodeAnnotation, - Label as NodeLabel, - Name as NodeName, -} from 'Components/CompoundSearchFilter/attributes/node'; import type { FilteredWorkflowView } from 'Components/FilteredWorkflowViewSelector/types'; -const allSearchFilterEntities: CompoundSearchFilterConfig = [ - { - displayName: 'Cluster', - searchCategory: 'ALERTS', - attributes: [clusterIdAttribute, clusterLabelAttribute, clusterNameAttribute], - }, - { - displayName: 'Deployment', - searchCategory: 'ALERTS', - attributes: [ - DeploymentAnnotation, - DeploymentID, - DeploymentLabel, - DeploymentName, - DeploymentInactive, // Status - ], - }, - { - displayName: 'Namespace', - searchCategory: 'ALERTS', - attributes: [NamespaceAnnotation, NamespaceID, NamespaceLabel, NamespaceName], - }, - { - displayName: 'Node', - searchCategory: 'ALERTS', - attributes: [NodeAnnotation, NodeLabel, NodeName], - }, - { - displayName: 'Policy', - searchCategory: 'ALERTS', - attributes: [PolicyCategory, PolicyLifecycleStage, PolicyName, PolicySeverity], - }, - { - displayName: 'Policy violation', - searchCategory: 'ALERTS', - attributes: [AlertViolationTime, AlertEntityType], // non-alphabetical because no Name - }, - { - displayName: 'Resource', - searchCategory: 'ALERTS', - attributes: [ResourceName], - }, -]; - -const allowedEntitiesByView: Record = { - 'Applications view': ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'], - 'Platform view': ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'], - 'Node view': ['Cluster', 'Policy', 'Policy violation', 'Node'], - 'Full view': [], -}; - -export function getSearchFilterConfig( - filteredWorkflowView: FilteredWorkflowView -): CompoundSearchFilterConfig { - const allowed = allowedEntitiesByView[filteredWorkflowView]; - if (allowed.length === 0) { - return allSearchFilterEntities; - } - return allSearchFilterEntities.filter((entity) => allowed.includes(entity.displayName)); -} +import { getSearchFilterConfig } from './ViolationsTableSearchFilter.utils'; export type ViolationsTableSearchFilterProps = { searchFilter: SearchFilter; diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.utils.test.ts b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.utils.test.ts new file mode 100644 index 0000000000000..20cc10b6c1b09 --- /dev/null +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.utils.test.ts @@ -0,0 +1,45 @@ +import { getSearchFilterConfig } from './ViolationsTableSearchFilter.utils'; + +describe('getSearchFilterConfig', () => { + it('should return exactly the expected entities for "Applications view"', () => { + const config = getSearchFilterConfig('Applications view'); + const names = config.map((e) => e.displayName).sort(); + + expect(names).toEqual( + ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'].sort() + ); + }); + + it('should return exactly the expected entities for "Platform view"', () => { + const config = getSearchFilterConfig('Platform view'); + const names = config.map((e) => e.displayName).sort(); + + expect(names).toEqual( + ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'].sort() + ); + }); + + it('should return exactly the expected entities for "Node view"', () => { + const config = getSearchFilterConfig('Node view'); + const names = config.map((e) => e.displayName).sort(); + + expect(names).toEqual(['Cluster', 'Node', 'Policy', 'Policy violation'].sort()); + }); + + it('should return all entities for "Full view"', () => { + const config = getSearchFilterConfig('Full view'); + const names = config.map((e) => e.displayName).sort(); + + expect(names).toEqual( + [ + 'Cluster', + 'Deployment', + 'Namespace', + 'Node', + 'Policy', + 'Policy violation', + 'Resource', + ].sort() + ); + }); +}); diff --git a/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.utils.ts b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.utils.ts new file mode 100644 index 0000000000000..49aab95c4fe31 --- /dev/null +++ b/ui/apps/platform/src/Containers/Violations/ViolationsTableSearchFilter.utils.ts @@ -0,0 +1,108 @@ +import type { CompoundSearchFilterConfig } from 'Components/CompoundSearchFilter/types'; +import { + Category as PolicyCategory, + LifecycleStage as PolicyLifecycleStage, + Name as PolicyName, + Severity as PolicySeverity, +} from 'Components/CompoundSearchFilter/attributes/policy'; +import { + EntityType as AlertEntityType, + ViolationTime as AlertViolationTime, +} from 'Components/CompoundSearchFilter/attributes/alert'; +import { + clusterIdAttribute, + clusterLabelAttribute, + clusterNameAttribute, +} from 'Components/CompoundSearchFilter/attributes/cluster'; +import { + Annotation as NamespaceAnnotation, + ID as NamespaceID, + Label as NamespaceLabel, + Name as NamespaceName, +} from 'Components/CompoundSearchFilter/attributes/namespace'; +import { + Annotation as DeploymentAnnotation, + ID as DeploymentID, + Inactive as DeploymentInactive, + Label as DeploymentLabel, + Name as DeploymentName, +} from 'Components/CompoundSearchFilter/attributes/deployment'; +import { Name as ResourceName } from 'Components/CompoundSearchFilter/attributes/resource'; +import { + Annotation as NodeAnnotation, + Label as NodeLabel, + Name as NodeName, +} from 'Components/CompoundSearchFilter/attributes/node'; +import type { FilteredWorkflowView } from 'Components/FilteredWorkflowViewSelector/types'; + +type ViolationSearchEntityName = + | 'Cluster' + | 'Deployment' + | 'Namespace' + | 'Node' + | 'Policy' + | 'Policy violation' + | 'Resource'; + +const allSearchFilterEntities: CompoundSearchFilterConfig = [ + { + displayName: 'Cluster', + searchCategory: 'ALERTS', + attributes: [clusterIdAttribute, clusterLabelAttribute, clusterNameAttribute], + }, + { + displayName: 'Deployment', + searchCategory: 'ALERTS', + attributes: [ + DeploymentAnnotation, + DeploymentID, + DeploymentLabel, + DeploymentName, + DeploymentInactive, // Status + ], + }, + { + displayName: 'Namespace', + searchCategory: 'ALERTS', + attributes: [NamespaceAnnotation, NamespaceID, NamespaceLabel, NamespaceName], + }, + { + displayName: 'Node', + searchCategory: 'ALERTS', + attributes: [NodeAnnotation, NodeLabel, NodeName], + }, + { + displayName: 'Policy', + searchCategory: 'ALERTS', + attributes: [PolicyCategory, PolicyLifecycleStage, PolicyName, PolicySeverity], + }, + { + displayName: 'Policy violation', + searchCategory: 'ALERTS', + attributes: [AlertViolationTime, AlertEntityType], // non-alphabetical because no Name + }, + { + displayName: 'Resource', + searchCategory: 'ALERTS', + attributes: [ResourceName], + }, +]; + +const allowedEntitiesByView: Record = { + 'Applications view': ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'], + 'Platform view': ['Cluster', 'Deployment', 'Namespace', 'Policy', 'Policy violation'], + 'Node view': ['Cluster', 'Policy', 'Policy violation', 'Node'], + 'Full view': [], +}; + +export function getSearchFilterConfig( + filteredWorkflowView: FilteredWorkflowView +): CompoundSearchFilterConfig { + const allowed = allowedEntitiesByView[filteredWorkflowView]; + if (allowed.length === 0) { + return allSearchFilterEntities; + } + return allSearchFilterEntities.filter((entity) => + (allowed as readonly string[]).includes(entity.displayName) + ); +}