You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2021/05/20 16:52:38 UTC
[superset] branch master updated: chore: Improves the native
filters UI/UX - iteration 1 (#14714)
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 d924223 chore: Improves the native filters UI/UX - iteration 1 (#14714)
d924223 is described below
commit d92422395013d7741a2052a8e93cb743f2dcafa5
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Thu May 20 13:52:01 2021 -0300
chore: Improves the native filters UI/UX - iteration 1 (#14714)
---
.../FilterScope/FilterScope.test.tsx | 69 +--
.../FiltersConfigForm/FilterScope/FilterScope.tsx | 1 -
.../FiltersConfigForm/FiltersConfigForm.tsx | 477 +++++++++++----------
.../FiltersConfigModal/FiltersConfigModal.tsx | 1 +
.../Footer/CancelConfirmationAlert.tsx | 17 +-
5 files changed, 306 insertions(+), 259 deletions(-)
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope.test.tsx
index ab19250..e9c35d1 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope.test.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope.test.tsx
@@ -18,7 +18,12 @@
*/
import React from 'react';
import { Provider } from 'react-redux';
-import { render, screen, fireEvent } from 'spec/helpers/testing-library';
+import {
+ render,
+ screen,
+ fireEvent,
+ waitFor,
+} from 'spec/helpers/testing-library';
import { mockStoreWithChartsInTabsAndRoot } from 'spec/fixtures/mockStore';
import { Form, FormInstance } from 'src/common/components';
import { NativeFiltersForm } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/types';
@@ -34,7 +39,7 @@ describe('FilterScope', () => {
save,
};
- const MockModal = ({ scope }: { scope: object | undefined }) => {
+ const MockModal = ({ scope }: { scope?: object }) => {
const [newForm] = Form.useForm<NativeFiltersForm>();
form = newForm;
if (scope) {
@@ -55,56 +60,66 @@ describe('FilterScope', () => {
);
};
- const getWrapper = (scope?: object) => {
- render(<MockModal scope={scope} />);
- };
-
const getTreeSwitcher = (order = 0) =>
document.querySelectorAll('.ant-tree-switcher')[order];
it('renders "apply to all" filter scope', () => {
- getWrapper();
- expect(screen.queryByRole('tree')).toBe(null);
+ render(<MockModal />);
+ expect(screen.queryByRole('tree')).not.toBeInTheDocument();
});
- it('select tree values with 1 excluded', () => {
- getWrapper();
+ it('select tree values with 1 excluded', async () => {
+ render(<MockModal />);
+ fireEvent.click(screen.getByText('Scoping'));
fireEvent.click(screen.getByLabelText('Apply to specific panels'));
expect(screen.getByRole('tree')).not.toBe(null);
fireEvent.click(getTreeSwitcher(2));
fireEvent.click(screen.getByText('CHART_ID2'));
- expect(form.getFieldValue('filters')?.[mockedProps.filterId].scope).toEqual(
- {
+ await waitFor(() =>
+ expect(
+ form.getFieldValue('filters')?.[mockedProps.filterId].scope,
+ ).toEqual({
excluded: [20],
rootPath: ['ROOT_ID'],
- },
+ }),
);
});
- it('select 1 value only', () => {
- getWrapper();
+ it('select 1 value only', async () => {
+ render(<MockModal />);
+ fireEvent.click(screen.getByText('Scoping'));
fireEvent.click(screen.getByLabelText('Apply to specific panels'));
expect(screen.getByRole('tree')).not.toBe(null);
fireEvent.click(getTreeSwitcher(2));
fireEvent.click(screen.getByText('CHART_ID2'));
fireEvent.click(screen.getByText('tab1'));
- expect(form.getFieldValue('filters')?.[mockedProps.filterId].scope).toEqual(
- {
+ await waitFor(() =>
+ expect(
+ form.getFieldValue('filters')?.[mockedProps.filterId].scope,
+ ).toEqual({
excluded: [18, 20],
rootPath: ['ROOT_ID'],
- },
+ }),
);
});
- it('correct init tree with values', () => {
- getWrapper({
- rootPath: ['TAB_ID'],
- excluded: [],
- });
- fireEvent.click(screen.getByLabelText('Apply to specific panels'));
- expect(screen.getByRole('tree')).not.toBe(null);
- expect(document.querySelectorAll('.ant-tree-checkbox-checked').length).toBe(
- 1,
+ it('correct init tree with values', async () => {
+ render(
+ <MockModal
+ scope={{
+ rootPath: ['TAB_ID'],
+ excluded: [],
+ }}
+ />,
);
+ fireEvent.click(screen.getByText('Scoping'));
+ fireEvent.click(screen.getByLabelText('Apply to specific panels'));
+
+ await waitFor(() => {
+ expect(screen.getByRole('tree')).toBeInTheDocument();
+ expect(
+ document.querySelectorAll('.ant-tree-checkbox-checked').length,
+ ).toBe(1);
+ });
});
});
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope.tsx
index 58d6434..e8ffde2 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/FilterScope.tsx
@@ -64,7 +64,6 @@ const FilterScope: FC<FilterScopeProps> = ({
return (
<Wrapper>
- <Typography.Title level={5}>{t('Scoping')}</Typography.Title>
<CleanFormItem
name={[...pathToFormValue, 'scoping']}
initialValue={initialScoping}
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 fe5d048..47a0637 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -34,7 +34,7 @@ import {
import { FormInstance } from 'antd/lib/form';
import React, { useCallback, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
-import { Checkbox, Form, Input, Typography } from 'src/common/components';
+import { Checkbox, Form, Input } from 'src/common/components';
import { Select } from 'src/components/Select';
import SupersetResourceSelect, {
cachedSupersetGet,
@@ -48,6 +48,7 @@ import Button from 'src/components/Button';
import { getChartDataRequest } from 'src/chart/chartAction';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { waitForAsyncData } from 'src/middleware/asyncEvent';
+import Tabs from 'src/components/Tabs';
import { ColumnSelect } from './ColumnSelect';
import { NativeFiltersForm } from '../types';
import {
@@ -67,6 +68,8 @@ import {
getFiltersConfigModalTestId,
} from '../FiltersConfigModal';
+const { TabPane } = Tabs;
+
const StyledContainer = styled.div`
display: flex;
flex-direction: row-reverse;
@@ -92,6 +95,17 @@ const CleanFormItem = styled(Form.Item)`
margin-bottom: 0;
`;
+const FilterTabs = {
+ configuration: {
+ key: 'configuration',
+ name: t('Configuration'),
+ },
+ scoping: {
+ key: 'scoping',
+ name: t('Scoping'),
+ },
+};
+
export interface FiltersConfigFormProps {
filterId: string;
filterToEdit?: Filter;
@@ -264,254 +278,271 @@ export const FiltersConfigForm: React.FC<FiltersConfigFormProps> = ({
return (
<>
- <Typography.Title level={5}>{t('Settings')}</Typography.Title>
- <StyledContainer>
- <StyledFormItem
- name={['filters', filterId, 'name']}
- label={<StyledLabel>{t('Filter name')}</StyledLabel>}
- initialValue={filterToEdit?.name}
- rules={[{ required: !removed, message: t('Name is required') }]}
+ <Tabs defaultActiveKey={FilterTabs.configuration.key} centered>
+ <TabPane
+ tab={FilterTabs.configuration.name}
+ key={FilterTabs.configuration.key}
+ forceRender
>
- <Input {...getFiltersConfigModalTestId('name-input')} />
- </StyledFormItem>
- <StyledFormItem
- name={['filters', filterId, 'filterType']}
- rules={[{ required: !removed, message: t('Name is required') }]}
- initialValue={filterToEdit?.filterType || 'filter_select'}
- label={<StyledLabel>{t('Filter Type')}</StyledLabel>}
- {...getFiltersConfigModalTestId('filter-type')}
- >
- <Select
- options={nativeFilterVizTypes.map(filterType => ({
- value: filterType,
- // @ts-ignore
- label: nativeFilterItems[filterType]?.value.name,
- }))}
- onChange={({ value }: { value: string }) => {
- setNativeFilterFieldValues(form, filterId, {
- filterType: value,
- defaultDataMask: null,
- });
- forceUpdate();
- }}
- />
- </StyledFormItem>
- </StyledContainer>
- {hasDataset && (
- <>
- <StyledFormItem
- name={['filters', filterId, 'dataset']}
- initialValue={{ value: initialDatasetId }}
- label={<StyledLabel>{t('Dataset')}</StyledLabel>}
- rules={[{ required: !removed, message: t('Dataset is required') }]}
- {...getFiltersConfigModalTestId('datasource-input')}
- >
- <SupersetResourceSelect
- initialId={initialDatasetId}
- resource="dataset"
- searchColumn="table_name"
- transformItem={datasetToSelectOption}
- isMulti={false}
- onError={onDatasetSelectError}
- defaultOptions={Object.values(loadedDatasets).map(
- datasetToSelectOption,
- )}
- onChange={e => {
- // We need reset column when dataset changed
- if (datasetId && e?.value !== datasetId) {
- setNativeFilterFieldValues(form, filterId, {
- defaultDataMask: null,
- column: null,
- });
- }
- forceUpdate();
- }}
- />
- </StyledFormItem>
- {hasColumn && (
+ <StyledContainer>
<StyledFormItem
- // don't show the column select unless we have a dataset
- // style={{ display: datasetId == null ? undefined : 'none' }}
- name={['filters', filterId, 'column']}
- initialValue={initColumn}
- label={<StyledLabel>{t('Column')}</StyledLabel>}
- rules={[{ required: !removed, message: t('Field is required') }]}
- data-test="field-input"
+ name={['filters', filterId, 'name']}
+ label={<StyledLabel>{t('Filter name')}</StyledLabel>}
+ initialValue={filterToEdit?.name}
+ rules={[{ required: !removed, message: t('Name is required') }]}
>
- <ColumnSelect
- form={form}
- filterId={filterId}
- datasetId={datasetId}
- onChange={() => {
- // We need reset default value when when column changed
+ <Input {...getFiltersConfigModalTestId('name-input')} />
+ </StyledFormItem>
+ <StyledFormItem
+ name={['filters', filterId, 'filterType']}
+ rules={[{ required: !removed, message: t('Name is required') }]}
+ initialValue={filterToEdit?.filterType || 'filter_select'}
+ label={<StyledLabel>{t('Filter Type')}</StyledLabel>}
+ {...getFiltersConfigModalTestId('filter-type')}
+ >
+ <Select
+ options={nativeFilterVizTypes.map(filterType => ({
+ value: filterType,
+ // @ts-ignore
+ label: nativeFilterItems[filterType]?.value.name,
+ }))}
+ onChange={({ value }: { value: string }) => {
setNativeFilterFieldValues(form, filterId, {
+ filterType: value,
defaultDataMask: null,
});
forceUpdate();
}}
/>
</StyledFormItem>
- )}
- {hasAdditionalFilters && (
+ </StyledContainer>
+ {hasDataset && (
<>
<StyledFormItem
- name={['filters', filterId, 'adhoc_filters']}
- initialValue={filterToEdit?.adhoc_filters}
+ name={['filters', filterId, 'dataset']}
+ initialValue={{ value: initialDatasetId }}
+ label={<StyledLabel>{t('Dataset')}</StyledLabel>}
+ rules={[
+ { required: !removed, message: t('Dataset is required') },
+ ]}
+ {...getFiltersConfigModalTestId('datasource-input')}
>
- <AdhocFilterControl
- columns={
- datasetDetails?.columns?.filter(
- (c: ColumnMeta) => c.filterable,
- ) || []
- }
- savedMetrics={datasetDetails?.metrics || []}
- datasource={datasetDetails}
- onChange={(filters: AdhocFilter[]) => {
- setNativeFilterFieldValues(form, filterId, {
- adhoc_filters: filters,
- });
+ <SupersetResourceSelect
+ initialId={initialDatasetId}
+ resource="dataset"
+ searchColumn="table_name"
+ transformItem={datasetToSelectOption}
+ isMulti={false}
+ onError={onDatasetSelectError}
+ defaultOptions={Object.values(loadedDatasets).map(
+ datasetToSelectOption,
+ )}
+ onChange={e => {
+ // We need reset column when dataset changed
+ if (datasetId && e?.value !== datasetId) {
+ setNativeFilterFieldValues(form, filterId, {
+ defaultDataMask: null,
+ column: null,
+ });
+ }
forceUpdate();
}}
- label={<StyledLabel>{t('Adhoc filters')}</StyledLabel>}
/>
</StyledFormItem>
- <StyledFormItem
- name={['filters', filterId, 'time_range']}
- label={<StyledLabel>{t('Time range')}</StyledLabel>}
- initialValue={filterToEdit?.time_range || 'No filter'}
- >
- <DateFilterControl
- name="time_range"
- onChange={timeRange => {
+ {hasColumn && (
+ <StyledFormItem
+ // don't show the column select unless we have a dataset
+ // style={{ display: datasetId == null ? undefined : 'none' }}
+ name={['filters', filterId, 'column']}
+ initialValue={initColumn}
+ label={<StyledLabel>{t('Column')}</StyledLabel>}
+ rules={[
+ { required: !removed, message: t('Field is required') },
+ ]}
+ data-test="field-input"
+ >
+ <ColumnSelect
+ form={form}
+ filterId={filterId}
+ datasetId={datasetId}
+ onChange={() => {
+ // We need reset default value when when column changed
+ setNativeFilterFieldValues(form, filterId, {
+ defaultDataMask: null,
+ });
+ forceUpdate();
+ }}
+ />
+ </StyledFormItem>
+ )}
+ {hasAdditionalFilters && (
+ <>
+ <StyledFormItem
+ name={['filters', filterId, 'adhoc_filters']}
+ initialValue={filterToEdit?.adhoc_filters}
+ >
+ <AdhocFilterControl
+ columns={
+ datasetDetails?.columns?.filter(
+ (c: ColumnMeta) => c.filterable,
+ ) || []
+ }
+ savedMetrics={datasetDetails?.metrics || []}
+ datasource={datasetDetails}
+ onChange={(filters: AdhocFilter[]) => {
+ setNativeFilterFieldValues(form, filterId, {
+ adhoc_filters: filters,
+ });
+ forceUpdate();
+ }}
+ label={<StyledLabel>{t('Adhoc filters')}</StyledLabel>}
+ />
+ </StyledFormItem>
+ <StyledFormItem
+ name={['filters', filterId, 'time_range']}
+ label={<StyledLabel>{t('Time range')}</StyledLabel>}
+ initialValue={filterToEdit?.time_range || 'No filter'}
+ >
+ <DateFilterControl
+ name="time_range"
+ onChange={timeRange => {
+ setNativeFilterFieldValues(form, filterId, {
+ time_range: timeRange,
+ });
+ forceUpdate();
+ }}
+ />
+ </StyledFormItem>
+ </>
+ )}
+ </>
+ )}
+ {hasFilledDataset && (
+ <CleanFormItem
+ name={['filters', filterId, 'defaultValueFormData']}
+ hidden
+ initialValue={newFormData}
+ />
+ )}
+ <CleanFormItem
+ name={['filters', filterId, 'defaultValueQueriesData']}
+ hidden
+ initialValue={null}
+ />
+ {isCascadingFilter && (
+ <StyledFormItem
+ name={['filters', filterId, 'parentFilter']}
+ label={<StyledLabel>{t('Parent filter')}</StyledLabel>}
+ initialValue={parentFilterOptions.find(
+ ({ value }) => value === filterToEdit?.cascadeParentIds[0],
+ )}
+ data-test="parent-filter-input"
+ >
+ <Select
+ placeholder={t('None')}
+ options={parentFilterOptions}
+ isClearable
+ />
+ </StyledFormItem>
+ )}
+ <StyledContainer>
+ <StyledFormItem className="bottom" label={<StyledLabel />}>
+ {hasDataset && hasFilledDataset && (
+ <Button onClick={refreshHandler}>
+ {isDataDirty ? t('Populate') : t('Refresh')}
+ </Button>
+ )}
+ </StyledFormItem>
+ <StyledFormItem
+ name={['filters', filterId, 'defaultDataMask']}
+ initialValue={filterToEdit?.defaultDataMask}
+ data-test="default-input"
+ label={<StyledLabel>{t('Default Value')}</StyledLabel>}
+ >
+ {showDefaultValue ? (
+ <DefaultValue
+ setDataMask={dataMask => {
setNativeFilterFieldValues(form, filterId, {
- time_range: timeRange,
+ defaultDataMask: dataMask,
});
forceUpdate();
}}
+ filterId={filterId}
+ hasDataset={hasDataset}
+ form={form}
+ formData={newFormData}
/>
- </StyledFormItem>
- </>
- )}
- </>
- )}
- {hasFilledDataset && (
- <CleanFormItem
- name={['filters', filterId, 'defaultValueFormData']}
- hidden
- initialValue={newFormData}
- />
- )}
- <CleanFormItem
- name={['filters', filterId, 'defaultValueQueriesData']}
- hidden
- initialValue={null}
- />
- {isCascadingFilter && (
- <StyledFormItem
- name={['filters', filterId, 'parentFilter']}
- label={<StyledLabel>{t('Parent filter')}</StyledLabel>}
- initialValue={parentFilterOptions.find(
- ({ value }) => value === filterToEdit?.cascadeParentIds[0],
- )}
- data-test="parent-filter-input"
- >
- <Select
- placeholder={t('None')}
- options={parentFilterOptions}
- isClearable
+ ) : hasFilledDataset ? (
+ t('Click "Populate" to get "Default Value" ->')
+ ) : (
+ t('Fill all required fields to enable "Default Value"')
+ )}
+ </StyledFormItem>
+ </StyledContainer>
+ <StyledCheckboxFormItem
+ name={['filters', filterId, 'isInstant']}
+ initialValue={filterToEdit?.isInstant || false}
+ valuePropName="checked"
+ colon={false}
+ >
+ <Checkbox data-test="apply-changes-instantly-checkbox">
+ {t('Apply changes instantly')}
+ </Checkbox>
+ </StyledCheckboxFormItem>
+ <ControlItems
+ disabled={!showDefaultValue}
+ filterToEdit={filterToEdit}
+ formFilter={formFilter}
+ filterId={filterId}
+ form={form}
+ forceUpdate={forceUpdate}
/>
- </StyledFormItem>
- )}
- <StyledContainer>
- <StyledFormItem className="bottom" label={<StyledLabel />}>
- {hasDataset && hasFilledDataset && (
- <Button onClick={refreshHandler}>
- {isDataDirty ? t('Populate') : t('Refresh')}
- </Button>
- )}
- </StyledFormItem>
- <StyledFormItem
- name={['filters', filterId, 'defaultDataMask']}
- initialValue={filterToEdit?.defaultDataMask}
- data-test="default-input"
- label={<StyledLabel>{t('Default Value')}</StyledLabel>}
- >
- {showDefaultValue ? (
- <DefaultValue
- setDataMask={dataMask => {
- setNativeFilterFieldValues(form, filterId, {
- defaultDataMask: dataMask,
- });
- forceUpdate();
- }}
- filterId={filterId}
- hasDataset={hasDataset}
- form={form}
- formData={newFormData}
- />
- ) : hasFilledDataset ? (
- t('Click "Populate" to get "Default Value" ->')
- ) : (
- t('Fill all required fields to enable "Default Value"')
+ {hasMetrics && (
+ <StyledFormItem
+ // don't show the column select unless we have a dataset
+ // style={{ display: datasetId == null ? undefined : 'none' }}
+ 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>
)}
- </StyledFormItem>
- </StyledContainer>
- <StyledCheckboxFormItem
- name={['filters', filterId, 'isInstant']}
- initialValue={filterToEdit?.isInstant || false}
- valuePropName="checked"
- colon={false}
- >
- <Checkbox data-test="apply-changes-instantly-checkbox">
- {t('Apply changes instantly')}
- </Checkbox>
- </StyledCheckboxFormItem>
- <ControlItems
- disabled={!showDefaultValue}
- filterToEdit={filterToEdit}
- formFilter={formFilter}
- filterId={filterId}
- form={form}
- forceUpdate={forceUpdate}
- />
- {hasMetrics && (
- <StyledFormItem
- // don't show the column select unless we have a dataset
- // style={{ display: datasetId == null ? undefined : 'none' }}
- name={['filters', filterId, 'sortMetric']}
- initialValue={filterToEdit?.sortMetric}
- label={<StyledLabel>{t('Sort Metric')}</StyledLabel>}
- data-test="field-input"
+ </TabPane>
+ <TabPane
+ tab={FilterTabs.scoping.name}
+ key={FilterTabs.scoping.key}
+ forceRender
>
- <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();
- }
- }}
+ <FilterScope
+ updateFormValues={(values: any) =>
+ setNativeFilterFieldValues(form, filterId, values)
+ }
+ pathToFormValue={['filters', filterId]}
+ forceUpdate={forceUpdate}
+ scope={filterToEdit?.scope}
+ formScope={formFilter?.scope}
+ formScoping={formFilter?.scoping}
/>
- </StyledFormItem>
- )}
- <FilterScope
- updateFormValues={(values: any) =>
- setNativeFilterFieldValues(form, filterId, values)
- }
- pathToFormValue={['filters', filterId]}
- forceUpdate={forceUpdate}
- scope={filterToEdit?.scope}
- formScope={formFilter?.scope}
- formScoping={formFilter?.scoping}
- />
+ </TabPane>
+ </Tabs>
</>
);
};
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
index d19538c..4b25bd5 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.tsx
@@ -39,6 +39,7 @@ import { useOpenModal, useRemoveCurrentFilter } from './state';
export const StyledModalBody = styled.div`
display: flex;
+ height: 500px;
flex-direction: row;
.filters-list {
width: ${({ theme }) => theme.gridUnit * 50}px;
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/Footer/CancelConfirmationAlert.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/Footer/CancelConfirmationAlert.tsx
index 5e2e896..ba48f69 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/Footer/CancelConfirmationAlert.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/Footer/CancelConfirmationAlert.tsx
@@ -36,6 +36,7 @@ export function CancelConfirmationAlert({
}: ConfirmationAlertProps) {
return (
<Alert
+ closable={false}
type="warning"
key="alert"
message={title}
@@ -47,14 +48,6 @@ export function CancelConfirmationAlert({
action={
<div css={{ display: 'flex' }}>
<Button
- key="submit"
- buttonSize="small"
- buttonStyle="primary"
- onClick={onConfirm}
- >
- {t('Yes, cancel')}
- </Button>
- <Button
key="cancel"
buttonSize="small"
buttonStyle="secondary"
@@ -62,6 +55,14 @@ export function CancelConfirmationAlert({
>
{t('Keep editing')}
</Button>
+ <Button
+ key="submit"
+ buttonSize="small"
+ buttonStyle="primary"
+ onClick={onConfirm}
+ >
+ {t('Yes, cancel')}
+ </Button>
</div>
}
/>