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 13b115bec..68f3ad5f2 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 aa83b8bbb..32d020457 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 097d80b8d..fd220e458 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 8b01d0faf..23fd0f35d 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}}
>