This is an automated email from the ASF dual-hosted git repository.
villebro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new b6f00e6 chore: Improves the native filters UI/UX - iteration 6
(#14932)
b6f00e6 is described below
commit b6f00e69e156098e9e1569d11f0f7831bac1024f
Author: Michael S. Molina <[email protected]>
AuthorDate: Wed Jun 2 03:03:13 2021 -0300
chore: Improves the native filters UI/UX - iteration 6 (#14932)
---
.../FiltersConfigModal/FilterTabs.tsx | 20 ++-
.../FiltersConfigForm/FiltersConfigForm.tsx | 172 ++++++++++++---------
.../FiltersConfigForm/getControlItemsMap.test.tsx | 8 +-
.../FiltersConfigForm/getControlItemsMap.tsx | 15 +-
.../FiltersConfigModal/FiltersConfigModal.tsx | 6 +-
5 files changed, 137 insertions(+), 84 deletions(-)
diff --git
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterTabs.tsx
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterTabs.tsx
index ccb0e00..3d68796 100644
---
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterTabs.tsx
+++
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FilterTabs.tsx
@@ -24,7 +24,7 @@ import Icon from 'src/components/Icon';
import { FilterRemoval } from './types';
import { REMOVAL_DELAY_SECS } from './utils';
-export const FILTER_WIDTH = 200;
+export const FILTER_WIDTH = 180;
export const StyledSpan = styled.span`
cursor: pointer;
@@ -115,6 +115,7 @@ const FilterTabsContainer = styled(LineEditableTabs)`
padding-right: ${theme.gridUnit}px;
padding-bottom: ${theme.gridUnit * 3}px;
padding-left: ${theme.gridUnit * 3}px;
+ width: 270px;
}
// extra selector specificity:
@@ -185,6 +186,8 @@ const FilterTabs: FC<FilterTabsProps> = ({
children,
}) => (
<FilterTabsContainer
+ id="native-filters-tabs"
+ type="editable-card"
tabPosition="left"
onChange={onChange}
activeKey={currentFilterId}
@@ -193,7 +196,20 @@ const FilterTabs: FC<FilterTabsProps> = ({
tabBarExtraContent={{
left: <StyledHeader>{t('Filters')}</StyledHeader>,
right: (
- <StyledAddFilterBox onClick={() => onEdit('', 'add')}>
+ <StyledAddFilterBox
+ onClick={() => {
+ onEdit('', 'add');
+ setTimeout(() => {
+ const element = document.getElementById('native-filters-tabs');
+ if (element) {
+ const navList = element.getElementsByClassName(
+ 'ant-tabs-nav-list',
+ )[0];
+ navList.scrollTop = navList.scrollHeight;
+ }
+ }, 0);
+ }}
+ >
<PlusOutlined />{' '}
<span data-test="add-filter-button" aria-label="Add filter">
{t('Add filter')}
diff --git
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
index 90768e8..76fa33b 100644
---
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -229,6 +229,7 @@ const FILTERS_WITH_ADHOC_FILTERS = ['filter_select',
'filter_range'];
const BASIC_CONTROL_ITEMS = ['enableEmptyFilter', 'multiSelect'];
+// TODO: Rename the filter plugins and remove this mapping
const FILTER_TYPE_NAME_MAPPING = {
[t('Select filter')]: t('Value'),
[t('Range filter')]: t('Numerical range'),
@@ -254,7 +255,12 @@ const FiltersConfigForm = (
ref: React.RefObject<any>,
) => {
const [metrics, setMetrics] = useState<Metric[]>([]);
- const [activeTabKey, setActiveTabKey] = useState<string | undefined>();
+ const [activeTabKey, setActiveTabKey] = useState<string>(
+ FilterTabs.configuration.key,
+ );
+ const [activeFilterPanelKey, setActiveFilterPanelKey] = useState<
+ string | string[]
+ >(FilterPanels.basic.key);
const [hasDefaultValue, setHasDefaultValue] = useState(
!!filterToEdit?.defaultDataMask?.filterState?.value,
);
@@ -410,10 +416,6 @@ const FiltersConfigForm = (
[],
);
- if (removed) {
- return <RemovedFilter onClick={() => restoreFilter(filterId)} />;
- }
-
const parentFilterOptions = parentFilters.map(filter => ({
value: filter.id,
label: filter.title,
@@ -423,6 +425,14 @@ const FiltersConfigForm = (
({ value }) => value === filterToEdit?.cascadeParentIds[0],
);
+ const hasParentFilter = !!parentFilter;
+
+ const hasPreFilter =
+ !!filterToEdit?.adhoc_filters || !!filterToEdit?.time_range;
+
+ const hasSorting =
+ typeof filterToEdit?.controlValues?.sortAscending === 'boolean';
+
const showDefaultValue = !hasDataset || (!isDataDirty && hasFilledDataset);
const controlItems = formFilter
@@ -447,9 +457,27 @@ const FiltersConfigForm = (
forceUpdate();
};
+ let hasCheckedAdvancedControl = hasParentFilter || hasPreFilter ||
hasSorting;
+ if (!hasCheckedAdvancedControl) {
+ hasCheckedAdvancedControl = Object.keys(controlItems)
+ .filter(key => !BASIC_CONTROL_ITEMS.includes(key))
+ .some(key => controlItems[key].checked);
+ }
+
+ useEffect(() => {
+ const activeFilterPanelKey = [FilterPanels.basic.key];
+ if (hasCheckedAdvancedControl) {
+ activeFilterPanelKey.push(FilterPanels.advanced.key);
+ }
+ setActiveFilterPanelKey(activeFilterPanelKey);
+ }, [hasCheckedAdvancedControl]);
+
+ if (removed) {
+ return <RemovedFilter onClick={() => restoreFilter(filterId)} />;
+ }
+
return (
<StyledTabs
- defaultActiveKey={FilterTabs.configuration.key}
activeKey={activeTabKey}
onChange={activeKey => setActiveTabKey(activeKey)}
centered
@@ -559,7 +587,8 @@ const FiltersConfigForm = (
</StyledRowContainer>
)}
<StyledCollapse
- defaultActiveKey={FilterPanels.basic.key}
+ activeKey={activeFilterPanelKey}
+ onChange={key => setActiveFilterPanelKey(key)}
expandIconPosition="right"
>
<Collapse.Panel
@@ -630,7 +659,7 @@ const FiltersConfigForm = (
</CollapsibleControl>
{Object.keys(controlItems)
.filter(key => BASIC_CONTROL_ITEMS.includes(key))
- .map(key => controlItems[key])}
+ .map(key => controlItems[key].element)}
<StyledRowFormItem
name={['filters', filterId, 'isInstant']}
initialValue={filterToEdit?.isInstant || false}
@@ -650,7 +679,7 @@ const FiltersConfigForm = (
{isCascadingFilter && (
<CollapsibleControl
title={t('Filter is hierarchical')}
- checked={!!parentFilter}
+ checked={hasParentFilter}
onChange={checked => {
if (checked) {
// execute after render
@@ -687,13 +716,11 @@ const FiltersConfigForm = (
)}
{Object.keys(controlItems)
.filter(key => !BASIC_CONTROL_ITEMS.includes(key))
- .map(key => controlItems[key])}
+ .map(key => controlItems[key].element)}
{hasDataset && hasAdditionalFilters && (
<CollapsibleControl
title={t('Pre-filter available values')}
- checked={
- !!filterToEdit?.adhoc_filters || !!filterToEdit?.time_range
- }
+ checked={hasPreFilter}
onChange={checked => {
if (checked) {
// execute after render
@@ -757,72 +784,71 @@ const FiltersConfigForm = (
</StyledRowFormItem>
</CollapsibleControl>
)}
- <CollapsibleControl
- title={t('Sort filter values')}
- onChange={checked => onSortChanged(checked || undefined)}
- checked={
- typeof filterToEdit?.controlValues?.sortAscending ===
- 'boolean'
- }
- >
- <StyledRowContainer>
- <StyledFormItem
- name={[
- 'filters',
- filterId,
- 'controlValues',
- 'sortAscending',
- ]}
- initialValue={filterToEdit?.controlValues?.sortAscending}
- label={<StyledLabel>{t('Sort type')}</StyledLabel>}
- >
- <Select
- form={form}
- filterId={filterId}
- name="sortAscending"
- options={[
- {
- value: true,
- label: t('Sort ascending'),
- },
- {
- value: false,
- label: t('Sort descending'),
- },
- ]}
- onChange={({ value }: { value: boolean }) =>
- onSortChanged(value)
- }
- />
- </StyledFormItem>
- {hasMetrics && (
+ {formFilter?.filterType !== 'filter_range' && (
+ <CollapsibleControl
+ title={t('Sort filter values')}
+ onChange={checked => onSortChanged(checked || undefined)}
+ checked={hasSorting}
+ >
+ <StyledRowContainer>
<StyledFormItem
- name={['filters', filterId, 'sortMetric']}
- initialValue={filterToEdit?.sortMetric}
- label={<StyledLabel>{t('Sort Metric')}</StyledLabel>}
- data-test="field-input"
+ name={[
+ 'filters',
+ filterId,
+ 'controlValues',
+ 'sortAscending',
+ ]}
+ initialValue={filterToEdit?.controlValues?.sortAscending}
+ label={<StyledLabel>{t('Sort type')}</StyledLabel>}
>
- <SelectControl
+ <Select
form={form}
filterId={filterId}
- name="sortMetric"
- options={metrics.map((metric: Metric) => ({
- value: metric.metric_name,
- label: metric.verbose_name ?? metric.metric_name,
- }))}
- onChange={(value: string | null): void => {
- if (value !== undefined) {
- setNativeFilterFieldValues(form, filterId, {
- sortMetric: value,
- });
- forceUpdate();
- }
- }}
+ name="sortAscending"
+ options={[
+ {
+ value: true,
+ label: t('Sort ascending'),
+ },
+ {
+ value: false,
+ label: t('Sort descending'),
+ },
+ ]}
+ onChange={({ value }: { value: boolean }) =>
+ onSortChanged(value)
+ }
/>
</StyledFormItem>
- )}
- </StyledRowContainer>
- </CollapsibleControl>
+ {hasMetrics && (
+ <StyledFormItem
+ name={['filters', filterId, 'sortMetric']}
+ initialValue={filterToEdit?.sortMetric}
+ label={<StyledLabel>{t('Sort Metric')}</StyledLabel>}
+ data-test="field-input"
+ >
+ <SelectControl
+ form={form}
+ filterId={filterId}
+ name="sortMetric"
+ options={metrics.map((metric: Metric) => ({
+ value: metric.metric_name,
+ label: metric.verbose_name ?? metric.metric_name,
+ }))}
+ onChange={(value: string | null): void => {
+ if (value !== undefined) {
+ setNativeFilterFieldValues(form, filterId, {
+ sortMetric: value,
+ });
+ forceUpdate();
+ }
+ }}
+ />
+ </StyledFormItem>
+ )}
+ </StyledRowContainer>
+ </CollapsibleControl>
+ )}
</Collapse.Panel>
)}
</StyledCollapse>
diff --git
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx
index 0554f62..fcff22d 100644
---
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx
+++
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx
@@ -83,8 +83,12 @@ beforeEach(() => {
jest.clearAllMocks();
});
-function renderControlItems(controlItemsMap: {}): any {
- return render(<>{Object.values(controlItemsMap).map(value => value)}</>);
+function renderControlItems(
+ controlItemsMap: ReturnType<typeof getControlItemsMap>,
+) {
+ return render(
+ <>{Object.values(controlItemsMap).map(value => value.element)}</>,
+ );
}
test('Should render null when has no "formFilter"', () => {
diff --git
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx
index ae4541a..15e877e 100644
---
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx
+++
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.tsx
@@ -50,7 +50,10 @@ export default function getControlItemsMap({
const controlPanelRegistry = getChartControlPanelRegistry();
const controlItems =
getControlItems(controlPanelRegistry.get(filterType)) ?? [];
- const map = {};
+ const map: Record<
+ string,
+ { element: React.ReactNode; checked: boolean }
+ > = {};
controlItems
.filter(
@@ -59,6 +62,9 @@ export default function getControlItemsMap({
controlItem.name !== 'sortAscending',
)
.forEach(controlItem => {
+ const initialValue =
+ filterToEdit?.controlValues?.[controlItem.name] ??
+ controlItem?.config?.default;
const element = (
<Tooltip
key={controlItem.name}
@@ -72,10 +78,7 @@ export default function getControlItemsMap({
<StyledRowFormItem
key={controlItem.name}
name={['filters', filterId, 'controlValues', controlItem.name]}
- initialValue={
- filterToEdit?.controlValues?.[controlItem.name] ??
- controlItem?.config?.default
- }
+ initialValue={initialValue}
valuePropName="checked"
colon={false}
>
@@ -104,7 +107,7 @@ export default function getControlItemsMap({
</StyledRowFormItem>
</Tooltip>
);
- map[controlItem.name] = element;
+ map[controlItem.name] = { element, checked: initialValue };
});
return map;
}
diff --git
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
index 1a4bd38..c1f39c0 100644
---
a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
+++
b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
@@ -183,7 +183,11 @@ export function FiltersConfigModal({
filterIds
.filter(filterId => filterId !== id && !removedFilters[filterId])
.filter(filterId =>
- CASCADING_FILTERS.includes(formValues.filters[filterId]?.filterType),
+ CASCADING_FILTERS.includes(
+ formValues.filters[filterId]
+ ? formValues.filters[filterId].filterType
+ : filterConfigMap[filterId]?.filterType,
+ ),
)
.map(id => ({
id,