diff --git a/superset-frontend/src/dashboard/components/nativeFilters/ConfigModal/SharedStyles.tsx b/superset-frontend/src/dashboard/components/nativeFilters/ConfigModal/SharedStyles.tsx index b51ac560722..eccae1077d9 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/ConfigModal/SharedStyles.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/ConfigModal/SharedStyles.tsx @@ -75,6 +75,7 @@ export const BaseModalWrapper = styled(StyledModal)` export const BaseModalBody = styled.div` display: flex; height: 100%; + min-height: 500px; flex-direction: row; flex: 1; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx index 2ece961fd33..d00093f0c51 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx @@ -17,7 +17,13 @@ * under the License. */ -import { act, render, screen, userEvent } from 'spec/helpers/testing-library'; +import { + act, + fireEvent, + render, + screen, + userEvent, +} from 'spec/helpers/testing-library'; import { stateWithoutNativeFilters } from 'spec/fixtures/mockStore'; import { testWithId } from 'src/utils/testUtils'; import { Preset, makeApi } from '@superset-ui/core'; @@ -387,6 +393,15 @@ test('FilterBar apply button is disabled after creating a filter', async () => { userEvent.click(screen.getByTestId(getTestId('collapsable'))); userEvent.click(screen.getByLabelText('setting')); userEvent.click(screen.getByText('Add or edit filters and controls')); + + // First add a filter via the dropdown (modal now shows empty state by default) + const dropdownButton = screen.getByTestId('new-item-dropdown-button'); + fireEvent.mouseEnter(dropdownButton); + const addFilterMenuItem = await screen.findByRole('menuitem', { + name: /add filter/i, + }); + fireEvent.click(addFilterMenuItem); + userEvent.click(screen.getByText('Value')); userEvent.click(screen.getByText('Time range')); userEvent.type( diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/index.tsx index 199287e678d..018c8dd14b3 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/index.tsx @@ -84,7 +84,7 @@ const FilterBarSettings = () => { const { openFilterConfigModal, FilterConfigModalComponent } = useFilterConfigModal({ - createNewOnOpen: filterValues.length === 0, + createNewOnOpen: false, dashboardId, }); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/ConfigModalContent/ConfigModalContent.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/ConfigModalContent/ConfigModalContent.tsx index 156a710e498..8bbd7fd8b96 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/ConfigModalContent/ConfigModalContent.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/ConfigModalContent/ConfigModalContent.tsx @@ -25,7 +25,8 @@ import { } from '@superset-ui/core'; import type { FormInstance } from '@superset-ui/core/components'; import { styled } from '@apache-superset/core/theme'; -import { Flex } from '@superset-ui/core/components'; +import { EmptyState, Flex } from '@superset-ui/core/components'; +import { t } from '@apache-superset/core/translation'; import FilterContentRenderer from './FilterContentRenderer'; import CustomizationContentRenderer from './CustomizationContentRenderer'; import { FiltersConfigFormHandle } from '../FiltersConfigForm/FiltersConfigForm'; @@ -105,6 +106,28 @@ function ConfigModalContent({ isChartCustomizationId(currentItemId) && chartCustomizationIds.includes(currentItemId); + const hasNoItems = + filterState.orderedIds.length === 0 && + customizationState.orderedIds.length === 0; + const showEmptyState = hasNoItems || !currentItemId; + + if (showEmptyState) { + return ( + + + + + + ); + } + return (
theme.sizeUnit * 3}px; + + & button { + width: 100%; + } `; // min-height: 0 lets the flex item shrink below its content size so that @@ -264,6 +268,9 @@ const ConfigModalSidebar: FC = ({
); + const hasNoItems = + filterOrderedIds.length === 0 && customizationOrderedIds.length === 0; + return ( = ({ onAddCustomization={onAddCustomization} /> - onCollapseChange(keys as string[])} - ghost - isDragging={isDragging} - > - - + - - - + ) : ( + onCollapseChange(keys as string[])} + ghost + isDragging={isDragging} > - - - + + + + + + + + + )} ); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx index 36f9977c2ce..3583671ad59 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx @@ -772,3 +772,58 @@ test('renders a filter with a chart containing BigInt values', async () => { expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); }); + +test('displays empty state when modal opens with no filters and createNewOnOpen is false', () => { + defaultRender(defaultState(), { ...props, createNewOnOpen: false }); + + // Check left panel empty state + expect( + screen.getByText('No filters or customizations created yet'), + ).toBeInTheDocument(); + + // Check right panel empty state + expect( + screen.getByText( + /Manage filters and customizations to set scoping, descriptions, and limitations/, + ), + ).toBeInTheDocument(); + + // Verify no filter form is rendered (no "Untitled" filter created) + expect(screen.queryByText(FILTER_TYPE_REGEX)).not.toBeInTheDocument(); +}); + +test('does not auto-create a filter when createNewOnOpen is false', () => { + defaultRender(defaultState(), { ...props, createNewOnOpen: false }); + + // The filter configuration form should not be visible + expect(screen.queryByText(FILTER_NAME_REGEX)).not.toBeInTheDocument(); + expect(screen.queryByText(DATASET_REGEX)).not.toBeInTheDocument(); +}); + +test('empty state disappears when a filter is added via dropdown', async () => { + defaultRender(defaultState(), { + ...props, + createNewOnOpen: false, + }); + + // Verify empty state is shown initially + expect( + screen.getByText('No filters or customizations created yet'), + ).toBeInTheDocument(); + + // Add a filter via the dropdown + const dropdownButton = screen.getByTestId('new-item-dropdown-button'); + fireEvent.mouseEnter(dropdownButton); + const addFilterMenuItem = await screen.findByRole('menuitem', { + name: /add filter/i, + }); + fireEvent.click(addFilterMenuItem); + + // Verify empty state is gone and filter form is shown + await waitFor(() => { + expect( + screen.queryByText('No filters or customizations created yet'), + ).not.toBeInTheDocument(); + }); + expect(screen.getByText(FILTER_TYPE_REGEX)).toBeInTheDocument(); +});