({
options={cardSortSelectOptions}
/>
)}
+ {filterable && (
+
+
+ filterControlsRef.current?.clearFilters()}
+ >
+ {t('Clear all')}
+
+
+
+ )}
diff --git a/superset-frontend/src/pages/ChartList/ChartList.test.tsx b/superset-frontend/src/pages/ChartList/ChartList.test.tsx
index 6f9dedc5a09..1b16c7674b7 100644
--- a/superset-frontend/src/pages/ChartList/ChartList.test.tsx
+++ b/superset-frontend/src/pages/ChartList/ChartList.test.tsx
@@ -57,9 +57,12 @@ const mockUser = {
const findFilterByLabel = (labelText: string) => {
const containers = screen.getAllByTestId('select-filter-container');
for (const container of containers) {
- const label = container.querySelector('label');
- if (label?.textContent === labelText) {
- return container.querySelector('[role="combobox"], .ant-select');
+ // Compact pill filters show the label as button text
+ const pill = container.querySelector(
+ '[data-test="compact-filter-pill"]',
+ ) as HTMLElement | null;
+ if (pill && pill.textContent?.includes(labelText)) {
+ return pill;
}
}
return null;
diff --git a/superset-frontend/src/pages/DashboardList/DashboardList.cardview.test.tsx b/superset-frontend/src/pages/DashboardList/DashboardList.cardview.test.tsx
index c03299cfc43..7733251176d 100644
--- a/superset-frontend/src/pages/DashboardList/DashboardList.cardview.test.tsx
+++ b/superset-frontend/src/pages/DashboardList/DashboardList.cardview.test.tsx
@@ -156,18 +156,16 @@ describe('DashboardList Card View Tests', () => {
).toBeInTheDocument();
});
- // Find the sort select by its testId, then the combobox within it
+ // Find the sort select by its testId, then the pill button within it
const sortContainer = screen.getByTestId('card-sort-select');
- const sortCombobox = within(sortContainer).getByRole('combobox');
- await userEvent.click(sortCombobox);
+ // eslint-disable-next-line testing-library/no-node-access
+ const sortPill = sortContainer.querySelector(
+ '[data-test="compact-filter-pill"]',
+ ) as HTMLElement;
+ await userEvent.click(sortPill);
// Select "Alphabetical" from the dropdown
- const alphabeticalOption = await waitFor(() =>
- within(
- // eslint-disable-next-line testing-library/no-node-access
- document.querySelector('.rc-virtual-list')!,
- ).getByText('Alphabetical'),
- );
+ const alphabeticalOption = await screen.findByText('Alphabetical');
await userEvent.click(alphabeticalOption);
await waitFor(() => {
diff --git a/superset-frontend/src/pages/DashboardList/DashboardList.test.tsx b/superset-frontend/src/pages/DashboardList/DashboardList.test.tsx
index 2f4b8f9dc5e..87c974df801 100644
--- a/superset-frontend/src/pages/DashboardList/DashboardList.test.tsx
+++ b/superset-frontend/src/pages/DashboardList/DashboardList.test.tsx
@@ -20,7 +20,7 @@ import fetchMock from 'fetch-mock';
import { isFeatureEnabled } from '@superset-ui/core';
import {
screen,
- selectOption,
+ selectPillOption,
waitFor,
fireEvent,
} from 'spec/helpers/testing-library';
@@ -200,7 +200,7 @@ test('selecting Status filter encodes published=true in API call', async () => {
).toBeInTheDocument();
});
- await selectOption('Published', 'Status');
+ await selectPillOption('Published', 'Status');
await waitFor(() => {
const latest = getLatestDashboardApiCall();
@@ -242,7 +242,7 @@ test('selecting Owner filter encodes rel_m_m owner in API call', async () => {
).toBeInTheDocument();
});
- await selectOption('Admin User', 'Owner');
+ await selectPillOption('Admin User', 'Owner');
await waitFor(() => {
const latest = getLatestDashboardApiCall();
@@ -287,7 +287,7 @@ test('selecting Modified by filter encodes rel_o_m changed_by in API call', asyn
).toBeInTheDocument();
});
- await selectOption('Admin User', 'Modified by');
+ await selectPillOption('Admin User', 'Modified by');
await waitFor(() => {
const latest = getLatestDashboardApiCall();
diff --git a/superset-frontend/src/pages/DatasetList/DatasetList.integration.test.tsx b/superset-frontend/src/pages/DatasetList/DatasetList.integration.test.tsx
index 2a28b7bb11c..ee654a77458 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.integration.test.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.integration.test.tsx
@@ -20,7 +20,7 @@ import { act, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import fetchMock from 'fetch-mock';
import rison from 'rison';
-import { selectOption } from 'spec/helpers/testing-library';
+import { selectPillOption } from 'spec/helpers/testing-library';
import {
setupMocks,
renderDatasetList,
@@ -102,11 +102,11 @@ test('ListView provider correctly merges filter + sort + pagination state on ref
).toBeGreaterThan(callsBeforeSort);
});
- // 2. Apply a filter using selectOption helper
+ // 2. Apply a filter using selectPillOption helper (compact pill UI)
const beforeFilterCallCount = fetchMock.callHistory.calls(
API_ENDPOINTS.DATASOURCE_COMBINED,
).length;
- await selectOption('Virtual', 'Type');
+ await selectPillOption('Virtual', 'Type');
// Wait for filter API call to complete
await waitFor(() => {
diff --git a/superset-frontend/src/pages/DatasetList/DatasetList.listview.test.tsx b/superset-frontend/src/pages/DatasetList/DatasetList.listview.test.tsx
index 883658156c9..6ad588a8128 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.listview.test.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.listview.test.tsx
@@ -27,7 +27,7 @@ import userEvent from '@testing-library/user-event';
import fetchMock from 'fetch-mock';
import rison from 'rison';
import { SupersetClient } from '@superset-ui/core';
-import { selectOption } from 'spec/helpers/testing-library';
+import { selectPillOption } from 'spec/helpers/testing-library';
import {
setupMocks,
renderDatasetList,
@@ -1510,11 +1510,8 @@ test('bulk selection clears when filter changes', async () => {
API_ENDPOINTS.DATASOURCE_COMBINED,
).length;
- // Wait for filter combobox to be ready before applying filter
- await screen.findByRole('combobox', { name: 'Type' });
-
- // Apply a filter using selectOption helper
- await selectOption('Virtual', 'Type');
+ // Apply a filter using selectPillOption helper (compact pill UI)
+ await selectPillOption('Virtual', 'Type');
// Wait for filter API call to complete
await waitFor(() => {
@@ -1556,16 +1553,13 @@ test('type filter API call includes correct filter parameter', async () => {
expect(screen.getByTestId('listview-table')).toBeInTheDocument();
});
- // Wait for Type filter combobox
- await screen.findByRole('combobox', { name: 'Type' });
-
// Snapshot call count before filter
const callsBeforeFilter = fetchMock.callHistory.calls(
API_ENDPOINTS.DATASOURCE_COMBINED,
).length;
- // Apply Type filter
- await selectOption('Virtual', 'Type');
+ // Apply Type filter using compact pill UI
+ await selectPillOption('Virtual', 'Type');
// Wait for filter API call to complete
await waitFor(() => {
@@ -1606,16 +1600,13 @@ test('type filter persists after duplicating a dataset', async () => {
expect(screen.getByTestId('listview-table')).toBeInTheDocument();
});
- // Wait for Type filter combobox
- await screen.findByRole('combobox', { name: 'Type' });
-
// Snapshot call count before filter
const callsBeforeFilter = fetchMock.callHistory.calls(
API_ENDPOINTS.DATASOURCE_COMBINED,
).length;
- // Apply Type filter
- await selectOption('Virtual', 'Type');
+ // Apply Type filter using compact pill UI
+ await selectPillOption('Virtual', 'Type');
// Wait for filter API call to complete
await waitFor(() => {
diff --git a/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx b/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx
index 2d1c4752e9b..ee4c1257aae 100644
--- a/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx
+++ b/superset-frontend/src/pages/DatasetList/DatasetList.test.tsx
@@ -200,8 +200,8 @@ test('renders Name search filter', async () => {
test('renders Type filter (Virtual/Physical dropdown)', async () => {
renderDatasetList(mockAdminUser);
- // Filter dropdowns should be present
- const filters = await screen.findAllByRole('combobox');
+ // Filter pills should be present (compact pill UI)
+ const filters = await screen.findAllByTestId('compact-filter-pill');
expect(filters.length).toBeGreaterThan(0);
});
@@ -445,7 +445,8 @@ test('selecting Database filter triggers API call with database relation filter'
await waitForDatasetsPageReady();
- const filtersContainers = screen.getAllByRole('combobox');
+ // Filter pills should be present (compact pill UI replaces comboboxes)
+ const filtersContainers = screen.getAllByTestId('compact-filter-pill');
expect(filtersContainers.length).toBeGreaterThan(0);
});
diff --git a/superset-frontend/src/pages/GroupsList/GroupsList.test.tsx b/superset-frontend/src/pages/GroupsList/GroupsList.test.tsx
index 785066048e8..81bd86f8078 100644
--- a/superset-frontend/src/pages/GroupsList/GroupsList.test.tsx
+++ b/superset-frontend/src/pages/GroupsList/GroupsList.test.tsx
@@ -121,13 +121,17 @@ describe('GroupsList', () => {
test('renders the filters correctly', async () => {
await renderComponent();
- const filtersSelect = screen.getAllByTestId('filters-select')[0];
- expect(within(filtersSelect).getByText(/name/i)).toBeInTheDocument();
- expect(within(filtersSelect).getByText(/label/i)).toBeInTheDocument();
- expect(within(filtersSelect).getByText(/description/i)).toBeInTheDocument();
- expect(within(filtersSelect).getByText(/roles/i)).toBeInTheDocument();
- expect(within(filtersSelect).getByText(/users/i)).toBeInTheDocument();
+ // The compact filter UI renders the first search filter as an input,
+ // and select filters as pill buttons. Only "Name" search renders inline;
+ // "Label" and "Description" searches are hidden (one search box per page).
+ expect(screen.getByTestId('filters-search')).toBeInTheDocument();
+
+ // Select filters render as compact pill buttons
+ const pills = screen.getAllByTestId('compact-filter-pill');
+ const pillLabels = pills.map(p => p.textContent ?? '');
+ expect(pillLabels.some(l => /roles/i.test(l))).toBe(true);
+ expect(pillLabels.some(l => /users/i.test(l))).toBe(true);
});
test('renders correct columns in the table', async () => {
diff --git a/superset-frontend/src/pages/RolesList/RolesList.test.tsx b/superset-frontend/src/pages/RolesList/RolesList.test.tsx
index 39d8069429c..6a179238d4e 100644
--- a/superset-frontend/src/pages/RolesList/RolesList.test.tsx
+++ b/superset-frontend/src/pages/RolesList/RolesList.test.tsx
@@ -151,8 +151,11 @@ describe('RolesList', () => {
test('renders filters options', async () => {
await renderAndWait();
- const typeFilter = screen.queryAllByTestId('filters-select');
- expect(typeFilter).toHaveLength(4);
+ // Compact filter UI: one search input for "Name" and 3 select pills
+ // (Users, Permissions, Groups).
+ expect(screen.getByTestId('filters-search')).toBeInTheDocument();
+ const selectContainers = screen.getAllByTestId('select-filter-container');
+ expect(selectContainers).toHaveLength(3);
});
test('renders correct list columns', async () => {
diff --git a/superset-frontend/src/pages/RowLevelSecurityList/RowLevelSecurityList.test.tsx b/superset-frontend/src/pages/RowLevelSecurityList/RowLevelSecurityList.test.tsx
index b23b2421461..538e7ae41b1 100644
--- a/superset-frontend/src/pages/RowLevelSecurityList/RowLevelSecurityList.test.tsx
+++ b/superset-frontend/src/pages/RowLevelSecurityList/RowLevelSecurityList.test.tsx
@@ -166,11 +166,14 @@ describe('RuleList RTL', () => {
test('renders filter options', async () => {
await renderAndWait();
+ // Compact filter UI: only the first search filter renders (Name),
+ // subsequent search filters (Group Key) are hidden — one search box per page.
const searchFilters = screen.queryAllByTestId('filters-search');
- expect(searchFilters).toHaveLength(2);
+ expect(searchFilters).toHaveLength(1);
- const typeFilter = screen.queryAllByTestId('filters-select');
- expect(typeFilter).toHaveLength(3); // Update to expect 3 select filters
+ // Select filters render as compact pill buttons (Filter Type, Modified by)
+ const selectContainers = screen.queryAllByTestId('select-filter-container');
+ expect(selectContainers).toHaveLength(2);
});
test('renders correct list columns', async () => {
diff --git a/superset-frontend/src/pages/UsersList/UsersList.test.tsx b/superset-frontend/src/pages/UsersList/UsersList.test.tsx
index e994bd485cc..63539113eed 100644
--- a/superset-frontend/src/pages/UsersList/UsersList.test.tsx
+++ b/superset-frontend/src/pages/UsersList/UsersList.test.tsx
@@ -138,16 +138,16 @@ describe('UsersList', () => {
test('renders filters options', async () => {
await renderAndWait();
- const submenu = screen.queryAllByTestId('filters-select')[0];
- expect(within(submenu).getByText(/first name/i)).toBeInTheDocument();
- expect(within(submenu).getByText(/last name/i)).toBeInTheDocument();
- expect(within(submenu).getByText(/email/i)).toBeInTheDocument();
- expect(within(submenu).getByText(/username/i)).toBeInTheDocument();
- expect(within(submenu).getByText(/roles/i)).toBeInTheDocument();
- expect(within(submenu).getByText(/is active?/i)).toBeInTheDocument();
- expect(within(submenu).getByText(/created on/i)).toBeInTheDocument();
- expect(within(submenu).getByText(/changed on/i)).toBeInTheDocument();
- expect(within(submenu).getByText(/last login/i)).toBeInTheDocument();
+ // The compact filter UI shows: only the first search filter as an input,
+ // and select/datetime filters as pill buttons. Only "First name" search
+ // renders (subsequent search filters are hidden — one search box per page).
+ expect(screen.getByTestId('filters-search')).toBeInTheDocument();
+
+ // Select and datetime filters render as compact pill buttons
+ const pills = screen.getAllByTestId('compact-filter-pill');
+ const pillLabels = pills.map(p => p.textContent ?? '');
+ expect(pillLabels.some(l => /roles/i.test(l))).toBe(true);
+ expect(pillLabels.some(l => /is active\?/i.test(l))).toBe(true);
});
test('renders correct list columns', async () => {