feat(native-filters): Update filter bar buttons (#13506)

* refactor(native-filters): move data mask to root reducer

* refactor: update rest stuff for dataMask

* refactor: add ownCrrentState to explore

* fix: fix immer reducer

* fix: merge with master

* refactor: support explore dataMask

* refactor: support explore dataMask

* docs: add comment

* refactor: remove json stringify

* fix: fix failed cases

* feat: filter bat buttons start

* fix: fix CR notes

* fix: fix cascade filters

* fix: fix CR notes

* refactor: add clear all

* fix: fix CR notes

* fix: fix CR notes

* fix: fix CR notes

* feat: buttons in filter bar

* lint: update imports

* test: fix tests
This commit is contained in:
simcha90 2021-03-07 11:27:22 +02:00 committed by GitHub
parent 45972e9945
commit 3970d7316b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 34 deletions

View File

@ -122,6 +122,7 @@
"rison": "^0.1.1",
"shortid": "^2.2.6",
"urijs": "^1.19.4",
"use-immer": "^0.4.2",
"use-query-params": "^1.1.9"
},
"devDependencies": {
@ -54551,6 +54552,15 @@
"ts-essentials": "^2.0.3"
}
},
"node_modules/use-immer": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/use-immer/-/use-immer-0.4.2.tgz",
"integrity": "sha512-ONfZHEv/gzt/jyYxrJD3ZFUllKJED8F1mds9Fr9CYj54LmsiuGDg3vkx+R7WHS72p7DbSS1QbM7xNYrVWX+KcA==",
"peerDependencies": {
"immer": ">=2.0.0",
"react": "^16.8.0 || ^17.0.1"
}
},
"node_modules/use-isomorphic-layout-effect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.0.0.tgz",
@ -104943,6 +104953,12 @@
"ts-essentials": "^2.0.3"
}
},
"use-immer": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/use-immer/-/use-immer-0.4.2.tgz",
"integrity": "sha512-ONfZHEv/gzt/jyYxrJD3ZFUllKJED8F1mds9Fr9CYj54LmsiuGDg3vkx+R7WHS72p7DbSS1QbM7xNYrVWX+KcA==",
"requires": {}
},
"use-isomorphic-layout-effect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.0.0.tgz",

View File

@ -174,6 +174,7 @@
"rison": "^0.1.1",
"shortid": "^2.2.6",
"urijs": "^1.19.4",
"use-immer": "^0.4.2",
"use-query-params": "^1.1.9"
},
"devDependencies": {

View File

@ -42,9 +42,9 @@ describe('FilterBar', () => {
expect(wrapper.find({ name: 'filter' })).toExist();
expect(wrapper.find({ name: 'collapse' })).toExist();
});
it('has apply and reset all buttons', () => {
it('has apply and clear all buttons', () => {
expect(wrapper.find(Button).length).toBe(2);
expect(wrapper.find(Button).at(0)).toHaveProp('buttonStyle', 'secondary');
expect(wrapper.find(Button).at(0)).toHaveProp('buttonStyle', 'tertiary');
expect(wrapper.find(Button).at(1)).toHaveProp('buttonStyle', 'primary');
});
});

View File

@ -16,6 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
/* eslint-disable no-param-reassign */
import { styled, t, tn } from '@superset-ui/core';
import React, { useState, useEffect, useMemo, ChangeEvent } from 'react';
import { useDispatch, useSelector } from 'react-redux';
@ -33,8 +35,11 @@ import {
DataMaskUnit,
DataMaskState,
} from 'src/dataMask/types';
import { useImmer } from 'use-immer';
import { getInitialMask } from 'src/dataMask/reducer';
import { areObjectsEqual } from 'src/reduxUtils';
import FilterConfigurationLink from './FilterConfigurationLink';
import { useFilters, useFilterSets } from './state';
import { useFilterSets } from './state';
import { useFilterConfiguration } from '../state';
import { Filter } from '../types';
import {
@ -48,6 +53,7 @@ const barWidth = `250px`;
const BarWrapper = styled.div`
width: ${({ theme }) => theme.gridUnit * 8}px;
&.open {
width: ${barWidth}; // arbitrary...
}
@ -70,6 +76,7 @@ const Bar = styled.div`
transition: transform ${({ theme }) => theme.transitionTiming}s;
transition-delay: 0s;
} */
&.open {
display: flex;
/* &.animated {
@ -85,6 +92,7 @@ const StyledTitle = styled.h4`
color: ${({ theme }) => theme.colors.grayscale.dark1};
margin: 0;
overflow-wrap: break-word;
& > .ant-select {
width: 100%;
}
@ -105,6 +113,7 @@ const CollapsedBar = styled.div`
transition: transform ${({ theme }) => theme.transitionTiming}s;
transition-delay: 0s;
} */
&.open {
display: flex;
flex-direction: column;
@ -115,6 +124,7 @@ const CollapsedBar = styled.div`
transition-delay: ${({ theme }) => theme.transitionTiming * 3}s;
} */
}
svg {
width: ${({ theme }) => theme.gridUnit * 4}px;
height: ${({ theme }) => theme.gridUnit * 4}px;
@ -141,12 +151,15 @@ const TitleArea = styled.h4`
flex-direction: row;
justify-content: space-between;
margin: 0;
padding: ${({ theme }) => theme.gridUnit * 4}px;
padding: ${({ theme }) => theme.gridUnit * 2}px;
& > span {
flex-grow: 1;
}
& :not(:first-child) {
margin-left: ${({ theme }) => theme.gridUnit}px;
&:hover {
cursor: pointer;
}
@ -154,17 +167,22 @@ const TitleArea = styled.h4`
`;
const ActionButtons = styled.div`
display: flex;
display: grid;
flex-direction: row;
justify-content: space-around;
padding: ${({ theme }) => theme.gridUnit * 4}px;
padding-top: 0;
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 1 50%;
flex: 1;
}
`;
const Sets = styled(ActionButtons)`
grid-template-columns: 1fr;
`;
const FilterControls = styled.div`
padding: ${({ theme }) => theme.gridUnit * 4}px;
`;
@ -180,7 +198,11 @@ const FilterBar: React.FC<FiltersBarProps> = ({
toggleFiltersBar,
directPathToChild,
}) => {
const [filterData, setFilterData] = useState<DataMaskUnit>({});
const [filterData, setFilterData] = useImmer<DataMaskUnit>({});
const [
lastAppliedFilterData,
setLastAppliedFilterData,
] = useImmer<DataMaskUnit>({});
const dispatch = useDispatch();
const dataMaskState = useSelector<any, DataMaskUnitWithId>(
state => state.dataMask.nativeFilters ?? {},
@ -190,7 +212,6 @@ const FilterBar: React.FC<FiltersBarProps> = ({
const filterSetsConfigs = useSelector<any, FiltersSet[]>(
state => state.dashboardInfo?.metadata?.filter_sets_configuration || [],
);
const filters = useFilters();
const [filtersSetName, setFiltersSetName] = useState('');
const [selectedFiltersSetId, setSelectedFiltersSetId] = useState<
string | null
@ -238,20 +259,16 @@ const FilterBar: React.FC<FiltersBarProps> = ({
filter: Pick<Filter, 'id'> & Partial<Filter>,
dataMask: Partial<DataMaskState>,
) => {
setFilterData(prevFilterData => {
setFilterData(draft => {
const children = cascadeChildren[filter.id] || [];
// force instant updating on initialization or for parent filters
if (filter.isInstant || children.length > 0) {
dispatch(updateDataMask(filter.id, dataMask));
}
if (!dataMask.nativeFilters) {
return { ...prevFilterData };
if (dataMask.nativeFilters) {
draft[filter.id] = dataMask.nativeFilters;
}
return {
...prevFilterData,
[filter.id]: dataMask.nativeFilters,
};
});
};
@ -283,6 +300,7 @@ const FilterBar: React.FC<FiltersBarProps> = ({
);
}
});
setLastAppliedFilterData(() => filterData);
};
useEffect(() => {
@ -320,21 +338,20 @@ const FilterBar: React.FC<FiltersBarProps> = ({
setSelectedFiltersSetId(null);
};
const handleResetAll = () => {
const handleClearAll = () => {
filterConfigs.forEach(filter => {
dispatch(
updateDataMask(filter.id, {
nativeFilters: {
currentState: {
...filterData[filter.id]?.currentState,
value: filters[filter.id]?.defaultValue,
},
},
}),
);
setFilterData(draft => {
draft[filter.id] = getInitialMask(filter.id);
});
});
};
const isClearAllDisabled = !Object.values(dataMaskState).every(
filter =>
filterData[filter.id]?.currentState?.value === null ||
(!filterData[filter.id] && filter.currentState?.value === null),
);
return (
<BarWrapper data-test="filter-bar" className={cx({ open: filtersOpen })}>
<CollapsedBar
@ -360,14 +377,19 @@ const FilterBar: React.FC<FiltersBarProps> = ({
</TitleArea>
<ActionButtons>
<Button
buttonStyle="secondary"
disabled={!isClearAllDisabled}
buttonStyle="tertiary"
buttonSize="small"
onClick={handleResetAll}
onClick={handleClearAll}
data-test="filter-reset-button"
>
{t('Reset all')}
{t('Clear all')}
</Button>
<Button
disabled={
!isInitialized ||
areObjectsEqual(filterData, lastAppliedFilterData)
}
buttonStyle="primary"
htmlType="submit"
buttonSize="small"
@ -378,7 +400,7 @@ const FilterBar: React.FC<FiltersBarProps> = ({
</Button>
</ActionButtons>
{isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS_SET) && (
<ActionButtons>
<Sets>
<FilterSet>
<StyledTitle>
<div>{t('Choose filters set')}</div>
@ -429,7 +451,7 @@ const FilterBar: React.FC<FiltersBarProps> = ({
{t('Save Filters Set')}
</Button>
</FilterSet>
</ActionButtons>
</Sets>
)}
<FilterControls>
{cascadeFilters.map(filter => (