diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/CollapsibleControl.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/CollapsibleControl.tsx index 13b115becc..68f3ad5f29 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/CollapsibleControl.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/CollapsibleControl.tsx @@ -19,11 +19,14 @@ import React, { ReactNode, useEffect, useState } from 'react'; import { styled } from '@superset-ui/core'; import { Checkbox } from 'src/common/components'; +import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; interface CollapsibleControlProps { initialValue?: boolean; + disabled?: boolean; checked?: boolean; title: string; + tooltip?: string; children: ReactNode; onChange?: (checked: boolean) => void; } @@ -48,7 +51,9 @@ const StyledContainer = styled.div<{ checked: boolean }>` const CollapsibleControl = (props: CollapsibleControlProps) => { const { checked, + disabled, title, + tooltip, children, onChange = () => {}, initialValue = false, @@ -68,6 +73,7 @@ const CollapsibleControl = (props: CollapsibleControlProps) => { { const value = e.target.checked; // external `checked` value has more priority then local state @@ -78,7 +84,12 @@ const CollapsibleControl = (props: CollapsibleControlProps) => { onChange(value); }} > - {title} + <> + {title}  + {tooltip && ( + + )} + {isChecked && children} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx index aa83b8bbb8..32d0204578 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx @@ -129,6 +129,20 @@ export const StyledRowFormItem = styled(FormItem)` } `; +export const StyledRowSubFormItem = styled(FormItem)` + & .ant-form-item-label { + padding-bottom: 0; + } + + .ant-form-item-control-input-content > div > div { + height: auto; + } + + & .ant-form-item-control-input { + height: auto; + } +`; + export const StyledLabel = styled.span` color: ${({ theme }) => theme.colors.grayscale.base}; font-size: ${({ theme }) => theme.typography.sizes.s}px; @@ -454,10 +468,12 @@ const FiltersConfigForm = ( ...formFilter, }); - const [hasDefaultValue, setHasDefaultValue] = useDefaultValue( - formFilter, - filterToEdit, - ); + const [ + hasDefaultValue, + isRequired, + defaultValueTooltip, + setHasDefaultValue, + ] = useDefaultValue(formFilter, filterToEdit); useEffect(() => { if (hasDataset && hasFilledDataset && hasDefaultValue && isDataDirty) { @@ -540,6 +556,8 @@ const FiltersConfigForm = ( const hasAdhoc = formFilter?.adhoc_filters?.length > 0; + const defaultToFirstItem = formFilter?.controlValues?.defaultToFirstItem; + const preFilterValidator = () => { if (hasTimeRange || hasAdhoc) { return Promise.resolve(); @@ -713,26 +731,22 @@ const FiltersConfigForm = ( setHasDefaultValue(value)} > - {t('Default Value')}} - required={formFilter?.controlValues?.enableEmptyFilter} + required={hasDefaultValue} rules={[ { validator: (rule, value) => { const hasValue = !!value?.filterState?.value; - if ( - hasValue || - // TODO: do more generic - formFilter.controlValues?.defaultToFirstItem || - // Not marked as required - !formFilter.controlValues?.enableEmptyFilter - ) { + if (hasValue) { return Promise.resolve(); } return Promise.reject( @@ -775,7 +789,7 @@ const FiltersConfigForm = ( ) : ( t('Fill all required fields to enable "Default Value"') )} - + {Object.keys(controlItems) .filter(key => BASIC_CONTROL_ITEMS.includes(key)) @@ -814,7 +828,7 @@ const FiltersConfigForm = ( } }} > - {t('Parent filter')}} initialValue={parentFilter} @@ -832,7 +846,7 @@ const FiltersConfigForm = ( options={parentFilterOptions} isClearable /> - + )} {Object.keys(controlItems) @@ -848,7 +862,7 @@ const FiltersConfigForm = ( } }} > - } /> - + {showTimeRangePicker && ( {hasMetrics && ( - - + )} )} 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 097d80b8d8..fd220e458e 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx @@ -116,7 +116,7 @@ export default function getControlItemsMap({ forceUpdate(); }} > - {controlItem.config.label}{' '} + {controlItem.config.label}  {controlItem.config.description && ( { - setHasPartialDefaultValue( - value || formFilter?.controlValues?.enableEmptyFilter - ? true - : undefined, - ); + const required = + !!formFilter?.controlValues?.enableEmptyFilter && !defaultToFirstItem; + setisRequired(required); + setHasPartialDefaultValue(required ? true : value); }, - [formFilter?.controlValues?.enableEmptyFilter], + [formFilter?.controlValues?.enableEmptyFilter, defaultToFirstItem], ); useEffect(() => { - setHasDefaultValue(); - }, [setHasDefaultValue]); + setHasDefaultValue( + defaultToFirstItem + ? false + : !!formFilter?.defaultDataMask?.filterState?.value, + ); + }, [setHasDefaultValue, defaultToFirstItem]); - return [hasDefaultValue, setHasDefaultValue]; + useEffect(() => { + let tooltip = ''; + if (defaultToFirstItem) { + tooltip = t( + 'Default value set automatically when "Default to first item" is checked', + ); + } else if (isRequired) { + tooltip = t('Default value must be set when "Required" is checked'); + } else if (hasDefaultValue) { + tooltip = t( + 'Default value must be set when "Filter has default value" is checked', + ); + } + setDefaultValueTooltip(tooltip); + }, [hasDefaultValue, isRequired, defaultToFirstItem]); + + return [hasDefaultValue, isRequired, defaultValueTooltip, setHasDefaultValue]; }; diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index 8b01d0faf7..23fd0f35d9 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -284,7 +284,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { extra={{filterState.validateMessage}} >