diff --git a/superset-frontend/spec/javascripts/filters/utils_spec.ts b/superset-frontend/spec/javascripts/filters/utils_spec.ts new file mode 100644 index 0000000000000..453152b4152f8 --- /dev/null +++ b/superset-frontend/spec/javascripts/filters/utils_spec.ts @@ -0,0 +1,156 @@ +/** + * 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 { + getRangeExtraFormData, + getSelectExtraFormData, +} from '../../../src/filters/utils'; + +describe('Filter utils', () => { + describe('getRangeExtraFormData', () => { + it('getRangeExtraFormData - col: "testCol", lower: 1, upper: 2', () => { + expect(getRangeExtraFormData('testCol', 1, 2)).toEqual({ + append_form_data: { + filters: [ + { + col: 'testCol', + op: '>=', + val: 1, + }, + { + col: 'testCol', + op: '<=', + val: 2, + }, + ], + }, + }); + }); + it('getRangeExtraFormData - col: "testCol", lower: 0, upper: 0', () => { + expect(getRangeExtraFormData('testCol', 0, 0)).toEqual({ + append_form_data: { + filters: [ + { + col: 'testCol', + op: '>=', + val: 0, + }, + { + col: 'testCol', + op: '<=', + val: 0, + }, + ], + }, + }); + }); + it('getRangeExtraFormData - col: "testCol", lower: null, upper: 2', () => { + expect(getRangeExtraFormData('testCol', null, 2)).toEqual({ + append_form_data: { + filters: [ + { + col: 'testCol', + op: '<=', + val: 2, + }, + ], + }, + }); + }); + it('getRangeExtraFormData - col: "testCol", lower: 1, upper: undefined', () => { + expect(getRangeExtraFormData('testCol', 1, undefined)).toEqual({ + append_form_data: { + filters: [ + { + col: 'testCol', + op: '>=', + val: 1, + }, + ], + }, + }); + }); + }); + describe('getSelectExtraFormData', () => { + it('getSelectExtraFormData - col: "testCol", value: ["value"], emptyFilter: false, inverseSelection: false', () => { + expect( + getSelectExtraFormData('testCol', ['value'], false, false), + ).toEqual({ + append_form_data: { + filters: [ + { + col: 'testCol', + op: 'IN', + val: ['value'], + }, + ], + }, + }); + }); + it('getSelectExtraFormData - col: "testCol", value: ["value"], emptyFilter: true, inverseSelection: false', () => { + expect(getSelectExtraFormData('testCol', ['value'], true, false)).toEqual( + { + append_form_data: { + extras: { + where: '1 = 0', + }, + }, + }, + ); + }); + it('getSelectExtraFormData - col: "testCol", value: ["value"], emptyFilter: false, inverseSelection: true', () => { + expect(getSelectExtraFormData('testCol', ['value'], false, true)).toEqual( + { + append_form_data: { + filters: [ + { + col: 'testCol', + op: 'NOT IN', + val: ['value'], + }, + ], + }, + }, + ); + }); + it('getSelectExtraFormData - col: "testCol", value: [], emptyFilter: false, inverseSelection: false', () => { + expect(getSelectExtraFormData('testCol', [], false, false)).toEqual({ + append_form_data: { + filters: [], + }, + }); + }); + it('getSelectExtraFormData - col: "testCol", value: undefined, emptyFilter: false, inverseSelection: false', () => { + expect( + getSelectExtraFormData('testCol', undefined, false, false), + ).toEqual({ + append_form_data: { + filters: [], + }, + }); + }); + it('getSelectExtraFormData - col: "testCol", value: null, emptyFilter: false, inverseSelection: false', () => { + expect(getSelectExtraFormData('testCol', null, false, false)).toEqual({ + append_form_data: { + filters: [], + }, + }); + }); + }); +}); diff --git a/superset-frontend/src/common/components/index.tsx b/superset-frontend/src/common/components/index.tsx index 632b525790a6d..e193a2a28934f 100644 --- a/superset-frontend/src/common/components/index.tsx +++ b/superset-frontend/src/common/components/index.tsx @@ -44,6 +44,7 @@ export { Typography, Tree, Popover, + Slider, Radio, Row, Select, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx index 3361029eda441..8e7d35475bccf 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx @@ -222,7 +222,7 @@ const FilterValue: React.FC = ({ } = filter; const cascadingFilters = useCascadingFilters(id); const [loading, setLoading] = useState(true); - const [state, setState] = useState({ data: undefined }); + const [state, setState] = useState([]); const [formData, setFormData] = useState>({}); const [target] = targets; const { datasetId = 18, column } = target; @@ -256,7 +256,7 @@ const FilterValue: React.FC = ({ force: false, requestParams: { dashboardId: 0 }, }).then(response => { - setState({ data: response.result[0].data }); + setState(response.result); setLoading(false); }); } diff --git a/superset-frontend/src/filters/components/Range/AntdRangeFilter.tsx b/superset-frontend/src/filters/components/Range/AntdRangeFilter.tsx new file mode 100644 index 0000000000000..33c0f8341a3a1 --- /dev/null +++ b/superset-frontend/src/filters/components/Range/AntdRangeFilter.tsx @@ -0,0 +1,56 @@ +/** + * 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 { styled } from '@superset-ui/core'; +import React from 'react'; +import { Slider } from 'src/common/components'; +import { AntdPluginFilterRangeProps } from './types'; +import { AntdPluginFilterStylesProps } from '../types'; +import { getRangeExtraFormData } from '../../utils'; + +const Styles = styled.div` + height: ${({ height }) => height}; + width: ${({ width }) => width}; +`; + +export default function AntdRangeFilter(props: AntdPluginFilterRangeProps) { + const { data, formData, height, width, setExtraFormData } = props; + const [row] = data; + // @ts-ignore + const { min, max }: { min: number; max: number } = row; + const { groupby } = formData; + const [col] = groupby || []; + + const handleChange = (value: [number, number]) => { + const [lower, upper] = value; + + setExtraFormData(getRangeExtraFormData(col, lower, upper)); + }; + + return ( + + + + ); +} diff --git a/superset-frontend/src/filters/components/Range/buildQuery.ts b/superset-frontend/src/filters/components/Range/buildQuery.ts new file mode 100644 index 0000000000000..1ea4fb12bb1c7 --- /dev/null +++ b/superset-frontend/src/filters/components/Range/buildQuery.ts @@ -0,0 +1,74 @@ +/** + * 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 { + buildQueryContext, + ColumnType, + QueryFormData, +} from '@superset-ui/core'; + +/** + * The buildQuery function is used to create an instance of QueryContext that's + * sent to the chart data endpoint. In addition to containing information of which + * datasource to use, it specifies the type (e.g. full payload, samples, query) and + * format (e.g. CSV or JSON) of the result and whether or not to force refresh the data from + * the datasource as opposed to using a cached copy of the data, if available. + * + * More importantly though, QueryContext contains a property `queries`, which is an array of + * QueryObjects specifying individual data requests to be made. A QueryObject specifies which + * columns, metrics and filters, among others, to use during the query. Usually it will be enough + * to specify just one query based on the baseQueryObject, but for some more advanced use cases + * it is possible to define post processing operations in the QueryObject, or multiple queries + * if a viz needs multiple different result sets. + */ +export default function buildQuery(formData: QueryFormData) { + const { groupby } = formData; + const [column] = groupby || []; + return buildQueryContext(formData, baseQueryObject => { + return [ + { + ...baseQueryObject, + groupby: [], + metrics: [ + { + aggregate: 'MIN', + column: { + columnName: column, + id: 1, + type: ColumnType.FLOAT, + }, + expressionType: 'SIMPLE', + hasCustomLabel: true, + label: 'min', + }, + { + aggregate: 'MAX', + column: { + columnName: column, + id: 2, + type: ColumnType.FLOAT, + }, + expressionType: 'SIMPLE', + hasCustomLabel: true, + label: 'max', + }, + ], + }, + ]; + }); +} diff --git a/superset-frontend/src/filters/components/Range/controlPanel.ts b/superset-frontend/src/filters/components/Range/controlPanel.ts new file mode 100644 index 0000000000000..bfc1c09fd6da3 --- /dev/null +++ b/superset-frontend/src/filters/components/Range/controlPanel.ts @@ -0,0 +1,44 @@ +/** + * 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 { t, validateNonEmpty } from '@superset-ui/core'; +import { ControlPanelConfig, sections } from '@superset-ui/chart-controls'; + +const config: ControlPanelConfig = { + // For control input types, see: superset-frontend/src/explore/components/controls/index.js + controlPanelSections: [ + // @ts-ignore + sections.legacyRegularTime, + { + label: t('Query'), + expanded: true, + controlSetRows: [['groupby'], ['adhoc_filters']], + }, + ], + controlOverrides: { + groupby: { + validators: [validateNonEmpty], + clearable: false, + }, + row_limit: { + default: 100, + }, + }, +}; + +export default config; diff --git a/superset-frontend/src/filters/components/Range/images/thumbnail.png b/superset-frontend/src/filters/components/Range/images/thumbnail.png new file mode 100644 index 0000000000000..7afef30bd4e6e Binary files /dev/null and b/superset-frontend/src/filters/components/Range/images/thumbnail.png differ diff --git a/superset-frontend/src/filters/components/Range/index.ts b/superset-frontend/src/filters/components/Range/index.ts new file mode 100644 index 0000000000000..4a77fbb97c412 --- /dev/null +++ b/superset-frontend/src/filters/components/Range/index.ts @@ -0,0 +1,42 @@ +/** + * 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 { t, ChartMetadata, ChartPlugin } from '@superset-ui/core'; +import buildQuery from './buildQuery'; +import controlPanel from './controlPanel'; +import transformProps from './transformProps'; +import thumbnail from './images/thumbnail.png'; + +export default class AntdRangeFilterPlugin extends ChartPlugin { + constructor() { + const metadata = new ChartMetadata({ + name: t('Range Filter Plugin'), + description: 'Range Filter Plugin using AntD', + isNativeFilter: true, + thumbnail, + }); + + super({ + buildQuery, + controlPanel, + loadChart: () => import('./AntdRangeFilter'), + metadata, + transformProps, + }); + } +} diff --git a/superset-frontend/src/filters/components/Range/transformProps.ts b/superset-frontend/src/filters/components/Range/transformProps.ts new file mode 100644 index 0000000000000..ca9ec7f4a1748 --- /dev/null +++ b/superset-frontend/src/filters/components/Range/transformProps.ts @@ -0,0 +1,33 @@ +/** + * 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 { ChartProps } from '@superset-ui/core'; + +export default function transformProps(chartProps: ChartProps) { + const { formData, height, hooks, queriesData, width } = chartProps; + const { setExtraFormData } = hooks; + const { data } = queriesData[0]; + + return { + data, + formData, + height, + setExtraFormData, + width, + }; +} diff --git a/superset-frontend/src/filters/components/Range/types.ts b/superset-frontend/src/filters/components/Range/types.ts new file mode 100644 index 0000000000000..63c9fbdce4c73 --- /dev/null +++ b/superset-frontend/src/filters/components/Range/types.ts @@ -0,0 +1,39 @@ +/** + * 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 { + DataRecord, + QueryFormData, + SetExtraFormDataHook, +} from '@superset-ui/core'; +import { AntdPluginFilterStylesProps } from '../types'; + +interface AntdPluginFilterSelectCustomizeProps { + max?: number; + min?: number; +} + +export type PluginFilterRangeQueryFormData = QueryFormData & + AntdPluginFilterStylesProps & + AntdPluginFilterSelectCustomizeProps; + +export type AntdPluginFilterRangeProps = AntdPluginFilterStylesProps & { + data: DataRecord[]; + formData: PluginFilterRangeQueryFormData; + setExtraFormData: SetExtraFormDataHook; +}; diff --git a/superset-frontend/src/filters/components/Select/AntdSelectFilter.tsx b/superset-frontend/src/filters/components/Select/AntdSelectFilter.tsx new file mode 100644 index 0000000000000..c29b6aa214bf4 --- /dev/null +++ b/superset-frontend/src/filters/components/Select/AntdSelectFilter.tsx @@ -0,0 +1,94 @@ +/** + * 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 { styled } from '@superset-ui/core'; +import React, { useEffect, useState } from 'react'; +import { Select } from 'src/common/components'; +import { DEFAULT_FORM_DATA, AntdPluginFilterSelectProps } from './types'; +import { AntdPluginFilterStylesProps } from '../types'; +import { getSelectExtraFormData } from '../../utils'; + +const Styles = styled.div` + height: ${({ height }) => height}; + width: ${({ width }) => width}; +`; + +const { Option } = Select; + +export default function AntdPluginFilterSelect( + props: AntdPluginFilterSelectProps, +) { + const [values, setValues] = useState<(string | number)[]>([]); + const { data, formData, height, width, setExtraFormData } = props; + const { + defaultValues, + enableEmptyFilter, + multiSelect, + showSearch, + inverseSelection, + } = { + ...DEFAULT_FORM_DATA, + ...formData, + }; + + useEffect(() => { + setValues(defaultValues || []); + }, [defaultValues]); + + let { groupby = [] } = formData; + groupby = Array.isArray(groupby) ? groupby : [groupby]; + + function handleChange(value?: number[] | string[] | null) { + setValues(value || []); + const [col] = groupby; + const emptyFilter = + enableEmptyFilter && + !inverseSelection && + (value === undefined || value === null || value.length === 0); + setExtraFormData( + getSelectExtraFormData(col, value, emptyFilter, inverseSelection), + ); + } + const placeholderText = + (data || []).length === 0 + ? 'No data' + : `${data.length} option${data.length > 1 ? 's' : 0}`; + return ( + + + + ); +} diff --git a/superset-frontend/src/filters/components/Select/buildQuery.ts b/superset-frontend/src/filters/components/Select/buildQuery.ts new file mode 100644 index 0000000000000..cc5447225bf6d --- /dev/null +++ b/superset-frontend/src/filters/components/Select/buildQuery.ts @@ -0,0 +1,43 @@ +/** + * 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 { buildQueryContext, QueryFormData } from '@superset-ui/core'; + +/** + * The buildQuery function is used to create an instance of QueryContext that's + * sent to the chart data endpoint. In addition to containing information of which + * datasource to use, it specifies the type (e.g. full payload, samples, query) and + * format (e.g. CSV or JSON) of the result and whether or not to force refresh the data from + * the datasource as opposed to using a cached copy of the data, if available. + * + * More importantly though, QueryContext contains a property `queries`, which is an array of + * QueryObjects specifying individual data requests to be made. A QueryObject specifies which + * columns, metrics and filters, among others, to use during the query. Usually it will be enough + * to specify just one query based on the baseQueryObject, but for some more advanced use cases + * it is possible to define post processing operations in the QueryObject, or multiple queries + * if a viz needs multiple different result sets. + */ +export default function buildQuery(formData: QueryFormData) { + return buildQueryContext(formData, baseQueryObject => { + return [ + { + ...baseQueryObject, + }, + ]; + }); +} diff --git a/superset-frontend/src/filters/components/Select/controlPanel.ts b/superset-frontend/src/filters/components/Select/controlPanel.ts new file mode 100644 index 0000000000000..2432396ca2881 --- /dev/null +++ b/superset-frontend/src/filters/components/Select/controlPanel.ts @@ -0,0 +1,114 @@ +/** + * 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 { t, validateNonEmpty } from '@superset-ui/core'; +import { ControlPanelConfig, sections } from '@superset-ui/chart-controls'; +import { DEFAULT_FORM_DATA } from './types'; + +const { + enableEmptyFilter, + fetchPredicate, + inverseSelection, + multiSelect, + showSearch, +} = DEFAULT_FORM_DATA; + +const config: ControlPanelConfig = { + controlPanelSections: [ + // @ts-ignore + sections.legacyRegularTime, + { + label: t('Query'), + expanded: true, + controlSetRows: [ + ['groupby'], + ['metrics'], + ['adhoc_filters'], + [ + { + name: 'multiSelect', + config: { + type: 'CheckboxControl', + label: t('Multiple Select'), + default: multiSelect, + description: t('Allow selecting multiple values'), + }, + }, + ], + [ + { + name: 'enableEmptyFilter', + config: { + type: 'CheckboxControl', + label: t('Enable Empty Filter'), + default: enableEmptyFilter, + description: t( + 'When selection is empty, should an always false filter event be emitted', + ), + }, + }, + ], + [ + { + name: 'inverseSelection', + config: { + type: 'CheckboxControl', + label: t('Inverse Selection'), + default: inverseSelection, + description: t('Exclude selected values'), + }, + }, + ], + [ + { + name: 'showSearch', + config: { + type: 'CheckboxControl', + label: t('Search Field'), + default: showSearch, + description: t('Allow typing search terms'), + }, + }, + ], + [ + { + name: 'fetchPredicate', + config: { + type: 'TextControl', + label: t('Fetch predicate'), + default: fetchPredicate, + description: t( + 'Predicate applied when fetching distinct value to populate the filter control component.', + ), + }, + }, + null, + ], + ['row_limit', null], + ], + }, + ], + controlOverrides: { + groupby: { + multi: false, + validators: [validateNonEmpty], + }, + }, +}; + +export default config; diff --git a/superset-frontend/src/filters/components/Select/images/thumbnail.png b/superset-frontend/src/filters/components/Select/images/thumbnail.png new file mode 100644 index 0000000000000..7afef30bd4e6e Binary files /dev/null and b/superset-frontend/src/filters/components/Select/images/thumbnail.png differ diff --git a/superset-frontend/src/filters/components/Select/index.ts b/superset-frontend/src/filters/components/Select/index.ts new file mode 100644 index 0000000000000..f0d05c3739241 --- /dev/null +++ b/superset-frontend/src/filters/components/Select/index.ts @@ -0,0 +1,42 @@ +/** + * 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 { t, ChartMetadata, ChartPlugin } from '@superset-ui/core'; +import buildQuery from './buildQuery'; +import controlPanel from './controlPanel'; +import transformProps from './transformProps'; +import thumbnail from './images/thumbnail.png'; + +export default class AntdFilterSelectPlugin extends ChartPlugin { + constructor() { + const metadata = new ChartMetadata({ + name: t('Select Filter Plugin'), + description: 'Select Filter Plugin using AntD', + isNativeFilter: true, + thumbnail, + }); + + super({ + buildQuery, + controlPanel, + loadChart: () => import('./AntdSelectFilter'), + metadata, + transformProps, + }); + } +} diff --git a/superset-frontend/src/filters/components/Select/transformProps.ts b/superset-frontend/src/filters/components/Select/transformProps.ts new file mode 100644 index 0000000000000..5888bb54d60f3 --- /dev/null +++ b/superset-frontend/src/filters/components/Select/transformProps.ts @@ -0,0 +1,36 @@ +/** + * 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 { ChartProps } from '@superset-ui/core'; +import { DEFAULT_FORM_DATA } from './types'; + +export default function transformProps(chartProps: ChartProps) { + const { formData, height, hooks, queriesData, width } = chartProps; + const newFormData = { ...DEFAULT_FORM_DATA, ...formData }; + const { setExtraFormData = () => {} } = hooks; + + const { data } = queriesData[0]; + + return { + width, + height, + data, + formData: newFormData, + setExtraFormData, + }; +} diff --git a/superset-frontend/src/filters/components/Select/types.ts b/superset-frontend/src/filters/components/Select/types.ts new file mode 100644 index 0000000000000..bfa31170b56d1 --- /dev/null +++ b/superset-frontend/src/filters/components/Select/types.ts @@ -0,0 +1,52 @@ +/** + * 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 { + QueryFormData, + DataRecord, + SetExtraFormDataHook, +} from '@superset-ui/core'; +import { AntdPluginFilterStylesProps } from '../types'; + +interface AntdPluginFilterSelectCustomizeProps { + defaultValues?: (string | number)[]; + enableEmptyFilter: boolean; + fetchPredicate?: string; + inverseSelection: boolean; + multiSelect: boolean; + showSearch: boolean; +} + +export type AntdPluginFilterSelectQueryFormData = QueryFormData & + AntdPluginFilterStylesProps & + AntdPluginFilterSelectCustomizeProps; + +export type AntdPluginFilterSelectProps = AntdPluginFilterStylesProps & { + data: DataRecord[]; + setExtraFormData: SetExtraFormDataHook; + formData: AntdPluginFilterSelectQueryFormData; +}; + +export const DEFAULT_FORM_DATA: AntdPluginFilterSelectCustomizeProps = { + defaultValues: [], + enableEmptyFilter: false, + fetchPredicate: '', + inverseSelection: false, + multiSelect: true, + showSearch: true, +}; diff --git a/superset-frontend/src/filters/components/index.ts b/superset-frontend/src/filters/components/index.ts new file mode 100644 index 0000000000000..fadaf1cdec122 --- /dev/null +++ b/superset-frontend/src/filters/components/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ +export { default as AntdSelectFilterPlugin } from './Select'; +export { default as AntdRangeFilterPlugin } from './Range'; diff --git a/superset-frontend/src/filters/components/types.ts b/superset-frontend/src/filters/components/types.ts new file mode 100644 index 0000000000000..869dc79e32163 --- /dev/null +++ b/superset-frontend/src/filters/components/types.ts @@ -0,0 +1,22 @@ +/** + * 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. + */ +export interface AntdPluginFilterStylesProps { + height: number; + width: number; +} diff --git a/superset-frontend/src/filters/utils.ts b/superset-frontend/src/filters/utils.ts new file mode 100644 index 0000000000000..1a5d11ca241d0 --- /dev/null +++ b/superset-frontend/src/filters/utils.ts @@ -0,0 +1,65 @@ +/** + * 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 { QueryObjectFilterClause } from '@superset-ui/core'; + +export const getSelectExtraFormData = ( + col: string, + value?: undefined | null | string[] | number[], + emptyFilter = false, + inverseSelection = false, +) => ({ + append_form_data: emptyFilter + ? { + extras: { + where: '1 = 0', + }, + } + : { + filters: + value === undefined || value === null || value.length === 0 + ? [] + : [ + { + col, + op: inverseSelection ? ('NOT IN' as const) : ('IN' as const), + val: value, + }, + ], + }, +}); + +export const getRangeExtraFormData = ( + col: string, + lower?: number | null, + upper?: number | null, +) => { + const filters: QueryObjectFilterClause[] = []; + if (lower !== undefined && lower !== null) { + filters.push({ col, op: '>=', val: lower }); + } + if (upper !== undefined && upper !== null) { + filters.push({ col, op: '<=', val: upper }); + } + + return { + append_form_data: { + filters, + }, + }; +}; diff --git a/superset-frontend/src/visualizations/presets/MainPreset.js b/superset-frontend/src/visualizations/presets/MainPreset.js index 48df2f3665470..0aec9ecbbe15c 100644 --- a/superset-frontend/src/visualizations/presets/MainPreset.js +++ b/superset-frontend/src/visualizations/presets/MainPreset.js @@ -60,10 +60,9 @@ import { EchartsTimeseriesChartPlugin, } from '@superset-ui/plugin-chart-echarts'; import { - AntdRangeFilterPlugin, AntdSelectFilterPlugin, -} from '@superset-ui/plugin-filter-antd'; - + AntdRangeFilterPlugin, +} from 'src/filters/components/'; import FilterBoxChartPlugin from '../FilterBox/FilterBoxChartPlugin'; import TimeTableChartPlugin from '../TimeTable/TimeTableChartPlugin';