diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index e963ef23d0131..5dd8aff418961 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -59,8 +59,8 @@ "fontsource-inter": "^3.0.5", "geolib": "^2.0.24", "global-box": "^1.2.0", - "immer": "^8.0.1", "html-webpack-plugin": "^4.5.1", + "immer": "^8.0.1", "immutable": "^4.0.0-rc.12", "interweave": "^11.2.0", "jquery": "^3.5.1", diff --git a/superset-frontend/src/common/components/index.tsx b/superset-frontend/src/common/components/index.tsx index bc0eb71a055f3..c3ce3f39e8cbc 100644 --- a/superset-frontend/src/common/components/index.tsx +++ b/superset-frontend/src/common/components/index.tsx @@ -58,6 +58,7 @@ export { FormInstance } from 'antd/lib/form'; export { RadioChangeEvent } from 'antd/lib/radio'; export { default as Badge } from './Badge'; +export { default as Collapse } from './Collapse'; export { default as Progress } from './ProgressBar'; export const MenuItem = styled(AntdMenu.Item)` diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel.tsx index c68477daec2dd..0cdfffee78e52 100644 --- a/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel.tsx +++ b/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel.tsx @@ -37,6 +37,7 @@ import { FilterValue, } from './Styles'; import { Indicator } from './selectors'; +import { getFilterValueForDisplay } from '../nativeFilters/FilterBar/FilterSets/utils'; export interface IndicatorProps { indicator: Indicator; @@ -46,18 +47,21 @@ export interface IndicatorProps { const Indicator = ({ indicator: { column, name, value = [], path }, onClick, -}: IndicatorProps) => ( - onClick([...path, `LABEL-${column}`])}> - - - - - {name} - {value.length ? ': ' : ''} - - {value.length ? value.join(', ') : ''} - -); +}: IndicatorProps) => { + const resultValue = getFilterValueForDisplay(value); + return ( + onClick([...path, `LABEL-${column}`])}> + + + + + {name} + {resultValue ? ': ' : ''} + + {resultValue} + + ); +}; export interface DetailsPanelProps { appliedIndicators: Indicator[]; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.tsx index 92ea57ba5243a..f2964a122a39b 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.tsx @@ -36,11 +36,11 @@ import { useImmer } from 'use-immer'; import { getInitialMask } from 'src/dataMask/reducer'; import { areObjectsEqual } from 'src/reduxUtils'; import FilterConfigurationLink from './FilterConfigurationLink'; -import { useFilterConfiguration } from '../state'; import { Filter } from '../types'; import { buildCascadeFiltersTree, mapParentFiltersToChildren } from './utils'; import CascadePopover from './CascadePopover'; -import FilterSets from './FilterSets'; +import FilterSets from './FilterSets/FilterSets'; +import { useFilters, useFilterSets } from './state'; const barWidth = `250px`; @@ -128,14 +128,6 @@ const TitleArea = styled.h4` & > span { flex-grow: 1; } - - & :not(:first-child) { - margin-left: ${({ theme }) => theme.gridUnit}px; - - &:hover { - cursor: pointer; - } - } `; const StyledTabs = styled(Tabs)` @@ -153,6 +145,9 @@ const StyledTabs = styled(Tabs)` const ActionButtons = styled.div` display: grid; flex-direction: row; + justify-content: center; + align-items: center; + grid-gap: 10px; grid-template-columns: 1fr 1fr; ${({ theme }) => `padding: 0 ${theme.gridUnit * 2}px ${theme.gridUnit * 2}px`}; @@ -164,6 +159,9 @@ const ActionButtons = styled.div` const FilterControls = styled.div` padding: 0 ${({ theme }) => theme.gridUnit * 4}px; + &:hover { + cursor: pointer; + } `; interface FiltersBarProps { @@ -183,48 +181,67 @@ const FilterBar: React.FC = ({ setLastAppliedFilterData, ] = useImmer({}); const dispatch = useDispatch(); + const filterSets = useFilterSets(); + const filterSetsArray = Object.values(filterSets); + const filters = useFilters(); + const filtersArray = Object.values(filters); const dataMaskState = useSelector( state => state.dataMask.nativeFilters ?? {}, ); - const filterConfigs = useFilterConfiguration(); const canEdit = useSelector( ({ dashboardInfo }) => dashboardInfo.dash_edit_perm, ); const [visiblePopoverId, setVisiblePopoverId] = useState(null); const [isInitialized, setIsInitialized] = useState(false); + const handleApply = () => { + const filterIds = Object.keys(filterData); + filterIds.forEach(filterId => { + if (filterData[filterId]) { + dispatch( + updateDataMask(filterId, { + nativeFilters: filterData[filterId], + }), + ); + } + }); + setLastAppliedFilterData(() => filterData); + }; + useEffect(() => { if (isInitialized) { return; } - const areFiltersInitialized = filterConfigs.every( - filterConfig => - filterConfig.defaultValue === + const areFiltersInitialized = filtersArray.every(filterConfig => + areObjectsEqual( + filterConfig.defaultValue, filterData[filterConfig.id]?.currentState?.value, + ), ); if (areFiltersInitialized) { + handleApply(); setIsInitialized(true); } - }, [filterConfigs, filterData, isInitialized]); + }, [filtersArray, filterData, isInitialized]); useEffect(() => { - if (filterConfigs.length === 0 && filtersOpen) { + if (filtersArray.length === 0 && filtersOpen) { toggleFiltersBar(false); } - }, [filterConfigs]); + }, [filtersArray.length]); const cascadeChildren = useMemo( - () => mapParentFiltersToChildren(filterConfigs), - [filterConfigs], + () => mapParentFiltersToChildren(filtersArray), + [filtersArray], ); const cascadeFilters = useMemo(() => { - const filtersWithValue = filterConfigs.map(filter => ({ + const filtersWithValue = filtersArray.map(filter => ({ ...filter, currentValue: filterData[filter.id]?.currentState?.value, })); return buildCascadeFiltersTree(filtersWithValue); - }, [filterConfigs, filterData]); + }, [filtersArray, filterData]); const handleFilterSelectionChange = ( filter: Pick & Partial, @@ -243,35 +260,15 @@ const FilterBar: React.FC = ({ }); }; - const handleApply = () => { - const filterIds = Object.keys(filterData); - filterIds.forEach(filterId => { - if (filterData[filterId]) { - dispatch( - updateDataMask(filterId, { - nativeFilters: filterData[filterId], - }), - ); - } - }); - setLastAppliedFilterData(() => filterData); - }; - - useEffect(() => { - if (isInitialized) { - handleApply(); - } - }, [isInitialized]); - const handleClearAll = () => { - filterConfigs.forEach(filter => { + filtersArray.forEach(filter => { setFilterData(draft => { draft[filter.id] = getInitialMask(filter.id); }); }); }; - const isClearAllDisabled = !Object.values(dataMaskState).every( + const isClearAllDisabled = Object.values(dataMaskState).every( filter => filterData[filter.id]?.currentState?.value === null || (!filterData[filter.id] && filter.currentState?.value === null), @@ -295,6 +292,9 @@ const FilterBar: React.FC = ({ ); + const isApplyDisabled = + !isInitialized || areObjectsEqual(filterData, lastAppliedFilterData); + return ( = ({ {t('Filters')} {canEdit && ( @@ -318,7 +318,7 @@ const FilterBar: React.FC = ({ = ({ {t('Clear all')} = ({ onChange={() => {}} > {getFilterControls()} - + diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets.tsx deleted file mode 100644 index 0d3cbfaac661b..0000000000000 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets.tsx +++ /dev/null @@ -1,198 +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 { Input, Select } from 'src/common/components'; -import Button from 'src/components/Button'; -import React, { ChangeEvent, useState } from 'react'; -import { styled, t, tn } from '@superset-ui/core'; -import { useDispatch, useSelector } from 'react-redux'; -import { - DataMaskState, - DataMaskUnitWithId, - MaskWithId, -} from 'src/dataMask/types'; -import { setFilterSetsConfiguration } from 'src/dashboard/actions/nativeFilters'; -import { FiltersSet, FilterSets } from 'src/dashboard/reducers/types'; -import { generateFiltersSetId } from './utils'; -import { Filter } from '../types'; - -const FilterSet = styled.div` - display: grid; - align-items: center; - justify-content: center; - grid-template-columns: 1fr; - grid-gap: 10px; - padding-top: 10px; -`; - -const StyledTitle = styled.h4` - width: 100%; - font-size: ${({ theme }) => theme.typography.sizes.s}px; - color: ${({ theme }) => theme.colors.grayscale.dark1}; - margin: 0; - overflow-wrap: break-word; - - & > .ant-select { - width: 100%; - } -`; - -const ActionButtons = styled.div` - display: grid; - flex-direction: row; - grid-template-columns: 1fr 1fr; - ${({ theme }) => - `padding: 0 ${theme.gridUnit * 2}px ${theme.gridUnit * 2}px`}; - border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - - .btn { - flex: 1; - } -`; - -const Sets = styled(ActionButtons)` - grid-template-columns: 1fr; -`; - -type FilterSetsProps = { - dataMaskState: DataMaskUnitWithId; - onFilterSelectionChange: ( - filter: Pick & Partial, - dataMask: Partial, - ) => void; -}; - -const FilterSets: React.FC = ({ - onFilterSelectionChange, - dataMaskState, -}) => { - const dispatch = useDispatch(); - const [filtersSetName, setFiltersSetName] = useState(''); - const filterSets = useSelector( - state => state.nativeFilters.filterSets ?? {}, - ); - const filterSetsConfigs = useSelector( - state => state.dashboardInfo?.metadata?.filter_sets_configuration || [], - ); - const [selectedFiltersSetId, setSelectedFiltersSetId] = useState< - string | null - >(null); - - const takeFiltersSet = (value: string) => { - setSelectedFiltersSetId(value); - if (!value) { - return; - } - const filtersSet = filterSets[value]; - Object.values(filtersSet.dataMask?.nativeFilters ?? []).forEach( - dataMask => { - const { extraFormData, currentState, id } = dataMask as MaskWithId; - onFilterSelectionChange( - { id }, - { nativeFilters: { extraFormData, currentState } }, - ); - }, - ); - }; - - const handleSaveFilterSets = () => { - dispatch( - setFilterSetsConfiguration( - filterSetsConfigs.concat([ - { - name: filtersSetName.trim(), - id: generateFiltersSetId(), - dataMask: { - nativeFilters: dataMaskState, - }, - }, - ]), - ), - ); - setFiltersSetName(''); - }; - - const handleDeleteFilterSets = () => { - dispatch( - setFilterSetsConfiguration( - filterSetsConfigs.filter( - filtersSet => filtersSet.id !== selectedFiltersSetId, - ), - ), - ); - setFiltersSetName(''); - setSelectedFiltersSetId(null); - }; - - return ( - - - - {t('Choose filters set')} - - {Object.values(filterSets).map(({ name, id }) => ( - {name} - ))} - - - - {t('Delete Filters Set')} - - - {t('Name')} - ) => { - setFiltersSetName(value); - }} - /> - - - {t('Save Filters Set')} - - - - ); -}; - -export default FilterSets; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.tsx new file mode 100644 index 0000000000000..825267777dbf0 --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.tsx @@ -0,0 +1,181 @@ +/** + * 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 { Select, Typography } from 'src/common/components'; +import Button from 'src/components/Button'; +import React, { useState } from 'react'; +import { styled, t, tn } from '@superset-ui/core'; +import { useDispatch } from 'react-redux'; +import { + DataMaskState, + DataMaskUnitWithId, + MaskWithId, +} from 'src/dataMask/types'; +import { setFilterSetsConfiguration } from 'src/dashboard/actions/nativeFilters'; +import { generateFiltersSetId } from './utils'; +import { Filter } from '../../types'; +import { useFilters, useDataMask, useFilterSets } from '../state'; +import Footer from './Footer'; +import FiltersHeader from './FiltersHeader'; + +const FilterSet = styled.div` + display: grid; + align-items: center; + justify-content: center; + grid-template-columns: 1fr; + grid-gap: ${({ theme }) => theme.gridUnit}px; + ${({ theme }) => + `padding: 0 ${theme.gridUnit * 4}px ${theme.gridUnit * 4}px`}; + border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; + & button.superset-button { + margin-left: 0; + } + & input { + width: 100%; + } + & .ant-typography-edit-content { + left: 0; + margin-top: 0; + } +`; + +type FilterSetsProps = { + disabled: boolean; + dataMaskState: DataMaskUnitWithId; + onFilterSelectionChange: ( + filter: Pick & Partial, + dataMask: Partial, + ) => void; +}; + +const DEFAULT_FILTER_SET_NAME = t('New filter set'); + +const FilterSets: React.FC = ({ + disabled, + onFilterSelectionChange, + dataMaskState, +}) => { + const dispatch = useDispatch(); + const [filterSetName, setFilterSetName] = useState(DEFAULT_FILTER_SET_NAME); + const [editMode, setEditMode] = useState(false); + const filterSets = useFilterSets(); + const filterSetsArray = Object.values(filterSets); + const dataMask = useDataMask(); + const filters = Object.values(useFilters()); + const [selectedFiltersSetId, setSelectedFiltersSetId] = useState< + string | null + >(null); + + const takeFilterSet = (value: string) => { + setSelectedFiltersSetId(value); + if (!value) { + return; + } + const filtersSet = filterSets[value]; + Object.values(filtersSet.dataMask?.nativeFilters ?? []).forEach( + dataMask => { + const { extraFormData, currentState, id } = dataMask as MaskWithId; + onFilterSelectionChange( + { id }, + { nativeFilters: { extraFormData, currentState } }, + ); + }, + ); + }; + + const handleDeleteFilterSets = () => { + dispatch( + setFilterSetsConfiguration( + filterSetsArray.filter( + filtersSet => filtersSet.id !== selectedFiltersSetId, + ), + ), + ); + setFilterSetName(DEFAULT_FILTER_SET_NAME); + setSelectedFiltersSetId(null); + }; + + const handleCancel = () => { + setEditMode(false); + setFilterSetName(DEFAULT_FILTER_SET_NAME); + }; + + const handleCreateFilterSet = () => { + dispatch( + setFilterSetsConfiguration( + filterSetsArray.concat([ + { + name: filterSetName.trim(), + id: generateFiltersSetId(), + dataMask: { + nativeFilters: dataMaskState, + }, + }, + ]), + ), + ); + setEditMode(false); + setFilterSetName(DEFAULT_FILTER_SET_NAME); + }; + + return ( + + , + onChange: setFilterSetName, + }} + > + {filterSetName} + + + + ); +}; + +export default FilterSets; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx new file mode 100644 index 0000000000000..d8faa4bc39692 --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx @@ -0,0 +1,88 @@ +/** + * 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, { FC } from 'react'; +import { styled, t } from '@superset-ui/core'; +import { Collapse, Typography } from 'src/common/components'; +import { DataMaskUnitWithId } from 'src/dataMask/types'; +import { getFilterValueForDisplay } from './utils'; +import { Filter } from '../../types'; + +const FilterHeader = styled.div` + display: flex; + align-items: center; + font-size: ${({ theme }) => theme.typography.sizes.s}px; +`; + +const StyledCollapse = styled(Collapse)` + &.ant-collapse-ghost > .ant-collapse-item { + & > .ant-collapse-content > .ant-collapse-content-box { + padding: 0; + padding-top: 0; + padding-bottom: 0; + font-size: ${({ theme }) => theme.typography.sizes.s}px; + } + & > .ant-collapse-header { + padding: 0; + display: flex; + align-items: center; + flex-direction: row-reverse; + justify-content: flex-end; + & .ant-collapse-arrow { + position: static; + padding-left: ${({ theme }) => theme.gridUnit}px; + } + } +`; + +type FiltersHeaderProps = { + filters: Filter[]; + dataMask: DataMaskUnitWithId; +}; + +const FiltersHeader: FC = ({ filters, dataMask }) => { + const getFiltersHeader = () => ( + + + {t('Filters (%d)', filters.length)} + + + ); + return ( + + + {filters.map(({ id, name }) => ( + + {name}: + + {getFilterValueForDisplay(dataMask[id]?.currentState?.value) || ( + {t('None')} + )} + + + ))} + + + ); +}; + +export default FiltersHeader; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/Footer.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/Footer.tsx new file mode 100644 index 0000000000000..a19be0062e236 --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/Footer.tsx @@ -0,0 +1,107 @@ +/** + * 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, styled } from '@superset-ui/core'; +import React, { FC } from 'react'; +import Button from 'src/components/Button'; +import { Tooltip } from 'src/common/components/Tooltip'; + +type FooterProps = { + isApplyDisabled: boolean; + disabled: boolean; + editMode: boolean; + onCancel: () => void; + onEdit: () => void; + onCreate: () => void; +}; + +const ActionButton = styled.div<{ disabled: boolean }>` + display: flex; + padding: 1px; + & button { + ${({ disabled }) => `pointer-events: ${disabled ? 'none' : 'all'}`}; + flex: 1; + } +`; +const ActionButtons = styled.div` + display: grid; + flex-direction: row; + justify-content: center; + align-items: center; + grid-gap: 10px; + grid-template-columns: 1fr 1fr; +`; + +const APPLY_FILTERS = t('Please apply filter changes'); + +const Footer: FC = ({ + onCancel, + editMode, + onEdit, + onCreate, + disabled, + isApplyDisabled, +}) => ( + <> + {editMode ? ( + + + {t('Cancel')} + + + + + {t('Create')} + + + + + ) : ( + + + + {t('Create new filter set')} + + + + )} + > +); + +export default Footer; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils.ts new file mode 100644 index 0000000000000..8f5cce5df21dd --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils.ts @@ -0,0 +1,41 @@ +/** + * 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 shortid from 'shortid'; +import { t } from '@superset-ui/core'; + +export const generateFiltersSetId = () => `FILTERS_SET-${shortid.generate()}`; + +export const getFilterValueForDisplay = ( + value?: string[] | null | string | number | object, +): string => { + if (value === null || value === undefined) { + return ''; + } + if (typeof value === 'string' || typeof value === 'number') { + return `${value}`; + } + if (Array.isArray(value)) { + return value.join(', '); + } + if (typeof value === 'object') { + return JSON.stringify(value); + } + return t('Unknown value'); +}; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts index afa64826d4303..d0b5a5dc1706b 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/state.ts @@ -17,25 +17,33 @@ * under the License. */ import { useSelector } from 'react-redux'; -import { NativeFiltersState } from 'src/dashboard/reducers/types'; -import { DataMaskStateWithId } from 'src/dataMask/types'; +import { + FilterSets as FilterSetsType, + NativeFiltersState, +} from 'src/dashboard/reducers/types'; +import { DataMaskUnitWithId } from 'src/dataMask/types'; import { mergeExtraFormData } from '../utils'; import { Filter } from '../types'; -export function useFilters() { - return useSelector(state => state.nativeFilters.filters); -} +export const useFilterSets = () => + useSelector( + state => state.nativeFilters.filterSets || {}, + ); + +export const useFilters = () => + useSelector(state => state.nativeFilters.filters); + +export const useDataMask = () => + useSelector(state => state.dataMask.nativeFilters); export function useCascadingFilters(id: string) { const { filters } = useSelector( state => state.nativeFilters, ); - const { nativeFilters } = useSelector( - state => state.dataMask, - ); const filter = filters[id]; const cascadeParentIds: string[] = filter?.cascadeParentIds ?? []; let cascadedFilters = {}; + const nativeFilters = useDataMask(); cascadeParentIds.forEach(parentId => { const parentState = nativeFilters[parentId] || {}; const { extraFormData: parentExtra = {} } = parentState; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/utils.ts index c7f46686925f5..71681a23b906d 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/utils.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/utils.ts @@ -1,7 +1,3 @@ -import shortid from 'shortid'; -import { Filter } from '../types'; -import { CascadeFilter } from './types'; - /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -21,6 +17,9 @@ import { CascadeFilter } from './types'; * under the License. */ +import { Filter } from '../types'; +import { CascadeFilter } from './types'; + export function mapParentFiltersToChildren( filters: Filter[], ): { [id: string]: Filter[] } { @@ -52,5 +51,3 @@ export function buildCascadeFiltersTree(filters: Filter[]): CascadeFilter[] { .filter(filter => !filter.cascadeParentIds?.length) .map(getCascadeFilter); } - -export const generateFiltersSetId = () => `FILTERS_SET-${shortid.generate()}`;