feat(native-filters): enable filter indicator and make datasource optional (#13148)

* refactor: continue decouple native filters

* refactor(native-filters): decouple native filter parms from config modal

* lint: fix TS errors

* lint: fix TS errors

* refactor: update filter badge

* refactor: rename ant filters

* test: fix tests

* test: fix tests

* refactor: remove 18 from dataset
This commit is contained in:
simcha90 2021-02-16 16:12:35 +02:00 committed by GitHub
parent 5ab613d89e
commit 5aa38ef929
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 292 additions and 288 deletions

View File

@ -19,7 +19,16 @@
export default {
id: 1234,
slug: 'dashboardSlug',
metadata: {},
metadata: {
filter_configuration: [
{
id: 'DefaultsID',
filterType: 'filter_select',
targets: [{}],
cascadeParentIds: [],
},
],
},
userId: 'mock_user_id',
dash_edit_perm: true,
dash_save_perm: true,

View File

@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
import { FilterType } from 'src/dashboard/components/nativeFilters/types';
import { NativeFiltersState } from 'src/dashboard/reducers/types';
export const nativeFilters: NativeFiltersState = {
@ -24,7 +23,7 @@ export const nativeFilters: NativeFiltersState = {
'NATIVE_FILTER-e7Q8zKixx': {
id: 'NATIVE_FILTER-e7Q8zKixx',
name: 'region',
filterType: FilterType.filter_select,
filterType: 'filter_select',
targets: [
{
datasetId: 2,
@ -49,7 +48,7 @@ export const nativeFilters: NativeFiltersState = {
'NATIVE_FILTER-x9QPw0so1': {
id: 'NATIVE_FILTER-x9QPw0so1',
name: 'country_code',
filterType: FilterType.filter_select,
filterType: 'filter_select',
targets: [
{
datasetId: 2,
@ -75,6 +74,9 @@ export const nativeFilters: NativeFiltersState = {
filtersState: {
'NATIVE_FILTER-e7Q8zKixx': {
id: 'NATIVE_FILTER-e7Q8zKixx',
currentState: {
value: ['East Asia & Pacific'],
},
extraFormData: {
append_form_data: {
filters: [
@ -128,6 +130,9 @@ export const singleNativeFiltersState = {
[NATIVE_FILTER_ID]: {
id: NATIVE_FILTER_ID,
extraFormData,
currentState: {
value: ['No, not an ethnic minority'],
},
},
},
};

View File

@ -40,10 +40,25 @@ Object.defineProperty(window, 'matchMedia', {
})),
});
jest.mock('@superset-ui/core', () => ({
// @ts-ignore
...jest.requireActual('@superset-ui/core'),
getChartMetadataRegistry: () => ({
items: {
filter_select: {
value: {
datasourceCount: 1,
behaviors: ['NATIVE_FILTER'],
},
},
},
}),
}));
describe('FiltersConfigModal', () => {
const mockedProps = {
isOpen: true,
initialFilterId: 'DefaultFilterId',
initialFilterId: 'DefaultsID',
createNewOnOpen: true,
onCancel: jest.fn(),
save: jest.fn(),

View File

@ -18,10 +18,10 @@
*/
export const nativeFiltersInfo = {
filters: {
DefaultID1: {
id: 'DefaultID1',
DefaultsID: {
id: 'DefaultsID',
name: 'test',
type: 'text',
type: 'filter_select',
targets: [
{
datasetId: 0,

View File

@ -18,10 +18,9 @@
*/
import { TIME_FILTER_MAP } from 'src/explore/constants';
import { getChartIdsInFilterScope } from 'src/dashboard/util/activeDashboardFilters';
import {
NativeFiltersState,
NativeFilterState,
} from 'src/dashboard/reducers/types';
import { NativeFiltersState } from 'src/dashboard/reducers/types';
import { getTreeCheckedItems } from '../nativeFilters/FilterConfigModal/utils';
import { Layout } from '../../types';
export enum IndicatorStatus {
Unset = 'UNSET',
@ -130,7 +129,7 @@ const getRejectedColumns = (chart: any): Set<string> =>
);
export type Indicator = {
column: string;
column?: string;
name: string;
value: string[];
status: IndicatorStatus;
@ -172,50 +171,52 @@ export const selectIndicatorsForChart = (
return indicators;
};
const selectNativeIndicatorValue = (
filterState: NativeFilterState,
): string[] => {
const filters = filterState?.extraFormData?.append_form_data?.filters;
if (filters?.length) {
const filter = filters[0];
if ('val' in filter) {
const val = filter.val as string | string[];
if (Array.isArray(val)) {
return val;
}
return [val];
}
}
return [];
};
export const selectNativeIndicatorsForChart = (
nativeFilters: NativeFiltersState,
chartId: number,
charts: any,
dashboardLayout: Layout,
): Indicator[] => {
const chart = charts[chartId];
const appliedColumns = getAppliedColumns(chart);
const rejectedColumns = getRejectedColumns(chart);
const getStatus = (column: string, value: string[]): IndicatorStatus => {
if (rejectedColumns.has(column)) return IndicatorStatus.Incompatible;
if (appliedColumns.has(column) && value.length > 0) {
const getStatus = (
value: string[],
isAffectedByScope: boolean,
column?: string,
): IndicatorStatus => {
if (!column && isAffectedByScope) {
// Filter without datasource
return IndicatorStatus.Applied;
}
if (column && rejectedColumns.has(column))
return IndicatorStatus.Incompatible;
if (column && appliedColumns.has(column) && value.length > 0) {
return IndicatorStatus.Applied;
}
return IndicatorStatus.Unset;
};
const indicators = Object.values(nativeFilters.filters).map(nativeFilter => {
const column = nativeFilter.targets[0].column.name;
const isAffectedByScope = getTreeCheckedItems(
nativeFilter.scope,
dashboardLayout,
).some(
layoutItem => dashboardLayout[layoutItem]?.meta?.chartId === chartId,
);
const column = nativeFilter.targets[0]?.column?.name;
const filterState = nativeFilters.filtersState[nativeFilter.id];
const value = selectNativeIndicatorValue(filterState);
let value = filterState?.currentState?.value ?? [];
if (!Array.isArray(value)) {
value = [value];
}
return {
column,
name: nativeFilter.name,
path: [nativeFilter.id],
status: getStatus(column, value),
status: getStatus(value, isAffectedByScope, column),
value,
};
});

View File

@ -51,15 +51,19 @@ const FilterValue: React.FC<FilterProps> = ({
const { id, targets, filterType } = filter;
const cascadingFilters = useCascadingFilters(id);
const filterState = useFilterState(id);
const [loading, setLoading] = useState<boolean>(true);
const [state, setState] = useState([]);
const [error, setError] = useState<boolean>(false);
const [formData, setFormData] = useState<Partial<QueryFormData>>({});
const inputRef = useRef<HTMLInputElement>(null);
const [target] = targets;
const { datasetId = 18, column } = target;
const {
datasetId,
column = {},
}: Partial<{ datasetId: number; column: { name?: string } }> = target;
const { name: groupby } = column;
const currentValue = filterState.currentState?.value;
const hasDataSource = !!(datasetId && groupby);
const [loading, setLoading] = useState<boolean>(hasDataSource);
useEffect(() => {
const newFormData = getFormData({
datasetId,
@ -71,6 +75,9 @@ const FilterValue: React.FC<FilterProps> = ({
});
if (!areObjectsEqual(formData || {}, newFormData)) {
setFormData(newFormData);
if (!hasDataSource) {
return;
}
getChartDataRequest({
formData: newFormData,
force: false,
@ -131,7 +138,8 @@ const FilterValue: React.FC<FilterProps> = ({
height={20}
width={220}
formData={formData}
queriesData={state}
// For charts that don't have datasource we need workaround for empty placeholder
queriesData={hasDataSource ? state : [{ data: [null] }]}
chartType={filterType}
// @ts-ignore (update superset-ui)
hooks={{ setExtraFormData }}

View File

@ -21,6 +21,8 @@ import {
SuperChart,
t,
getChartControlPanelRegistry,
getChartMetadataRegistry,
Behavior,
} from '@superset-ui/core';
import { FormInstance } from 'antd/lib/form';
import React, { useCallback } from 'react';
@ -39,15 +41,10 @@ import { CustomControlItem } from '@superset-ui/chart-controls';
import { ColumnSelect } from './ColumnSelect';
import { NativeFiltersForm } from './types';
import FilterScope from './FilterScope';
import {
FilterTypeNames,
getControlItems,
setFilterFieldValues,
useForceUpdate,
} from './utils';
import { getControlItems, setFilterFieldValues, useForceUpdate } from './utils';
import { useBackendFormUpdate } from './state';
import { getFormData } from '../utils';
import { Filter, FilterType } from '../types';
import { Filter } from '../types';
type DatasetSelectValue = {
value: number;
@ -121,9 +118,25 @@ export const FilterConfigForm: React.FC<FilterConfigFormProps> = ({
const controlItems = getControlItems(
controlPanelRegistry.get(formFilter?.filterType),
);
useBackendFormUpdate(form, filterId, filterToEdit);
const initDatasetId = filterToEdit?.targets[0].datasetId;
const nativeFilterItems = getChartMetadataRegistry().items;
const nativeFilterVizTypes = Object.entries(nativeFilterItems)
// @ts-ignore
.filter(([, { value }]) =>
value.behaviors?.includes(Behavior.NATIVE_FILTER),
)
.map(([key]) => key);
// @ts-ignore
const hasDatasource = !!nativeFilterItems[formFilter?.filterType]?.value
?.datasourceCount;
const hasFilledDatasource =
(formFilter?.dataset && formFilter?.column) || !hasDatasource;
useBackendFormUpdate(form, filterId, filterToEdit, hasDatasource);
const initDatasetId = filterToEdit?.targets[0]?.datasetId;
const initColumn = filterToEdit?.targets[0]?.column?.name;
const newFormData = getFormData({
datasetId: formFilter?.dataset?.value,
@ -179,69 +192,76 @@ export const FilterConfigForm: React.FC<FilterConfigFormProps> = ({
<Input />
</StyledFormItem>
<StyledFormItem
name={['filters', filterId, 'dataset']}
initialValue={{ value: initDatasetId }}
label={<StyledLabel>{t('Datasource')}</StyledLabel>}
rules={[{ required: !removed, message: t('Datasource is required') }]}
data-test="datasource-input"
name={['filters', filterId, 'filterType']}
rules={[{ required: !removed, message: t('Name is required') }]}
initialValue={filterToEdit?.filterType || 'filter_select'}
label={<StyledLabel>{t('Filter Type')}</StyledLabel>}
>
<SupersetResourceSelect
initialId={initDatasetId}
resource="dataset"
searchColumn="table_name"
transformItem={datasetToSelectOption}
isMulti={false}
onError={onDatasetSelectError}
onChange={e => {
// We need reset column when dataset changed
const datasetId = formFilter?.dataset?.value;
if (datasetId && e?.value !== datasetId) {
setFilterFieldValues(form, filterId, {
column: null,
});
}
<Select
options={nativeFilterVizTypes.map(filterType => ({
value: filterType,
// @ts-ignore
label: nativeFilterItems[filterType]?.value.name,
}))}
onChange={({ value }: { value: string }) => {
setFilterFieldValues(form, filterId, {
filterType: value,
defaultValue: null,
});
forceUpdate();
}}
/>
</StyledFormItem>
</StyledContainer>
<StyledFormItem
// don't show the column select unless we have a dataset
// style={{ display: datasetId == null ? undefined : 'none' }}
name={['filters', filterId, 'column']}
initialValue={initColumn}
label={<StyledLabel>{t('Field')}</StyledLabel>}
rules={[{ required: !removed, message: t('Field is required') }]}
data-test="field-input"
>
<ColumnSelect
form={form}
filterId={filterId}
datasetId={formFilter?.dataset?.value}
onChange={forceUpdate}
/>
</StyledFormItem>
<StyledFormItem
name={['filters', filterId, 'filterType']}
rules={[{ required: !removed, message: t('Name is required') }]}
initialValue={filterToEdit?.filterType || FilterType.filter_select}
label={<StyledLabel>{t('Filter Type')}</StyledLabel>}
>
<Select
options={Object.values(FilterType).map(filterType => ({
value: filterType,
label: FilterTypeNames[filterType],
}))}
onChange={({ value }: { value: FilterType }) => {
setFilterFieldValues(form, filterId, {
filterType: value,
defaultValue: null,
});
forceUpdate();
}}
/>
</StyledFormItem>
{formFilter?.dataset && formFilter?.column && (
{hasDatasource && (
<>
<StyledFormItem
name={['filters', filterId, 'dataset']}
initialValue={{ value: initDatasetId }}
label={<StyledLabel>{t('Datasource')}</StyledLabel>}
rules={[
{ required: !removed, message: t('Datasource is required') },
]}
data-test="datasource-input"
>
<SupersetResourceSelect
initialId={initDatasetId}
resource="dataset"
searchColumn="table_name"
transformItem={datasetToSelectOption}
isMulti={false}
onError={onDatasetSelectError}
onChange={e => {
// We need reset column when dataset changed
const datasetId = formFilter?.dataset?.value;
if (datasetId && e?.value !== datasetId) {
setFilterFieldValues(form, filterId, {
column: null,
});
}
forceUpdate();
}}
/>
</StyledFormItem>
<StyledFormItem
// don't show the column select unless we have a dataset
// style={{ display: datasetId == null ? undefined : 'none' }}
name={['filters', filterId, 'column']}
initialValue={initColumn}
label={<StyledLabel>{t('Field')}</StyledLabel>}
rules={[{ required: !removed, message: t('Field is required') }]}
data-test="field-input"
>
<ColumnSelect
form={form}
filterId={filterId}
datasetId={formFilter?.dataset?.value}
onChange={forceUpdate}
/>
</StyledFormItem>
</>
)}
{hasFilledDatasource && (
<CleanFormItem
name={['filters', filterId, 'defaultValueFormData']}
hidden
@ -259,25 +279,29 @@ export const FilterConfigForm: React.FC<FilterConfigFormProps> = ({
data-test="default-input"
label={<StyledLabel>{t('Default Value')}</StyledLabel>}
>
{formFilter?.dataset &&
formFilter?.column &&
formFilter?.defaultValueQueriesData && (
<SuperChart
height={25}
width={250}
formData={newFormData}
queriesData={formFilter?.defaultValueQueriesData}
chartType={formFilter?.filterType}
hooks={{
setExtraFormData: ({ currentState }) => {
setFilterFieldValues(form, filterId, {
defaultValue: currentState?.value,
});
forceUpdate();
},
}}
/>
)}
{((hasFilledDatasource && formFilter?.defaultValueQueriesData) ||
!hasDatasource) && (
<SuperChart
height={25}
width={250}
formData={newFormData}
// For charts that don't have datasource we need workaround for empty placeholder
queriesData={
hasDatasource
? formFilter?.defaultValueQueriesData
: [{ data: [null] }]
}
chartType={formFilter?.filterType}
hooks={{
setExtraFormData: ({ currentState }) => {
setFilterFieldValues(form, filterId, {
defaultValue: currentState?.value,
});
forceUpdate();
},
}}
/>
)}
</StyledFormItem>
<StyledFormItem
name={['filters', filterId, 'parentFilter']}

View File

@ -400,20 +400,22 @@ export function FilterConfigModal({
const formInputs = values.filters[id];
// if user didn't open a filter, return the original config
if (!formInputs) return filterConfigMap[id];
let target = {};
if (formInputs.dataset && formInputs.column) {
target = {
datasetId: formInputs.dataset.value,
column: {
name: formInputs.column,
},
};
}
return {
id,
controlValues: formInputs.controlValues,
name: formInputs.name,
filterType: formInputs.filterType,
// for now there will only ever be one target
targets: [
{
datasetId: formInputs.dataset.value,
column: {
name: formInputs.column,
},
},
],
targets: [target],
defaultValue: formInputs.defaultValue || null,
cascadeParentIds: formInputs.parentFilter
? [formInputs.parentFilter.value]

View File

@ -1,67 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { styled } from '@superset-ui/core';
import { Button } from 'src/common/components';
import Icon from 'src/components/Icon';
import { useFilterConfiguration } from '../state';
interface Args {
filter: any;
index: number;
}
interface FiltersListProps {
setEditFilter: (arg0: Args) => void;
setDataset: (arg0: any) => void;
}
const FiltersStyle = styled.div`
display: flex;
flex-direction: row;
`;
const FiltersList = ({ setEditFilter, setDataset }: FiltersListProps) => {
const filterConfigs = useFilterConfiguration();
<>
{filterConfigs.map((filter, i: number) => (
<FiltersStyle>
<Button
type="link"
key={filter.name}
onClick={() => {
setEditFilter({ filter, index: i });
setDataset(filter.targets[0].datasetId);
}}
>
{filter.name}
</Button>
<span
role="button"
title="Edit dashboard"
tabIndex={0}
className="action-button"
>
<Icon name="trash" />
</span>
</FiltersStyle>
))}
</>;
};
export default FiltersList;

View File

@ -79,11 +79,16 @@ export const useBackendFormUpdate = (
form: FormInstance<NativeFiltersForm>,
filterId: string,
filterToEdit?: Filter,
hasDatasource?: boolean,
) => {
const forceUpdate = useForceUpdate();
const formFilter = (form.getFieldValue('filters') || {})[filterId];
useEffect(() => {
let resolvedDefaultValue: any = null;
if (!hasDatasource) {
forceUpdate();
return;
}
// No need to check data set change because it cascading update column
// So check that column exists is enough
if (!formFilter?.column) {
@ -107,7 +112,7 @@ export const useBackendFormUpdate = (
if (
filterToEdit?.filterType === formFilter?.filterType &&
filterToEdit?.targets[0].datasetId === formFilter?.dataset?.value &&
formFilter?.column === filterToEdit?.targets[0]?.column?.name
formFilter?.column === filterToEdit?.targets[0].column?.name
) {
resolvedDefaultValue = filterToEdit?.defaultValue;
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
import { QueryObjectFilterClause } from '@superset-ui/core';
import { Column, FilterType, Scope } from '../types';
import { Column, Scope } from '../types';
export enum Scoping {
all,
@ -30,7 +30,7 @@ export type AntCallback = (value1?: any, value2?: any) => void;
export interface NativeFiltersFormItem {
scope: Scope;
name: string;
filterType: FilterType;
filterType: string;
dataset: {
value: number;
label: string;

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { t } from '@superset-ui/core';
import { flatMapDeep } from 'lodash';
import { Charts, Layout, LayoutItem } from 'src/dashboard/types';
import {
@ -29,7 +29,7 @@ import React from 'react';
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
import { CustomControlItem } from '@superset-ui/chart-controls';
import { TreeItem } from './types';
import { FilterType, Scope } from '../types';
import { Scope } from '../types';
export const useForceUpdate = () => {
const [, updateState] = React.useState({});
@ -155,12 +155,6 @@ export const findFilterScope = (
};
};
export const FilterTypeNames = {
[FilterType.filter_select]: t('Select'),
[FilterType.filter_range]: t('Range'),
[FilterType.filter_time]: t('Time'),
};
export const setFilterFieldValues = (
form: FormInstance,
filterId: string,

View File

@ -27,12 +27,6 @@ export interface Scope {
excluded: number[];
}
export enum FilterType {
filter_select = 'filter_select',
filter_range = 'filter_range',
filter_time = 'filter_time',
}
/** The target of a filter is the datasource/column being filtered */
export interface Target {
datasetId: number;
@ -51,10 +45,10 @@ export interface Filter {
id: string; // randomly generated at filter creation
name: string;
scope: Scope;
filterType: FilterType;
filterType: string;
// for now there will only ever be one target
// when multiple targets are supported, change this to Target[]
targets: [Target];
targets: [Partial<Target>];
controlValues: {
[key: string]: any;
};

View File

@ -41,26 +41,34 @@ export const getFormData = ({
datasetId?: number;
inputRef?: RefObject<HTMLInputElement>;
cascadingFilters?: object;
groupby: string;
}): Partial<QueryFormData> => ({
adhoc_filters: [],
datasource: `${datasetId}__table`,
extra_filters: [],
extra_form_data: cascadingFilters,
granularity_sqla: 'ds',
groupby: [groupby],
metrics: ['count'],
row_limit: 10000,
showSearch: true,
currentValue,
defaultValue,
time_range: 'No filter',
time_range_endpoints: ['inclusive', 'exclusive'],
url_params: {},
viz_type: 'filter_select',
inputRef,
...controlValues,
});
groupby?: string;
}): Partial<QueryFormData> => {
let otherProps: { datasource?: string; groupby?: string[] } = {};
if (datasetId && groupby) {
otherProps = {
datasource: `${datasetId}__table`,
groupby: [groupby],
};
}
return {
adhoc_filters: [],
extra_filters: [],
extra_form_data: cascadingFilters,
granularity_sqla: 'ds',
metrics: ['count'],
row_limit: 10000,
showSearch: true,
currentValue,
defaultValue,
time_range: 'No filter',
time_range_endpoints: ['inclusive', 'exclusive'],
url_params: {},
viz_type: 'filter_select',
inputRef,
...controlValues,
...otherProps,
};
};
export function mergeExtraFormData(
originalExtra: ExtraFormData,

View File

@ -52,7 +52,13 @@ const sortByStatus = (indicators: Indicator[]): Indicator[] => {
};
const mapStateToProps = (
{ datasources, dashboardFilters, nativeFilters, charts }: any,
{
datasources,
dashboardFilters,
nativeFilters,
charts,
dashboardLayout: { present },
}: any,
{ chartId }: FiltersBadgeProps,
) => {
const dashboardIndicators = selectIndicatorsForChart(
@ -66,6 +72,7 @@ const mapStateToProps = (
nativeFilters,
chartId,
charts,
present,
);
const indicators = uniqWith(

View File

@ -19,16 +19,16 @@
import { styled } from '@superset-ui/core';
import React from 'react';
import { Slider } from 'src/common/components';
import { AntdPluginFilterRangeProps } from './types';
import { AntdPluginFilterStylesProps } from '../types';
import { PluginFilterRangeProps } from './types';
import { PluginFilterStylesProps } from '../types';
import { getRangeExtraFormData } from '../../utils';
const Styles = styled.div<AntdPluginFilterStylesProps>`
const Styles = styled.div<PluginFilterStylesProps>`
height: ${({ height }) => height};
width: ${({ width }) => width};
`;
export default function AntdRangeFilter(props: AntdPluginFilterRangeProps) {
export default function RangeFilterPlugin(props: PluginFilterRangeProps) {
const { data, formData, height, width, setExtraFormData, inputRef } = props;
const [row] = data;
// @ts-ignore

View File

@ -22,10 +22,10 @@ import controlPanel from './controlPanel';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
export default class AntdRangeFilterPlugin extends ChartPlugin {
export default class RangeFilterPlugin extends ChartPlugin {
constructor() {
const metadata = new ChartMetadata({
name: t('Range filter plugin'),
name: t('Range filter'),
description: 'Range filter plugin using AntD',
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
thumbnail,
@ -34,7 +34,7 @@ export default class AntdRangeFilterPlugin extends ChartPlugin {
super({
buildQuery,
controlPanel,
loadChart: () => import('./AntdRangeFilter'),
loadChart: () => import('./RangeFilterPlugin'),
metadata,
transformProps,
});

View File

@ -22,18 +22,18 @@ import {
SetExtraFormDataHook,
} from '@superset-ui/core';
import { RefObject } from 'react';
import { AntdPluginFilterStylesProps } from '../types';
import { PluginFilterStylesProps } from '../types';
interface AntdPluginFilterSelectCustomizeProps {
interface PluginFilterSelectCustomizeProps {
max?: number;
min?: number;
}
export type PluginFilterRangeQueryFormData = QueryFormData &
AntdPluginFilterStylesProps &
AntdPluginFilterSelectCustomizeProps;
PluginFilterStylesProps &
PluginFilterSelectCustomizeProps;
export type AntdPluginFilterRangeProps = AntdPluginFilterStylesProps & {
export type PluginFilterRangeProps = PluginFilterStylesProps & {
data: DataRecord[];
formData: PluginFilterRangeQueryFormData;
setExtraFormData: SetExtraFormDataHook;

View File

@ -21,10 +21,10 @@ import { action } from '@storybook/addon-actions';
import { boolean, withKnobs } from '@storybook/addon-knobs';
import { SuperChart, getChartTransformPropsRegistry } from '@superset-ui/core';
import { mockQueryDataForCountries } from 'spec/fixtures/mockNativeFilters';
import AntdSelectFilterPlugin from './index';
import SelectFilterPlugin from './index';
import transformProps from './transformProps';
new AntdSelectFilterPlugin().configure({ key: 'filter_select' }).register();
new SelectFilterPlugin().configure({ key: 'filter_select' }).register();
getChartTransformPropsRegistry().registerValue('filter_select', transformProps);

View File

@ -19,20 +19,18 @@
import { styled } from '@superset-ui/core';
import React, { useEffect, useState } from 'react';
import { Select } from 'src/common/components';
import { AntdPluginFilterSelectProps } from './types';
import { AntdPluginFilterStylesProps } from '../types';
import { PluginFilterSelectProps } from './types';
import { PluginFilterStylesProps } from '../types';
import { getSelectExtraFormData } from '../../utils';
const Styles = styled.div<AntdPluginFilterStylesProps>`
const Styles = styled.div<PluginFilterStylesProps>`
height: ${({ height }) => height};
width: ${({ width }) => width};
`;
const { Option } = Select;
export default function AntdPluginFilterSelect(
props: AntdPluginFilterSelectProps,
) {
export default function PluginFilterSelect(props: PluginFilterSelectProps) {
const { data, formData, height, width, setExtraFormData } = props;
const {
defaultValue,

View File

@ -22,10 +22,10 @@ import controlPanel from './controlPanel';
import transformProps from './transformProps';
import thumbnail from './images/thumbnail.png';
export default class AntdFilterSelectPlugin extends ChartPlugin {
export default class FilterSelectPlugin extends ChartPlugin {
constructor() {
const metadata = new ChartMetadata({
name: t('Select filter plugin'),
name: t('Select filter'),
description: 'Select filter plugin using AntD',
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
thumbnail,
@ -34,7 +34,7 @@ export default class AntdFilterSelectPlugin extends ChartPlugin {
super({
buildQuery,
controlPanel,
loadChart: () => import('./AntdSelectFilter'),
loadChart: () => import('./SelectFilterPlugin'),
metadata,
transformProps,
});

View File

@ -22,9 +22,9 @@ import {
SetExtraFormDataHook,
} from '@superset-ui/core';
import { RefObject } from 'react';
import { AntdPluginFilterStylesProps } from '../types';
import { PluginFilterStylesProps } from '../types';
interface AntdPluginFilterSelectCustomizeProps {
interface PluginFilterSelectCustomizeProps {
defaultValue?: (string | number)[] | null;
currentValue?: (string | number)[] | null;
enableEmptyFilter: boolean;
@ -34,17 +34,17 @@ interface AntdPluginFilterSelectCustomizeProps {
inputRef?: RefObject<HTMLInputElement>;
}
export type AntdPluginFilterSelectQueryFormData = QueryFormData &
AntdPluginFilterStylesProps &
AntdPluginFilterSelectCustomizeProps;
export type PluginFilterSelectQueryFormData = QueryFormData &
PluginFilterStylesProps &
PluginFilterSelectCustomizeProps;
export type AntdPluginFilterSelectProps = AntdPluginFilterStylesProps & {
export type PluginFilterSelectProps = PluginFilterStylesProps & {
data: DataRecord[];
setExtraFormData: SetExtraFormDataHook;
formData: AntdPluginFilterSelectQueryFormData;
formData: PluginFilterSelectQueryFormData;
};
export const DEFAULT_FORM_DATA: AntdPluginFilterSelectCustomizeProps = {
export const DEFAULT_FORM_DATA: PluginFilterSelectCustomizeProps = {
defaultValue: null,
currentValue: null,
enableEmptyFilter: false,

View File

@ -19,18 +19,18 @@
import { styled } from '@superset-ui/core';
import React, { useState, useEffect } from 'react';
import DateFilterControl from 'src/explore/components/controls/DateFilterControl/DateFilterControl';
import { AntdPluginFilterStylesProps } from '../types';
import { AntdPluginFilterTimeProps } from './types';
import { PluginFilterStylesProps } from '../types';
import { PluginFilterTimeProps } from './types';
const DEFAULT_VALUE = 'Last week';
const Styles = styled.div<AntdPluginFilterStylesProps>`
const Styles = styled.div<PluginFilterStylesProps>`
height: ${({ height }) => height}px;
width: ${({ width }) => width}px;
overflow-x: scroll;
`;
export default function AntdTimeFilter(props: AntdPluginFilterTimeProps) {
export default function TimeFilterPlugin(props: PluginFilterTimeProps) {
const { formData, setExtraFormData, width } = props;
const { defaultValue, currentValue } = formData;

View File

@ -24,15 +24,16 @@ import thumbnail from './images/thumbnail.png';
export default class TimeFilterPlugin extends ChartPlugin {
constructor() {
const metadata = new ChartMetadata({
name: t('Time range filter plugin'),
name: t('Time filter'),
description: 'Custom time filter plugin',
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
thumbnail,
datasourceCount: 0,
});
super({
controlPanel,
loadChart: () => import('./AntdTimeFilter'),
loadChart: () => import('./TimeFilterPlugin'),
metadata,
transformProps,
});

View File

@ -21,21 +21,21 @@ import {
DataRecord,
SetExtraFormDataHook,
} from '@superset-ui/core';
import { AntdPluginFilterStylesProps } from '../types';
import { PluginFilterStylesProps } from '../types';
interface PluginFilterTimeCustomizeProps {
defaultValue?: string | null;
currentValue?: string | null;
}
export type AntdPluginFilterSelectQueryFormData = QueryFormData &
AntdPluginFilterStylesProps &
export type PluginFilterSelectQueryFormData = QueryFormData &
PluginFilterStylesProps &
PluginFilterTimeCustomizeProps;
export type AntdPluginFilterTimeProps = AntdPluginFilterStylesProps & {
export type PluginFilterTimeProps = PluginFilterStylesProps & {
data: DataRecord[];
setExtraFormData: SetExtraFormDataHook;
formData: AntdPluginFilterSelectQueryFormData;
formData: PluginFilterSelectQueryFormData;
};
export const DEFAULT_FORM_DATA: PluginFilterTimeCustomizeProps = {

View File

@ -16,6 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
export { default as AntdSelectFilterPlugin } from './Select';
export { default as AntdRangeFilterPlugin } from './Range';
export { default as SelectFilterPlugin } from './Select';
export { default as RangeFilterPlugin } from './Range';
export { default as TimeFilterPlugin } from './Time';

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
export interface AntdPluginFilterStylesProps {
export interface PluginFilterStylesProps {
height: number;
width: number;
}

View File

@ -60,8 +60,8 @@ import {
EchartsTimeseriesChartPlugin,
} from '@superset-ui/plugin-chart-echarts';
import {
AntdSelectFilterPlugin,
AntdRangeFilterPlugin,
SelectFilterPlugin,
RangeFilterPlugin,
TimeFilterPlugin,
} from 'src/filters/components/';
import FilterBoxChartPlugin from '../FilterBox/FilterBoxChartPlugin';
@ -112,8 +112,8 @@ export default class MainPreset extends Preset {
new EchartsTimeseriesChartPlugin().configure({
key: 'echarts_timeseries',
}),
new AntdSelectFilterPlugin().configure({ key: 'filter_select' }),
new AntdRangeFilterPlugin().configure({ key: 'filter_range' }),
new SelectFilterPlugin().configure({ key: 'filter_select' }),
new RangeFilterPlugin().configure({ key: 'filter_range' }),
new TimeFilterPlugin().configure({ key: 'filter_time' }),
],
});