Skip to content

Commit

Permalink
feat(native-filters): Highlight charts affected by focused native fil…
Browse files Browse the repository at this point in the history
…ter (apache#14693)

* Highlight charts affected by focused native filter

* Remove tabs animation on dashboard

* Remove a test that checks for "animated={true}" prop on tabs

* Move hooks types to a separate interface
  • Loading branch information
kgabryje authored May 21, 2021
1 parent 29828f8 commit c831655
Show file tree
Hide file tree
Showing 30 changed files with 220 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,6 @@ describe('DashboardBuilder', () => {
expect(parentSize.find(Tabs.TabPane)).toHaveLength(2);
});

it('should have default animated=true on Tabs for perf', () => {
const wrapper = setup({ dashboardLayout: undoableDashboardLayoutWithTabs });
const tabProps = wrapper.find(ParentSize).find(Tabs).props();
expect(tabProps.animated).toEqual(true);
});

it('should render a TabPane and DashboardGrid for first Tab', () => {
const wrapper = setup({ dashboardLayout: undoableDashboardLayoutWithTabs });
const parentSize = wrapper.find(ParentSize);
Expand Down
28 changes: 26 additions & 2 deletions superset-frontend/src/dashboard/actions/nativeFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ type BootstrapData = {
};
};

export interface SetBooststapData {
export interface SetBootstrapData {
type: typeof HYDRATE_DASHBOARD;
data: BootstrapData;
}
Expand Down Expand Up @@ -182,6 +182,28 @@ export function saveFilterSets(
};
}

export const SET_FOCUSED_NATIVE_FILTER = 'SET_FOCUSED_NATIVE_FILTER';
export interface SetFocusedNativeFilter {
type: typeof SET_FOCUSED_NATIVE_FILTER;
id: string;
}
export const UNSET_FOCUSED_NATIVE_FILTER = 'UNSET_FOCUSED_NATIVE_FILTER';
export interface UnsetFocusedNativeFilter {
type: typeof UNSET_FOCUSED_NATIVE_FILTER;
}

export function setFocusedNativeFilter(id: string): SetFocusedNativeFilter {
return {
type: SET_FOCUSED_NATIVE_FILTER,
id,
};
}
export function unsetFocusedNativeFilter(): UnsetFocusedNativeFilter {
return {
type: UNSET_FOCUSED_NATIVE_FILTER,
};
}

export type AnyFilterAction =
| SetFilterConfigBegin
| SetFilterConfigComplete
Expand All @@ -190,4 +212,6 @@ export type AnyFilterAction =
| SetFilterSetsConfigComplete
| SetFilterSetsConfigFail
| SaveFilterSets
| SetBooststapData;
| SetBootstrapData
| SetFocusedNativeFilter
| UnsetFocusedNativeFilter;
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const DashboardContainer: FC<DashboardContainerProps> = ({ topLevelTabs }) => {
activeKey={activeKey}
renderTabBar={() => <></>}
fullWidth={false}
animated
animated={false}
allowOverflow
>
{childIds.map((id, index) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const FilterFocusHighlight = React.forwardRef(
styles = {
borderColor: theme.colors.primary.light2,
opacity: 1,
boxShadow: `0px 0px ${({ theme }) => theme.gridUnit * 2}px ${
boxShadow: `0px 0px ${theme.gridUnit * 2}px ${
theme.colors.primary.light2
}`,
pointerEvents: 'auto',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,17 @@ import {
Behavior,
ChartDataResponseResult,
} from '@superset-ui/core';
import { useDispatch } from 'react-redux';
import { areObjectsEqual } from 'src/reduxUtils';
import { getChartDataRequest } from 'src/chart/chartAction';
import Loading from 'src/components/Loading';
import BasicErrorAlert from 'src/components/ErrorMessage/BasicErrorAlert';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { waitForAsyncData } from 'src/middleware/asyncEvent';
import {
setFocusedNativeFilter,
unsetFocusedNativeFilter,
} from 'src/dashboard/actions/nativeFilters';
import { ClientErrorObject } from 'src/utils/getClientErrorObject';
import { FilterProps } from './types';
import { getFormData } from '../../utils';
Expand Down Expand Up @@ -61,6 +66,7 @@ const FilterValue: React.FC<FilterProps> = ({
const { name: groupby } = column;
const hasDataSource = !!datasetId;
const [loading, setLoading] = useState<boolean>(hasDataSource);
const dispatch = useDispatch();
useEffect(() => {
const newFormData = getFormData({
...filter,
Expand Down Expand Up @@ -130,6 +136,9 @@ const FilterValue: React.FC<FilterProps> = ({
const setDataMask = (dataMask: DataMask) =>
onFilterSelectionChange(filter, dataMask);

const setFocusedFilter = () => dispatch(setFocusedNativeFilter(id));
const unsetFocusedFilter = () => dispatch(unsetFocusedNativeFilter());

if (error) {
return (
<BasicErrorAlert
Expand All @@ -155,7 +164,7 @@ const FilterValue: React.FC<FilterProps> = ({
behaviors={[Behavior.NATIVE_FILTER]}
filterState={filter.dataMask?.filterState}
ownState={filter.dataMask?.ownState}
hooks={{ setDataMask }}
hooks={{ setDataMask, setFocusedFilter, unsetFocusedFilter }}
/>
)}
</FilterItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { DataMask, HandlerFunction, styled, t } from '@superset-ui/core';
import { useDispatch } from 'react-redux';
import { DataMaskState, DataMaskWithId } from 'src/dataMask/types';
import { setFilterSetsConfiguration } from 'src/dashboard/actions/nativeFilters';
import { Filters, FilterSet, FilterSets } from 'src/dashboard/reducers/types';
import { Filters, FilterSet } from 'src/dashboard/reducers/types';
import { areObjectsEqual } from 'src/reduxUtils';
import { findExistingFilterSet, generateFiltersSetId } from './utils';
import { Filter } from '../../types';
Expand Down
21 changes: 17 additions & 4 deletions superset-frontend/src/dashboard/containers/DashboardComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,26 @@ function selectFocusedFilterScope(dashboardState, dashboardFilters) {
};
}

function selectFocusedNativeFilterScope(nativeFilters) {
if (!nativeFilters.focusedFilterId) return null;
const id = nativeFilters.focusedFilterId;
const focusedFilterScope = nativeFilters.filters[id].scope;
return {
chartId: id,
scope: {
scope: focusedFilterScope.rootPath,
immune: focusedFilterScope.excluded,
},
};
}

function mapStateToProps(
{
dashboardLayout: undoableLayout,
dashboardState,
dashboardInfo,
dashboardFilters,
nativeFilters,
},
ownProps,
) {
Expand All @@ -101,10 +115,9 @@ function mapStateToProps(
directPathToChild: dashboardState.directPathToChild,
directPathLastUpdated: dashboardState.directPathLastUpdated,
dashboardId: dashboardInfo.id,
focusedFilterScope: selectFocusedFilterScope(
dashboardState,
dashboardFilters,
),
focusedFilterScope:
selectFocusedFilterScope(dashboardState, dashboardFilters) ||
selectFocusedNativeFilterScope(nativeFilters),
};

// rows and columns need more data about their child dimensions
Expand Down
14 changes: 14 additions & 0 deletions superset-frontend/src/dashboard/reducers/nativeFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
SAVE_FILTER_SETS,
SET_FILTER_CONFIG_COMPLETE,
SET_FILTER_SETS_CONFIG_COMPLETE,
SET_FOCUSED_NATIVE_FILTER,
UNSET_FOCUSED_NATIVE_FILTER,
} from 'src/dashboard/actions/nativeFilters';
import { FilterSet, NativeFiltersState } from './types';
import { FilterConfiguration } from '../components/nativeFilters/types';
Expand Down Expand Up @@ -58,6 +60,7 @@ export function getInitialState({
} else {
state.filterSets = prevState?.filterSets ?? {};
}
state.focusedFilterId = undefined;
return state as NativeFiltersState;
}

Expand Down Expand Up @@ -97,6 +100,17 @@ export default function nativeFilterReducer(
state,
});

case SET_FOCUSED_NATIVE_FILTER:
return {
...state,
focusedFilterId: action.id,
};

case UNSET_FOCUSED_NATIVE_FILTER:
return {
...state,
focusedFilterId: undefined,
};
// TODO handle SET_FILTER_CONFIG_FAIL action
default:
return state;
Expand Down
1 change: 1 addition & 0 deletions superset-frontend/src/dashboard/reducers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,5 @@ export type Filters = {
export type NativeFiltersState = {
filters: Filters;
filterSets: FilterSets;
focusedFilterId?: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@
border-radius: @border-radius-large;
box-shadow: inset 0 0 0 2px @shadow-highlight,
0 0 0 3px fade(@shadow-highlight, @opacity-light);
transition: box-shadow 1s ease-in-out;
transition: box-shadow 0.2s ease-in-out, opacity 0.2s ease-in-out,
border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}

&.fade-out {
border-radius: @border-radius-large;
box-shadow: none;
transition: box-shadow 1s ease-in-out;
transition: box-shadow 0.2s ease-in-out, opacity 0.2s ease-in-out,
border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,16 @@ import { PluginFilterGroupByProps } from './types';
const { Option } = Select;

export default function PluginFilterGroupBy(props: PluginFilterGroupByProps) {
const { data, formData, height, width, setDataMask, filterState } = props;
const {
data,
formData,
height,
width,
setDataMask,
setFocusedFilter,
unsetFocusedFilter,
filterState,
} = props;
const { defaultValue, inputRef, multiSelect } = formData;

const [value, setValue] = useState<string[]>(defaultValue ?? []);
Expand Down Expand Up @@ -68,6 +77,8 @@ export default function PluginFilterGroupBy(props: PluginFilterGroupByProps) {
mode={multiSelect ? 'multiple' : undefined}
// @ts-ignore
onChange={handleChange}
onBlur={unsetFocusedFilter}
onFocus={setFocusedFilter}
ref={inputRef}
>
{columns.map(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ export default function transformProps(chartProps: ChartProps) {
width,
filterState,
} = chartProps;
const { setDataMask = () => {} } = hooks;
const {
setDataMask = () => {},
setFocusedFilter = () => {},
unsetFocusedFilter = () => {},
} = hooks;

const { data } = queriesData[0];

Expand All @@ -41,5 +45,7 @@ export default function transformProps(chartProps: ChartProps) {
data,
formData: { ...DEFAULT_FORM_DATA, ...formData },
setDataMask,
setFocusedFilter,
unsetFocusedFilter,
};
}
6 changes: 2 additions & 4 deletions superset-frontend/src/filters/components/GroupBy/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ import {
DataRecord,
FilterState,
QueryFormData,
SetDataMaskHook,
} from '@superset-ui/core';
import { RefObject } from 'react';
import { PluginFilterStylesProps } from '../types';
import { PluginFilterHooks, PluginFilterStylesProps } from '../types';

interface PluginFilterGroupByCustomizeProps {
defaultValue?: string[] | null;
Expand All @@ -39,10 +38,9 @@ export type PluginFilterGroupByQueryFormData = QueryFormData &
export type PluginFilterGroupByProps = PluginFilterStylesProps & {
behaviors: Behavior[];
data: DataRecord[];
setDataMask: SetDataMaskHook;
filterState: FilterState;
formData: PluginFilterGroupByQueryFormData;
};
} & PluginFilterHooks;

export const DEFAULT_FORM_DATA: PluginFilterGroupByCustomizeProps = {
defaultValue: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) {
height,
width,
setDataMask,
setFocusedFilter,
unsetFocusedFilter,
inputRef,
filterState,
} = props;
Expand Down Expand Up @@ -73,15 +75,17 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) {
{Number.isNaN(Number(min)) || Number.isNaN(Number(max)) ? (
<h4>{t('Chosen non-numeric column')}</h4>
) : (
<Slider
range
min={min}
max={max}
value={value}
onAfterChange={handleAfterChange}
onChange={handleChange}
ref={inputRef}
/>
<div onMouseEnter={setFocusedFilter} onMouseLeave={unsetFocusedFilter}>
<Slider
range
min={min}
max={max}
value={value}
onAfterChange={handleAfterChange}
onChange={handleChange}
ref={inputRef}
/>
</div>
)}
</Styles>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ export default function transformProps(chartProps: ChartProps) {
behaviors,
filterState,
} = chartProps;
const { setDataMask } = hooks;
const {
setDataMask = () => {},
setFocusedFilter = () => {},
unsetFocusedFilter = () => {},
} = hooks;
const { data } = queriesData[0];

return {
Expand All @@ -39,5 +43,7 @@ export default function transformProps(chartProps: ChartProps) {
setDataMask,
filterState,
width,
setFocusedFilter,
unsetFocusedFilter,
};
}
6 changes: 2 additions & 4 deletions superset-frontend/src/filters/components/Range/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ import {
DataRecord,
FilterState,
QueryFormData,
SetDataMaskHook,
} from '@superset-ui/core';
import { RefObject } from 'react';
import { PluginFilterStylesProps } from '../types';
import { PluginFilterHooks, PluginFilterStylesProps } from '../types';

interface PluginFilterSelectCustomizeProps {
max?: number;
Expand All @@ -38,8 +37,7 @@ export type PluginFilterRangeQueryFormData = QueryFormData &
export type PluginFilterRangeProps = PluginFilterStylesProps & {
data: DataRecord[];
formData: PluginFilterRangeQueryFormData;
setDataMask: SetDataMaskHook;
filterState: FilterState;
behaviors: Behavior[];
inputRef: RefObject<any>;
};
} & PluginFilterHooks;
Loading

0 comments on commit c831655

Please sign in to comment.