From a8ae170f28c2bf47eb5eda1120670377474932fa Mon Sep 17 00:00:00 2001 From: Rohan Chakraborty Date: Mon, 5 Oct 2026 12:43:35 +0530 Subject: [PATCH 1/4] fix: [data-view] hide select filters that have no options --- apps/www/src/content/docs/dataview/props.ts | 2 +- .../data-view/__tests__/data-view.test.tsx | 38 +++++++++++++++++++ .../data-view/components/filters.tsx | 9 ++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/apps/www/src/content/docs/dataview/props.ts b/apps/www/src/content/docs/dataview/props.ts index 91a8bd61f..9766f346b 100644 --- a/apps/www/src/content/docs/dataview/props.ts +++ b/apps/www/src/content/docs/dataview/props.ts @@ -92,7 +92,7 @@ export interface DataViewField { /** Filter input type. */ filterType?: 'string' | 'number' | 'date' | 'select' | 'multiselect'; - /** Options when filterType is select/multiselect. */ + /** Options when filterType is select/multiselect. The add-filter menu leaves out the field while this is empty. */ filterOptions?: Array<{ label: string; value: string }>; /** Allow sorting. */ diff --git a/packages/raystack/components/data-view/__tests__/data-view.test.tsx b/packages/raystack/components/data-view/__tests__/data-view.test.tsx index 9e98859d4..f0206a145 100644 --- a/packages/raystack/components/data-view/__tests__/data-view.test.tsx +++ b/packages/raystack/components/data-view/__tests__/data-view.test.tsx @@ -364,6 +364,44 @@ describe('DataView', () => { ).toBeInTheDocument(); }); + it('does not offer select or multiselect fields without filterOptions', () => { + const fields: DataViewField[] = [ + { accessorKey: 'name', label: 'Name', filterable: true }, + { + accessorKey: 'status', + label: 'Status', + filterable: true, + filterType: 'select', + filterOptions: [{ label: 'Active', value: 'string' }] + }, + { + accessorKey: 'email', + label: 'Email', + filterable: true, + filterType: 'select', + filterOptions: [] + }, + { + accessorKey: 'id', + label: 'Id', + filterable: true, + filterType: 'multiselect' + } + ]; + render( + + ( + + )} + /> + + ); + expect(screen.getByRole('button')).toHaveTextContent(/^Name,Status$/); + }); + it('search input updates the query', async () => { const user = userEvent.setup(); render( diff --git a/packages/raystack/components/data-view/components/filters.tsx b/packages/raystack/components/data-view/components/filters.tsx index c2e55708f..682dc93df 100644 --- a/packages/raystack/components/data-view/components/filters.tsx +++ b/packages/raystack/components/data-view/components/filters.tsx @@ -115,7 +115,14 @@ export function Filters({ handleFilterOperationChange } = useFilters(); - const filterableFields = fields?.filter(f => f.filterable) ?? []; + const filterableFields = + fields?.filter( + f => + f.filterable && + (f.filterOptions?.length || + (f.filterType !== FilterType.select && + f.filterType !== FilterType.multiselect)) + ) ?? []; const appliedFiltersSet = new Set( tableQuery?.filters?.map(filter => filter.name) From 52f0084657af2d935371f6a00e823d56583edb7c Mon Sep 17 00:00:00 2001 From: Rohan Chakraborty Date: Mon, 5 Oct 2026 15:50:54 +0530 Subject: [PATCH 2/4] fix: [data-view] keep filter trigger while select options load --- apps/www/src/content/docs/dataview/props.ts | 2 +- .../data-view/__tests__/data-view.test.tsx | 20 +++++++++++++++++++ .../data-view/components/filters.tsx | 20 +++++++++---------- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/apps/www/src/content/docs/dataview/props.ts b/apps/www/src/content/docs/dataview/props.ts index 9766f346b..d7f40e846 100644 --- a/apps/www/src/content/docs/dataview/props.ts +++ b/apps/www/src/content/docs/dataview/props.ts @@ -92,7 +92,7 @@ export interface DataViewField { /** Filter input type. */ filterType?: 'string' | 'number' | 'date' | 'select' | 'multiselect'; - /** Options when filterType is select/multiselect. The add-filter menu leaves out the field while this is empty. */ + /** Options when filterType is select/multiselect. The add-filter menu leaves out the field while this is empty, and the trigger is disabled if no other field is available. */ filterOptions?: Array<{ label: string; value: string }>; /** Allow sorting. */ diff --git a/packages/raystack/components/data-view/__tests__/data-view.test.tsx b/packages/raystack/components/data-view/__tests__/data-view.test.tsx index f0206a145..65af8f339 100644 --- a/packages/raystack/components/data-view/__tests__/data-view.test.tsx +++ b/packages/raystack/components/data-view/__tests__/data-view.test.tsx @@ -402,6 +402,26 @@ describe('DataView', () => { expect(screen.getByRole('button')).toHaveTextContent(/^Name,Status$/); }); + it('disables the filter trigger while select options are loading', () => { + render( + + + + ); + expect(screen.getByRole('button', { name: /filter/i })).toBeDisabled(); + }); + it('search input updates the query', async () => { const user = userEvent.setup(); render( diff --git a/packages/raystack/components/data-view/components/filters.tsx b/packages/raystack/components/data-view/components/filters.tsx index 682dc93df..6a368cccc 100644 --- a/packages/raystack/components/data-view/components/filters.tsx +++ b/packages/raystack/components/data-view/components/filters.tsx @@ -37,9 +37,15 @@ function AddFilter({ children, className }: AddFilterProps) { - const availableFilters = fieldList?.filter( + const unappliedFields = fieldList.filter( f => !appliedFiltersSet.has(f.accessorKey) ); + const availableFilters = unappliedFields.filter( + f => + f.filterOptions?.length || + (f.filterType !== FilterType.select && + f.filterType !== FilterType.multiselect) + ); const trigger = useMemo(() => { if (typeof children === 'function') @@ -70,9 +76,10 @@ function AddFilter({ ); }, [children, appliedFiltersSet, availableFilters, className]); - return availableFilters.length > 0 ? ( + return unappliedFields.length > 0 ? ( {trigger}} /> @@ -115,14 +122,7 @@ export function Filters({ handleFilterOperationChange } = useFilters(); - const filterableFields = - fields?.filter( - f => - f.filterable && - (f.filterOptions?.length || - (f.filterType !== FilterType.select && - f.filterType !== FilterType.multiselect)) - ) ?? []; + const filterableFields = fields?.filter(f => f.filterable) ?? []; const appliedFiltersSet = new Set( tableQuery?.filters?.map(filter => filter.name) From e283e60748792a45cc6d942ad9ac13daf42c630a Mon Sep 17 00:00:00 2001 From: Rohan Chakraborty Date: Mon, 12 Oct 2026 01:04:16 +0530 Subject: [PATCH 3/4] test: [data-view] assert add-filter menu items and fix fixture value --- .../data-view/__tests__/data-view.test.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/react/components/data-view/__tests__/data-view.test.tsx b/packages/react/components/data-view/__tests__/data-view.test.tsx index 54f8d1bd2..f33c1bd2e 100644 --- a/packages/react/components/data-view/__tests__/data-view.test.tsx +++ b/packages/react/components/data-view/__tests__/data-view.test.tsx @@ -372,7 +372,8 @@ describe('DataView', () => { ).toBeInTheDocument(); }); - it('does not offer select or multiselect fields without filterOptions', () => { + it('does not offer select or multiselect fields without filterOptions', async () => { + const user = userEvent.setup(); const fields: DataViewField[] = [ { accessorKey: 'name', label: 'Name', filterable: true }, { @@ -380,7 +381,7 @@ describe('DataView', () => { label: 'Status', filterable: true, filterType: 'select', - filterOptions: [{ label: 'Active', value: 'string' }] + filterOptions: [{ label: 'Active', value: 'active' }] }, { accessorKey: 'email', @@ -407,7 +408,14 @@ describe('DataView', () => { /> ); - expect(screen.getByRole('button')).toHaveTextContent(/^Name,Status$/); + const trigger = screen.getByRole('button'); + expect(trigger).toHaveTextContent(/^Name,Status$/); + await user.click(trigger); + await screen.findByRole('menuitem', { name: 'Status' }); + expect(screen.getAllByRole('menuitem').map(i => i.textContent)).toEqual([ + 'Name', + 'Status' + ]); }); it('disables the filter trigger while select options are loading', () => { From bb91fa003bb9105dde1788e33aee664bf324d7ba Mon Sep 17 00:00:00 2001 From: Rohan Chakraborty Date: Mon, 12 Oct 2026 14:54:47 +0530 Subject: [PATCH 4/4] test: [data-view] restore fixture option value --- .../react/components/data-view/__tests__/data-view.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/components/data-view/__tests__/data-view.test.tsx b/packages/react/components/data-view/__tests__/data-view.test.tsx index f33c1bd2e..577555369 100644 --- a/packages/react/components/data-view/__tests__/data-view.test.tsx +++ b/packages/react/components/data-view/__tests__/data-view.test.tsx @@ -381,7 +381,7 @@ describe('DataView', () => { label: 'Status', filterable: true, filterType: 'select', - filterOptions: [{ label: 'Active', value: 'active' }] + filterOptions: [{ label: 'Active', value: 'string' }] }, { accessorKey: 'email',