mirror of https://github.com/apache/superset.git
fix: Cancel alert is not appearing to all native filters modal fields (#15925)
This commit is contained in:
parent
eb78f4332f
commit
cc704dd53a
|
@ -119,22 +119,12 @@ describe('FiltersConfigModal', () => {
|
||||||
expect(onCancel.mock.calls).toHaveLength(1);
|
expect(onCancel.mock.calls).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows correct alert message for an unsaved filter', async () => {
|
it('shows correct alert message for unsaved filters', async () => {
|
||||||
addFilter();
|
addFilter();
|
||||||
await clickCancel();
|
await clickCancel();
|
||||||
expect(onCancel.mock.calls).toHaveLength(0);
|
expect(onCancel.mock.calls).toHaveLength(0);
|
||||||
expect(wrapper.find(Alert).text()).toContain(
|
expect(wrapper.find(Alert).text()).toContain(
|
||||||
'Are you sure you want to cancel? "New filter" will not be saved.',
|
'There are unsaved changes.',
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('shows correct alert message for 2 unsaved filters', async () => {
|
|
||||||
addFilter();
|
|
||||||
addFilter();
|
|
||||||
await clickCancel();
|
|
||||||
expect(onCancel.mock.calls).toHaveLength(0);
|
|
||||||
expect(wrapper.find(Alert).text()).toContain(
|
|
||||||
'Are you sure you want to cancel? "New filter" and "New filter" will not be saved.',
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,6 +44,7 @@ import React, {
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
import { FormItem } from 'src/components/Form';
|
import { FormItem } from 'src/components/Form';
|
||||||
import { Input } from 'src/common/components';
|
import { Input } from 'src/common/components';
|
||||||
import { Select } from 'src/components';
|
import { Select } from 'src/components';
|
||||||
|
@ -525,9 +526,21 @@ const FiltersConfigForm = (
|
||||||
showDataset,
|
showDataset,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const formChanged = useCallback(() => {
|
||||||
|
form.setFields([
|
||||||
|
{
|
||||||
|
name: 'changed',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}, [form]);
|
||||||
|
|
||||||
const updateFormValues = useCallback(
|
const updateFormValues = useCallback(
|
||||||
(values: any) => setNativeFilterFieldValues(form, filterId, values),
|
(values: any) => {
|
||||||
[filterId, form],
|
setNativeFilterFieldValues(form, filterId, values);
|
||||||
|
formChanged();
|
||||||
|
},
|
||||||
|
[filterId, form, formChanged],
|
||||||
);
|
);
|
||||||
|
|
||||||
const parentFilterOptions = parentFilters.map(filter => ({
|
const parentFilterOptions = parentFilters.map(filter => ({
|
||||||
|
@ -589,6 +602,11 @@ const FiltersConfigForm = (
|
||||||
formFilter?.filterType === 'filter_select' ||
|
formFilter?.filterType === 'filter_select' ||
|
||||||
formFilter?.filterType === 'filter_range';
|
formFilter?.filterType === 'filter_range';
|
||||||
|
|
||||||
|
const initialDefaultValue =
|
||||||
|
formFilter.filterType === filterToEdit?.filterType
|
||||||
|
? filterToEdit?.defaultDataMask
|
||||||
|
: null;
|
||||||
|
|
||||||
const preFilterValidator = () => {
|
const preFilterValidator = () => {
|
||||||
if (hasTimeRange || hasAdhoc) {
|
if (hasTimeRange || hasAdhoc) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
@ -784,16 +802,15 @@ const FiltersConfigForm = (
|
||||||
disabled={isRequired || defaultToFirstItem}
|
disabled={isRequired || defaultToFirstItem}
|
||||||
tooltip={defaultValueTooltip}
|
tooltip={defaultValueTooltip}
|
||||||
checked={hasDefaultValue}
|
checked={hasDefaultValue}
|
||||||
onChange={value => setHasDefaultValue(value)}
|
onChange={value => {
|
||||||
|
setHasDefaultValue(value);
|
||||||
|
formChanged();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{formFilter.filterType && (
|
{formFilter.filterType && (
|
||||||
<StyledRowSubFormItem
|
<StyledRowSubFormItem
|
||||||
name={['filters', filterId, 'defaultDataMask']}
|
name={['filters', filterId, 'defaultDataMask']}
|
||||||
initialValue={
|
initialValue={initialDefaultValue}
|
||||||
formFilter.filterType === filterToEdit?.filterType
|
|
||||||
? filterToEdit?.defaultDataMask
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
data-test="default-input"
|
data-test="default-input"
|
||||||
label={<StyledLabel>{t('Default Value')}</StyledLabel>}
|
label={<StyledLabel>{t('Default Value')}</StyledLabel>}
|
||||||
required={hasDefaultValue}
|
required={hasDefaultValue}
|
||||||
|
@ -820,6 +837,14 @@ const FiltersConfigForm = (
|
||||||
<DefaultValueContainer>
|
<DefaultValueContainer>
|
||||||
<DefaultValue
|
<DefaultValue
|
||||||
setDataMask={dataMask => {
|
setDataMask={dataMask => {
|
||||||
|
if (
|
||||||
|
!isEqual(
|
||||||
|
initialDefaultValue?.filterState?.value,
|
||||||
|
dataMask?.filterState?.value,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
formChanged();
|
||||||
|
}
|
||||||
setNativeFilterFieldValues(form, filterId, {
|
setNativeFilterFieldValues(form, filterId, {
|
||||||
defaultDataMask: dataMask,
|
defaultDataMask: dataMask,
|
||||||
});
|
});
|
||||||
|
@ -862,6 +887,7 @@ const FiltersConfigForm = (
|
||||||
title={t('Filter is hierarchical')}
|
title={t('Filter is hierarchical')}
|
||||||
initialValue={hasParentFilter}
|
initialValue={hasParentFilter}
|
||||||
onChange={checked => {
|
onChange={checked => {
|
||||||
|
formChanged();
|
||||||
if (checked) {
|
if (checked) {
|
||||||
// execute after render
|
// execute after render
|
||||||
setTimeout(
|
setTimeout(
|
||||||
|
@ -900,6 +926,7 @@ const FiltersConfigForm = (
|
||||||
title={t('Pre-filter available values')}
|
title={t('Pre-filter available values')}
|
||||||
initialValue={hasPreFilter}
|
initialValue={hasPreFilter}
|
||||||
onChange={checked => {
|
onChange={checked => {
|
||||||
|
formChanged();
|
||||||
if (checked) {
|
if (checked) {
|
||||||
validatePreFilter();
|
validatePreFilter();
|
||||||
}
|
}
|
||||||
|
@ -1000,7 +1027,10 @@ const FiltersConfigForm = (
|
||||||
{formFilter?.filterType !== 'filter_range' && (
|
{formFilter?.filterType !== 'filter_range' && (
|
||||||
<CollapsibleControl
|
<CollapsibleControl
|
||||||
title={t('Sort filter values')}
|
title={t('Sort filter values')}
|
||||||
onChange={checked => onSortChanged(checked || undefined)}
|
onChange={checked => {
|
||||||
|
onSortChanged(checked || undefined);
|
||||||
|
formChanged();
|
||||||
|
}}
|
||||||
initialValue={hasSorting}
|
initialValue={hasSorting}
|
||||||
>
|
>
|
||||||
<StyledRowFormItem
|
<StyledRowFormItem
|
||||||
|
|
|
@ -171,7 +171,7 @@ export function FiltersConfigModal({
|
||||||
setCurrentFilterId(initialCurrentFilterId);
|
setCurrentFilterId(initialCurrentFilterId);
|
||||||
setRemovedFilters({});
|
setRemovedFilters({});
|
||||||
setSaveAlertVisible(false);
|
setSaveAlertVisible(false);
|
||||||
setFormValues({ filters: {} });
|
setFormValues({ filters: {}, changed: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFilterTitle = (id: string) =>
|
const getFilterTitle = (id: string) =>
|
||||||
|
@ -212,6 +212,7 @@ export function FiltersConfigModal({
|
||||||
onSave,
|
onSave,
|
||||||
values,
|
values,
|
||||||
)();
|
)();
|
||||||
|
resetForm();
|
||||||
} else {
|
} else {
|
||||||
configFormRef.current.changeTab('configuration');
|
configFormRef.current.changeTab('configuration');
|
||||||
}
|
}
|
||||||
|
@ -223,7 +224,8 @@ export function FiltersConfigModal({
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
if (unsavedFiltersIds.length > 0) {
|
const changed = form.getFieldValue('changed');
|
||||||
|
if (unsavedFiltersIds.length > 0 || form.isFieldsTouched() || changed) {
|
||||||
setSaveAlertVisible(true);
|
setSaveAlertVisible(true);
|
||||||
} else {
|
} else {
|
||||||
handleConfirmCancel();
|
handleConfirmCancel();
|
||||||
|
@ -245,10 +247,8 @@ export function FiltersConfigModal({
|
||||||
<Footer
|
<Footer
|
||||||
onDismiss={() => setSaveAlertVisible(false)}
|
onDismiss={() => setSaveAlertVisible(false)}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
getFilterTitle={getFilterTitle}
|
|
||||||
handleSave={handleSave}
|
handleSave={handleSave}
|
||||||
saveAlertVisible={saveAlertVisible}
|
saveAlertVisible={saveAlertVisible}
|
||||||
unsavedFiltersIds={unsavedFiltersIds}
|
|
||||||
onConfirmCancel={handleConfirmCancel}
|
onConfirmCancel={handleConfirmCancel}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,6 @@ type FooterProps = {
|
||||||
onConfirmCancel: OnClickHandler;
|
onConfirmCancel: OnClickHandler;
|
||||||
onDismiss: OnClickHandler;
|
onDismiss: OnClickHandler;
|
||||||
saveAlertVisible: boolean;
|
saveAlertVisible: boolean;
|
||||||
getFilterTitle: (id: string) => string;
|
|
||||||
unsavedFiltersIds: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Footer: FC<FooterProps> = ({
|
const Footer: FC<FooterProps> = ({
|
||||||
|
@ -36,38 +34,17 @@ const Footer: FC<FooterProps> = ({
|
||||||
handleSave,
|
handleSave,
|
||||||
onDismiss,
|
onDismiss,
|
||||||
onConfirmCancel,
|
onConfirmCancel,
|
||||||
getFilterTitle,
|
|
||||||
unsavedFiltersIds,
|
|
||||||
saveAlertVisible,
|
saveAlertVisible,
|
||||||
}) => {
|
}) => {
|
||||||
const getUnsavedFilterNames = (): string => {
|
|
||||||
const unsavedFiltersNames = unsavedFiltersIds.map(
|
|
||||||
id => `"${getFilterTitle(id)}"`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (unsavedFiltersNames.length === 0) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unsavedFiltersNames.length === 1) {
|
|
||||||
return unsavedFiltersNames[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastFilter = unsavedFiltersNames.pop();
|
|
||||||
|
|
||||||
return `${unsavedFiltersNames.join(', ')} ${t('and')} ${lastFilter}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (saveAlertVisible) {
|
if (saveAlertVisible) {
|
||||||
return (
|
return (
|
||||||
<CancelConfirmationAlert
|
<CancelConfirmationAlert
|
||||||
key="cancel-confirm"
|
key="cancel-confirm"
|
||||||
title={`${unsavedFiltersIds.length} ${t('unsaved filters')}`}
|
title={t('There are unsaved changes.')}
|
||||||
onConfirm={onConfirmCancel}
|
onConfirm={onConfirmCancel}
|
||||||
onDismiss={onDismiss}
|
onDismiss={onDismiss}
|
||||||
>
|
>
|
||||||
{t(`Are you sure you want to cancel?`)} {getUnsavedFilterNames()}{' '}
|
{t(`Are you sure you want to cancel?`)}
|
||||||
{t(`will not be saved.`)}
|
|
||||||
</CancelConfirmationAlert>
|
</CancelConfirmationAlert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ export interface NativeFiltersFormItem {
|
||||||
|
|
||||||
export interface NativeFiltersForm {
|
export interface NativeFiltersForm {
|
||||||
filters: Record<string, NativeFiltersFormItem>;
|
filters: Record<string, NativeFiltersFormItem>;
|
||||||
|
changed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FilterRemoval =
|
export type FilterRemoval =
|
||||||
|
|
Loading…
Reference in New Issue