mirror of
https://github.com/apache/superset.git
synced 2024-09-12 00:29:39 -04:00
feat(native-filters): Add default first value to select filter (#13726)
* feat: native filters first default value
* fix: fix CR notes
* feat: add support for `Place` type
* refactor: sync with master
* feat: add first value to Select filters
* refactor: fix CR notes
* refactor: updates usage of `ownFilters` to `ownState`
* Revert "refactor: updates usage of `ownFilters` to `ownState`"
This reverts commit 58b91998
* fix: revert mocks
* fix: fix CR notes
* chore: update package json
* chore: update package json
* chore: update package json
* test: fix tests
* fix: fix BE empty metrics
* lint: fix lint
* fix: fix BE empty metrics
* refactor: fix Cr notes
This commit is contained in:
parent
1c6173c7ee
commit
468638c5b8
@ -17,7 +17,12 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React, { FC, useEffect, useState } from 'react';
|
||||
import { Behavior, SetDataMaskHook, SuperChart } from '@superset-ui/core';
|
||||
import {
|
||||
Behavior,
|
||||
SetDataMaskHook,
|
||||
SuperChart,
|
||||
AppSection,
|
||||
} from '@superset-ui/core';
|
||||
import { FormInstance } from 'antd/lib/form';
|
||||
import Loading from 'src/components/Loading';
|
||||
import { NativeFiltersForm } from '../types';
|
||||
@ -56,6 +61,7 @@ const DefaultValue: FC<DefaultValueProps> = ({
|
||||
<SuperChart
|
||||
height={25}
|
||||
width={250}
|
||||
appSection={AppSection.FILTER_CONFIG_MODAL}
|
||||
behaviors={[Behavior.NATIVE_FILTER]}
|
||||
formData={formData}
|
||||
// For charts that don't have datasource we need workaround for empty placeholder
|
||||
|
@ -17,6 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
AppSection,
|
||||
Behavior,
|
||||
DataMask,
|
||||
ensureIsArray,
|
||||
@ -27,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';
|
||||
|
||||
@ -42,6 +43,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
|
||||
width,
|
||||
behaviors,
|
||||
setDataMask,
|
||||
appSection,
|
||||
} = props;
|
||||
const {
|
||||
defaultValue,
|
||||
@ -51,12 +53,28 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
|
||||
currentValue,
|
||||
inverseSelection,
|
||||
inputRef,
|
||||
defaultToFirstItem,
|
||||
} = formData;
|
||||
|
||||
const [values, setValues] = useState<(string | number | boolean)[]>(
|
||||
defaultValue ?? [],
|
||||
);
|
||||
const forceFirstValue =
|
||||
appSection === AppSection.FILTER_CONFIG_MODAL && defaultToFirstItem;
|
||||
|
||||
const groupby = ensureIsArray<string>(formData.groupby);
|
||||
// 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<SelectValue>(
|
||||
defaultToFirstItem && appSection !== AppSection.FILTER_CONFIG_MODAL
|
||||
? firstItem
|
||||
: initSelectValue,
|
||||
);
|
||||
|
||||
const [col] = groupby;
|
||||
const datatype: GenericDataType = coltypeMap[col];
|
||||
@ -64,26 +82,36 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
|
||||
timeFormatter: smartDateDetailedFormatter,
|
||||
});
|
||||
|
||||
const handleChange = (
|
||||
value?: (number | string)[] | number | string | null,
|
||||
) => {
|
||||
const resultValue: (number | string)[] = ensureIsArray<number | string>(
|
||||
const handleChange = (value?: SelectValue | number | string) => {
|
||||
let selectValue: (number | string)[] = ensureIsArray<number | string>(
|
||||
value,
|
||||
);
|
||||
setValues(resultValue);
|
||||
let stateValue: SelectValue | typeof FIRST_VALUE = selectValue.length
|
||||
? selectValue
|
||||
: null;
|
||||
|
||||
if (value === FIRST_VALUE) {
|
||||
selectValue = forceFirstValue ? [] : firstItem;
|
||||
stateValue = FIRST_VALUE;
|
||||
}
|
||||
|
||||
setValues(selectValue);
|
||||
|
||||
const emptyFilter =
|
||||
enableEmptyFilter && !inverseSelection && resultValue?.length === 0;
|
||||
enableEmptyFilter && !inverseSelection && selectValue?.length === 0;
|
||||
|
||||
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: stateValue,
|
||||
},
|
||||
};
|
||||
|
||||
@ -100,18 +128,23 @@ 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(forceFirstValue ? FIRST_VALUE : currentValue ?? []);
|
||||
}, [
|
||||
JSON.stringify(currentValue),
|
||||
defaultToFirstItem,
|
||||
multiSelect,
|
||||
enableEmptyFilter,
|
||||
inverseSelection,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
handleChange(defaultValue ?? []);
|
||||
// If we have `defaultToFirstItem` mode it means that default value always `FIRST_VALUE`
|
||||
handleChange(defaultToFirstItem ? FIRST_VALUE : defaultValue);
|
||||
}, [
|
||||
JSON.stringify(defaultValue),
|
||||
JSON.stringify(firstItem),
|
||||
defaultToFirstItem,
|
||||
multiSelect,
|
||||
enableEmptyFilter,
|
||||
inverseSelection,
|
||||
@ -127,6 +160,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
|
||||
allowClear
|
||||
// @ts-ignore
|
||||
value={values}
|
||||
disabled={forceFirstValue}
|
||||
showSearch={showSearch}
|
||||
mode={multiSelect ? 'multiple' : undefined}
|
||||
placeholder={placeholderText}
|
||||
|
@ -24,6 +24,7 @@ const {
|
||||
enableEmptyFilter,
|
||||
inverseSelection,
|
||||
multiSelect,
|
||||
defaultToFirstItem,
|
||||
sortAscending,
|
||||
} = DEFAULT_FORM_DATA;
|
||||
|
||||
@ -79,6 +80,19 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'defaultToFirstItem',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Default to first item'),
|
||||
default: defaultToFirstItem,
|
||||
resetConfig: true,
|
||||
renderTrigger: true,
|
||||
description: t('Select first item by default'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'inverseSelection',
|
||||
|
@ -22,7 +22,15 @@ import { DEFAULT_FORM_DATA, PluginFilterSelectChartProps } from './types';
|
||||
export default function transformProps(
|
||||
chartProps: PluginFilterSelectChartProps,
|
||||
) {
|
||||
const { formData, height, hooks, queriesData, width, behaviors } = chartProps;
|
||||
const {
|
||||
formData,
|
||||
height,
|
||||
hooks,
|
||||
queriesData,
|
||||
width,
|
||||
behaviors,
|
||||
appSection,
|
||||
} = chartProps;
|
||||
const newFormData = { ...DEFAULT_FORM_DATA, ...formData };
|
||||
const { setDataMask = () => {} } = hooks;
|
||||
const [queryData] = queriesData;
|
||||
@ -34,6 +42,7 @@ export default function transformProps(
|
||||
|
||||
return {
|
||||
coltypeMap,
|
||||
appSection,
|
||||
width,
|
||||
behaviors,
|
||||
height,
|
||||
|
@ -17,6 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
AppSection,
|
||||
ChartProps,
|
||||
Behavior,
|
||||
DataRecord,
|
||||
@ -28,12 +29,16 @@ 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;
|
||||
defaultToFirstItem: boolean;
|
||||
inputRef?: RefObject<HTMLInputElement>;
|
||||
sortAscending: boolean;
|
||||
}
|
||||
@ -51,6 +56,7 @@ export type PluginFilterSelectProps = PluginFilterStylesProps & {
|
||||
data: DataRecord[];
|
||||
setDataMask: SetDataMaskHook;
|
||||
behaviors: Behavior[];
|
||||
appSection: AppSection;
|
||||
formData: PluginFilterSelectQueryFormData;
|
||||
};
|
||||
|
||||
@ -59,6 +65,7 @@ export const DEFAULT_FORM_DATA: PluginFilterSelectCustomizeProps = {
|
||||
currentValue: null,
|
||||
enableEmptyFilter: false,
|
||||
inverseSelection: false,
|
||||
defaultToFirstItem: false,
|
||||
multiSelect: true,
|
||||
sortAscending: true,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user