Skip to content

Commit

Permalink
feat: Adds support to multiple dependencies to the native filters (#1…
Browse files Browse the repository at this point in the history
…8793)

* chore(native-filters): Remove cascading popovers from filter bar

Co-authored-by: Kamil Gabryjelski <kamil.gabryjelski@gmail.com>
  • Loading branch information
michael-s-molina and kgabryje authored Mar 4, 2022
1 parent 299b5dc commit 06e1e42
Show file tree
Hide file tree
Showing 69 changed files with 939 additions and 1,325 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ import {
nativeFilters,
exploreView,
} from 'cypress/support/directories';
import {
testItems,
WORLD_HEALTH_CHARTS,
waitForChartLoad,
} from './dashboard.helper';
import { testItems } from './dashboard.helper';
import { DASHBOARD_LIST } from '../dashboard_list/dashboard_list.helper';
import { CHART_LIST } from '../chart_list/chart_list.helper';
import { FORM_DATA_DEFAULTS } from '../explore/visualizations/shared.helper';
Expand Down Expand Up @@ -263,7 +259,6 @@ describe('Nativefilters Sanity test', () => {
'Filter has default value',
'Can select multiple values',
'Filter value is required',
'Filter is hierarchical',
'Select first filter value by default',
'Inverse selection',
'Dynamically search all filter values',
Expand Down Expand Up @@ -534,114 +529,6 @@ describe('Nativefilters Sanity test', () => {
.contains('year')
.should('be.visible');
});
it('User can create parent filters using "Filter is hierarchical"', () => {
cy.get(nativeFilters.filterFromDashboardView.expand).click({ force: true });
// Create region filter
cy.get(nativeFilters.filterFromDashboardView.createFilterButton)
.should('be.visible')
.click();
cy.get(nativeFilters.filtersPanel.filterTypeInput)
.find(nativeFilters.filtersPanel.filterTypeItem)
.click({ force: true });
cy.get('[label="Value"]').click();
cy.get(nativeFilters.modal.container)
.find(nativeFilters.filtersPanel.datasetName)
.click({ force: true })
.within(() =>
cy
.get('input')
.type('wb_health_population{enter}', { delay: 50, force: true }),
);
cy.get(nativeFilters.modal.container)
.find(nativeFilters.filtersPanel.filterName)
.click({ force: true })
.clear()
.type('region', { scrollBehavior: false, force: true });
cy.wait(3000);
cy.get(nativeFilters.silentLoading).should('not.exist');
cy.get(nativeFilters.filtersPanel.filterInfoInput)
.last()
.should('be.visible')
.click({ force: true });
cy.get(nativeFilters.filtersPanel.filterInfoInput).last().type('region');
cy.get(nativeFilters.filtersPanel.inputDropdown)
.last()
.should('be.visible', { timeout: 20000 })
.click({ force: true });
// Create country filter
cy.get(nativeFilters.addFilterButton.button)
.first()
.click({ force: true })
.then(() => {
cy.get(nativeFilters.addFilterButton.dropdownItem)
.contains('Filter')
.click({ force: true });
});
cy.get(nativeFilters.filtersPanel.filterTypeInput)
.find(nativeFilters.filtersPanel.filterTypeItem)
.last()
.click({ force: true });
cy.get('[label="Value"]').last().click({ force: true });
cy.get(nativeFilters.modal.container)
.find(nativeFilters.filtersPanel.datasetName)
.last()
.click({ scrollBehavior: false })
.within(() =>
cy
.get('input')
.clear({ force: true })
.type('wb_health_population{enter}', {
delay: 50,
force: true,
scrollBehavior: false,
}),
);
cy.get(nativeFilters.filtersPanel.filterInfoInput)
.last()
.should('be.visible')
.click({ force: true });
cy.get(nativeFilters.filtersPanel.inputDropdown)
.should('be.visible', { timeout: 20000 })
.last()
.click();
cy.get(nativeFilters.modal.container)
.find(nativeFilters.filtersPanel.filterName)
.last()
.click({ force: true })
.type('country', { scrollBehavior: false, force: true });
cy.get(nativeFilters.silentLoading).should('not.exist');
cy.get(nativeFilters.filtersPanel.filterInfoInput)
.last()
.click({ force: true });
cy.get(nativeFilters.filtersPanel.filterInfoInput)
.last()
.type('country_name', { delay: 50, scrollBehavior: false, force: true });
cy.get(nativeFilters.filtersPanel.inputDropdown)
.last()
.should('be.visible', { timeout: 20000 })
.click({ force: true });
// Setup parent filter
cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(
() => {
cy.contains('Filter is hierarchical').should('be.visible').click();
cy.wait(1000);
cy.get(nativeFilters.filterConfigurationSections.parentFilterInput)
.click()
.type('region{enter}', { delay: 30 });
},
);
cy.get(nativeFilters.modal.footer)
.contains('Save')
.should('be.visible')
.click();
WORLD_HEALTH_CHARTS.forEach(waitForChartLoad);
// assert that native filter is created
cy.get(nativeFilters.filterFromDashboardView.filterName)
.should('be.visible')
.contains('region');
cy.get(nativeFilters.filterIcon).click();
cy.contains('Select parent filters (2)').should('be.visible');
});
});

xdescribe('Nativefilters', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export type Props = Omit<SuperChartCoreProps, 'chartProps'> &
showOverflow?: boolean;
/** Prop for popovercontainer ref */
parentRef?: RefObject<any>;
/** Prop for chart ref */
inputRef?: RefObject<any>;
/** Chart width */
height?: number | string;
/** Chart height */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
/** Type checking is disabled for this file due to reselect only supporting
* TS declarations for selectors with up to 12 arguments. */
// @ts-nocheck
import { RefObject } from 'react';
import { createSelector } from 'reselect';
import {
AppSection,
Expand Down Expand Up @@ -90,6 +91,8 @@ export interface ChartPropsConfig {
appSection?: AppSection;
/** is the chart refreshing its contents */
isRefreshing?: boolean;
/** chart ref */
inputRef?: RefObject<any>;
}

const DEFAULT_WIDTH = 800;
Expand Down Expand Up @@ -128,6 +131,8 @@ export default class ChartProps<FormData extends RawFormData = RawFormData> {

isRefreshing?: boolean;

inputRef?: RefObject<any>;

constructor(config: ChartPropsConfig & { formData?: FormData } = {}) {
const {
annotationData = {},
Expand All @@ -143,6 +148,7 @@ export default class ChartProps<FormData extends RawFormData = RawFormData> {
height = DEFAULT_HEIGHT,
appSection,
isRefreshing,
inputRef,
} = config;
this.width = width;
this.height = height;
Expand All @@ -159,6 +165,7 @@ export default class ChartProps<FormData extends RawFormData = RawFormData> {
this.behaviors = behaviors;
this.appSection = appSection;
this.isRefreshing = isRefreshing;
this.inputRef = inputRef;
}
}

Expand All @@ -178,6 +185,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
input => input.behaviors,
input => input.appSection,
input => input.isRefreshing,
input => input.inputRef,
(
annotationData,
datasource,
Expand All @@ -192,6 +200,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
behaviors,
appSection,
isRefreshing,
inputRef,
) =>
new ChartProps({
annotationData,
Expand All @@ -207,6 +216,7 @@ ChartProps.createSelector = function create(): ChartPropsSelector {
behaviors,
appSection,
isRefreshing,
inputRef,
}),
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export type FilterSets = {
[filtersSetId: string]: FilterSet;
};

export interface Filter {
export type Filter = {
cascadeParentIds: string[];
defaultDataMask: DataMask;
id: string; // randomly generated at filter creation
Expand All @@ -90,19 +90,31 @@ export interface Filter {
chartsInScope?: number[];
type: typeof NativeFilterType.NATIVE_FILTER;
description: string;
}
};

export interface Divider {
export type Divider = Partial<Omit<Filter, 'id' | 'type'>> & {
id: string;
title: string;
description: string;
type: typeof NativeFilterType.DIVIDER;
};

export function isNativeFilter(
filterElement: Filter | Divider,
): filterElement is Filter {
return filterElement.type === NativeFilterType.NATIVE_FILTER;
}

export function isFilterDivider(
filterElement: Filter | Divider,
): filterElement is Divider {
return filterElement.type === NativeFilterType.DIVIDER;
}

export type FilterConfiguration = Array<Filter | Divider>;

export type Filters = {
[filterId: string]: Filter;
[filterId: string]: Filter | Divider;
};

export type NativeFiltersState = {
Expand Down
4 changes: 2 additions & 2 deletions superset-frontend/spec/fixtures/mockNativeFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ export const mockQueryDataForCountries = [
export const buildNativeFilter = (
id: string,
name: string,
parents: string[],
dependencies: string[],
) => ({
id,
controlValues: {
Expand All @@ -481,7 +481,7 @@ export const buildNativeFilter = (
filterState: {},
ownState: {},
},
cascadeParentIds: parents,
cascadeParentIds: dependencies,
scope: {
rootPath: ['ROOT_ID'],
excluded: [],
Expand Down
63 changes: 37 additions & 26 deletions superset-frontend/src/components/Select/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* under the License.
*/
import React, {
forwardRef,
ReactElement,
ReactNode,
RefObject,
Expand Down Expand Up @@ -59,6 +60,7 @@ type PickedSelectProps = Pick<
| 'onClear'
| 'onFocus'
| 'onBlur'
| 'onDropdownVisibleChange'
| 'placeholder'
| 'showSearch'
| 'value'
Expand Down Expand Up @@ -264,31 +266,35 @@ const getQueryCacheKey = (value: string, page: number, pageSize: number) =>
* Each of the categories come with different abilities. For a comprehensive guide please refer to
* the storybook in src/components/Select/Select.stories.tsx.
*/
const Select = ({
allowNewOptions = false,
ariaLabel,
fetchOnlyOnSearch,
filterOption = true,
header = null,
invertSelection = false,
labelInValue = false,
lazyLoading = true,
loading,
mode = 'single',
name,
notFoundContent,
onError,
onChange,
onClear,
optionFilterProps = ['label', 'value'],
options,
pageSize = DEFAULT_PAGE_SIZE,
placeholder = t('Select ...'),
showSearch = true,
sortComparator = defaultSortComparator,
value,
...props
}: SelectProps) => {
const Select = (
{
allowNewOptions = false,
ariaLabel,
fetchOnlyOnSearch,
filterOption = true,
header = null,
invertSelection = false,
labelInValue = false,
lazyLoading = true,
loading,
mode = 'single',
name,
notFoundContent,
onError,
onChange,
onClear,
onDropdownVisibleChange,
optionFilterProps = ['label', 'value'],
options,
pageSize = DEFAULT_PAGE_SIZE,
placeholder = t('Select ...'),
showSearch = true,
sortComparator = defaultSortComparator,
value,
...props
}: SelectProps,
ref: RefObject<HTMLInputElement>,
) => {
const isAsync = typeof options === 'function';
const isSingleMode = mode === 'single';
const shouldShowSearch = isAsync || allowNewOptions ? true : showSearch;
Expand Down Expand Up @@ -616,6 +622,10 @@ const Select = ({
if (!isSingleMode && isDropdownVisible) {
handleTopOptions(selectValue);
}

if (onDropdownVisibleChange) {
onDropdownVisibleChange(isDropdownVisible);
}
};

const dropdownRender = (
Expand Down Expand Up @@ -759,6 +769,7 @@ const Select = ({
<StyledCheckOutlined iconSize="m" />
)
}
ref={ref}
{...props}
>
{shouldUseChildrenOptions &&
Expand All @@ -779,4 +790,4 @@ const Select = ({
);
};

export default Select;
export default forwardRef(Select);
Loading

0 comments on commit 06e1e42

Please sign in to comment.