diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx index fca1fb372e4a..8c8defeb18ee 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx @@ -51,7 +51,7 @@ const formMock: FormInstance = { const filterMock: Filter = { cascadeParentIds: [], defaultDataMask: {}, - id: 'mock', + id: 'filterMockId', name: 'mock', scope: { rootPath: [], @@ -69,7 +69,7 @@ const createProps: () => ControlItemsProps = () => ({ disabled: false, forceUpdate: jest.fn(), form: formMock, - filterId: 'filterId', + filterId: 'filterMockId', filterToEdit: filterMock, filterType: 'filterType', }); @@ -78,8 +78,25 @@ const createControlItems = () => [ null, false, {}, - { name: 'name_1', config: { renderTrigger: true, resetConfig: true } }, + { + name: 'name_1', + config: { type: 'CheckboxControl', renderTrigger: true, resetConfig: true }, + }, { name: 'groupby', config: { multiple: true, required: false } }, + { + name: 'select_1', + config: { + type: 'SelectControl', + renderTrigger: true, + freeForm: true, + default: 1, + choices: [ + [0, 'choice_0'], + [1, 'choice_1'], + [2, 'choice_2'], + ], + }, + }, ]; beforeEach(() => { @@ -146,7 +163,10 @@ test('Should render render ControlItems', () => { const controlItems = [ ...createControlItems(), - { name: 'name_2', config: { renderTrigger: true } }, + { + name: 'name_2', + config: { type: 'CheckboxControl', renderTrigger: true }, + }, ]; (getControlItems as jest.Mock).mockReturnValue(controlItems); const controlItemsMap = getControlItemsMap(props); @@ -169,7 +189,14 @@ test('Clickin on checkbox', () => { test('Clickin on checkbox when resetConfig:flase', () => { const props = createProps(); (getControlItems as jest.Mock).mockReturnValue([ - { name: 'name_1', config: { renderTrigger: true, resetConfig: false } }, + { + name: 'name_1', + config: { + type: 'CheckboxControl', + renderTrigger: true, + resetConfig: false, + }, + }, ]); const controlItemsMap = getControlItemsMap(props); renderControlItems(controlItemsMap); @@ -179,3 +206,26 @@ test('Clickin on checkbox when resetConfig:flase', () => { expect(props.forceUpdate).toBeCalled(); expect(setNativeFilterFieldValues).not.toBeCalled(); }); + +test('Should render SelectControl ControlItems', () => { + const props = createProps(); + const controlItems = [...createControlItems()]; + (getControlItems as jest.Mock).mockReturnValue(controlItems); + const controlItemsMap = getControlItemsMap(props); + renderControlItems(controlItemsMap); + expect(screen.getByRole('combobox')).toBeInTheDocument(); +}); + +test('Should change selection for SelectControl', () => { + const props = createProps(); + const controlItems = [...createControlItems()]; + (getControlItems as jest.Mock).mockReturnValue(controlItems); + const controlItemsMap = getControlItemsMap(props); + renderControlItems(controlItemsMap); + userEvent.click(screen.getByRole('combobox')); + userEvent.click(screen.getByTitle('choice_2')); + expect(setNativeFilterFieldValues).toBeCalledWith(formMock, filterMock.id, { + select_1: 2, + defaultDataMask: null, + }); +}); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx index 22078c26a926..4e804d2f4cd9 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx @@ -19,6 +19,7 @@ import { CustomControlItem, InfoTooltipWithTrigger, + ControlValueValidator, } from '@superset-ui/chart-controls'; import React from 'react'; import { AntdCheckbox, FormInstance } from 'src/components'; @@ -30,6 +31,8 @@ import { } from '@superset-ui/core'; import { Tooltip } from 'src/components/Tooltip'; import { FormItem } from 'src/components/Form'; +import SelectControl from 'src/explore/components/controls/SelectControl'; +import { RuleObject } from 'antd/lib/form'; import { doesColumnMatchFilterType, getControlItems, @@ -59,6 +62,25 @@ const CleanFormItem = styled(FormItem)` margin-bottom: 0; `; +// converts the controlPanel validators to an rc-field-form rule +const getControlItemRules = ( + validators: ControlValueValidator[] | undefined, +) => { + if (validators) { + const v = validators.map(validator => ({ + validator: (_rule: RuleObject, value: unknown) => { + const errorMessage = validator(value); + if (errorMessage !== false && errorMessage !== true) { + return Promise.reject(new Error(errorMessage)); + } + return Promise.resolve(); + }, + })); + return v; + } + return undefined; +}; + export default function getControlItemsMap({ datasetId, disabled, @@ -149,6 +171,7 @@ export default function getControlItemsMap({ (controlItem: CustomControlItem) => controlItem?.config?.renderTrigger && controlItem.name !== 'sortAscending' && + controlItem?.config?.type === 'CheckboxControl' && controlItem.name !== 'enableSingleValue', ) .forEach(controlItem => { @@ -214,6 +237,68 @@ export default function getControlItemsMap({ ); mapControlItems[controlItem.name] = { element, checked: initialValue }; }); + controlItems + .filter( + (controlItem: CustomControlItem) => + controlItem?.config?.renderTrigger && + controlItem?.config?.type === 'SelectControl', + ) + .forEach(controlItem => { + const initialValue = + filterToEdit?.controlValues?.[controlItem.name] ?? + controlItem?.config?.default; + const element = ( + <> +