From 37b3f0e96d2de00548187234340a32f3518eeaaf Mon Sep 17 00:00:00 2001 From: Simcha Shats Date: Mon, 5 Apr 2021 10:18:51 +0300 Subject: [PATCH] feat: add first value to Select filters --- .../components/Select/SelectFilterPlugin.tsx | 72 ++++++++++++------- .../src/filters/components/Select/types.ts | 7 +- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index ae2804b494015..e992d9eeeb365 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -17,8 +17,8 @@ * under the License. */ import { - createMultiFormatter, Behavior, + createMultiFormatter, DataMask, ensureIsArray, GenericDataType, @@ -28,7 +28,7 @@ import { } from '@superset-ui/core'; import React, { useEffect, useState } from 'react'; import { Select } from 'src/common/components'; -import { PluginFilterSelectProps } from './types'; +import { FIRST_VALUE, PluginFilterSelectProps, SelectValue } from './types'; import { StyledSelect, Styles } from '../common'; import { getDataRecordFormatter, getSelectExtraFormData } from '../../utils'; @@ -70,13 +70,25 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { inputRef, defaultToFirstItem, } = formData; + + const isDefaultToFirstItemInFilterConfigModal = + place === Place.FILTER_CONFIG_MODAL && defaultToFirstItem; + const groupby = ensureIsArray(formData.groupby); - const initValues = defaultValue ?? []; - const firstItem: (string | number)[] = data[0] - ? (groupby.map(col => data[0][col]) as string[]) ?? initValues - : initValues; - const [values, setValues] = useState<(string | number | boolean)[]>( - defaultToFirstItem ? firstItem : initValues, + // Correct initial value for Ant Select + const initSelectValue: SelectValue = + // `defaultValue` can be `FIRST_VALUE` if `defaultToFirstItem` is checked, so need convert it to correct value for Select + defaultValue === FIRST_VALUE ? [] : defaultValue ?? []; + + const firstItem: SelectValue = data[0] + ? (groupby.map(col => data[0][col]) as string[]) ?? initSelectValue + : initSelectValue; + + // If we are in config modal we always need show empty select for `defaultToFirstItem` + const [values, setValues] = useState( + defaultToFirstItem && place !== Place.FILTER_CONFIG_MODAL + ? firstItem + : initSelectValue, ); const [col] = groupby; @@ -85,26 +97,33 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { timeFormatter, }); - const handleChange = ( - value?: (number | string)[] | number | string | null, - ) => { - const resultValue: (number | string)[] = ensureIsArray( - value, - ); - setValues(resultValue); + const handleChange = (value?: SelectValue | number | string) => { + let selectValue: (number | string)[]; + if (value === FIRST_VALUE) { + selectValue = isDefaultToFirstItemInFilterConfigModal ? [] : firstItem; + } else { + selectValue = ensureIsArray(value); + } + setValues(selectValue); const emptyFilter = - enableEmptyFilter && !inverseSelection && resultValue?.length === 0; + enableEmptyFilter && !inverseSelection && selectValue?.length === 0; + + // Set currentState value for a case of non-`defaultToFirstItem` + const stateValue = selectValue.length ? selectValue : null; const dataMask = { extraFormData: getSelectExtraFormData( col, - resultValue, + selectValue, emptyFilter, inverseSelection, ), currentState: { - value: resultValue.length ? resultValue : null, + // We need to save in state `FIRST_VALUE` as some const and not as REAL value, + // because when FiltersBar check if all filters initialized it compares `defaultValue` with this value + // and because REAL value can be unpredictable for users that have different data for same dashboard we use `FIRST_VALUE` + value: value === FIRST_VALUE ? FIRST_VALUE : stateValue, }, }; @@ -121,7 +140,12 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { }; useEffect(() => { - handleChange(currentValue ?? []); + // For currentValue we need set always `FIRST_VALUE` only if we in config modal for `defaultToFirstItem` mode + handleChange( + isDefaultToFirstItemInFilterConfigModal + ? FIRST_VALUE + : currentValue ?? [], + ); }, [ JSON.stringify(currentValue), defaultToFirstItem, @@ -130,15 +154,9 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { inverseSelection, ]); - const isDefaultToFirstItemInFilterConfigModal = - place === Place.FILTER_CONFIG_MODAL && defaultToFirstItem; - useEffect(() => { - let resultValue = defaultToFirstItem ? firstItem : initValues; - if (isDefaultToFirstItemInFilterConfigModal) { - resultValue = null; - } - handleChange(resultValue); + // If we have `defaultToFirstItem` mode it means that default value always `FIRST_VALUE` + handleChange(defaultToFirstItem ? FIRST_VALUE : defaultValue); }, [ JSON.stringify(defaultValue), JSON.stringify(firstItem), diff --git a/superset-frontend/src/filters/components/Select/types.ts b/superset-frontend/src/filters/components/Select/types.ts index 05113f4bff2ac..83f26144001ce 100644 --- a/superset-frontend/src/filters/components/Select/types.ts +++ b/superset-frontend/src/filters/components/Select/types.ts @@ -29,9 +29,12 @@ import { import { RefObject } from 'react'; import { PluginFilterStylesProps } from '../types'; +export const FIRST_VALUE = '__FIRST_VALUE__'; +export type SelectValue = (number | string)[] | null; + interface PluginFilterSelectCustomizeProps { - defaultValue?: (string | number)[] | null; - currentValue?: (string | number)[] | null; + defaultValue?: SelectValue | typeof FIRST_VALUE; + currentValue?: SelectValue | typeof FIRST_VALUE; enableEmptyFilter: boolean; inverseSelection: boolean; multiSelect: boolean;