unlock unlimited code runs for $79, limited time →
NOSSLETTER.techLIVE
back to OSSpedia
refactoringapi-designtesting-strategy

fix(ui): remove kind filter from appset page (#29310)

Project: argoproj/argo-cd | Language: Go (TypeScript/React UI) | Concepts: refactoring, api-design, testing-strategy | Difficulty: junior
Link: https://github.com/argoproj/argo-cd/pull/29311

The problem

On the ApplicationSet details page in Argo CD, the sidebar filter UI included a "Kind" filter. However, for ApplicationSets, the only possible kind is always "Application", so this filter was confusing and unnecessary.

Why it's non-trivial

A naive fix would simply hide the "Kind" filter UI, but this would not prevent previously persisted or URL-based filters from still applying a "kind:" filter invisibly, potentially causing the resource list to appear empty with no way for the user to clear the filter. The logic for applying filters and rendering the filter UI had to be carefully coordinated to avoid this.

The approach

1. Remove "kind" filters from the effective resource filter when viewing an ApplicationSet

A new utility function is added to strip out "kind:" filters when not viewing a regular Application:

+export function withoutKindResourceFilters(filters: string[]): string[] {
+    return (filters || []).filter(f => !f.startsWith('kind:'));
+}
+
+export function getEffectiveResourceFilter(isApplication: boolean, resourceFilter?: string[]): string[] {
+    return isApplication ? resourceFilter || [] : withoutKindResourceFilters(resourceFilter);
+}
  • withoutKindResourceFilters removes any filter starting with "kind:".
  • getEffectiveResourceFilter returns the original filters for Applications, but strips "kind:" filters for ApplicationSets.

2. Use the effective filter in the resource tree

When rendering the resource tree, the code now uses the effective filter (with "kind:" filters removed for ApplicationSets):

-                            const treeFilter = getTreeFilter(pref.resourceFilter);
+                            const effectiveResourceFilter = getEffectiveResourceFilter(isApplication, pref.resourceFilter);
+                            const treeFilter = getTreeFilter(effectiveResourceFilter);
  • This ensures that even if a "kind:" filter is present in preferences or the URL, it is ignored for ApplicationSets.

3. Hide the "Kind" filter UI via a prop

A new prop, hideKindFilter, is added to the Filters component. It is set to true when rendering an ApplicationSet:

+                                                                    hideKindFilter={!isApplication}
  • This is passed to the Filters component, which conditionally renders the "Kind" filter UI:
-            {ResourceFilter({
-                label: 'KINDS',
-                prefix: 'kind',
-                options: kinds.map(label => ({
-                    label,
-                    count: getOptionCount(label, 'Kind')
-                })),
-                abbreviations: resources,
-                field: true
-            })}
+            {!hideKindFilter &&
+                ResourceFilter({
+                    label: 'KINDS',
+                    prefix: 'kind',
+                    options: kinds.map(label => ({
+                        label,
+                        count: getOptionCount(label, 'Kind')
+                    })),
+                    abbreviations: resources,
+                    field: true
+                })}
  • If hideKindFilter is true, the "Kind" filter is not shown.

4. Add tests for filter logic

A new test file verifies that "kind:" filters are ignored for ApplicationSets:

+import {getEffectiveResourceFilter} from './application-resource-filter';
+
+test('ApplicationSet ignores kind filters when applying resource filters', () => {
+    expect(getEffectiveResourceFilter(false, ['kind:Deployment', 'sync:OutOfSync'])).toEqual(['sync:OutOfSync']);
+});
+
+test('Application still applies kind filters', () => {
+    expect(getEffectiveResourceFilter(true, ['kind:Deployment', 'sync:OutOfSync'])).toEqual(['kind:Deployment', 'sync:OutOfSync']);
+});

From the review thread

No notable review discussion on this PR.

Takeaways

  • UI changes should be coordinated with underlying logic: Hiding a filter in the UI is not enough; ensure that persisted or URL-based filters are also handled in application logic to prevent invisible or "stuck" filters.
  • Feature flags/props help manage conditional UI: Passing a prop like hideKindFilter allows for flexible, context-sensitive UI changes without duplicating components.
  • Test the business logic, not just the UI: Adding tests for filter application logic ensures that changes behave as expected even if the UI changes again in the future.