fix: Prevents last temporal filter removal (#22982)

This commit is contained in:
Michael S. Molina 2023-02-03 10:50:12 -05:00 committed by GitHub
parent 32d497785a
commit b63ea6386f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 69 deletions

View File

@ -46,6 +46,7 @@ import {
CustomControlItem, CustomControlItem,
Dataset, Dataset,
ExpandedControlItem, ExpandedControlItem,
isTemporalColumn,
sections, sections,
} from '@superset-ui/chart-controls'; } from '@superset-ui/chart-controls';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
@ -293,13 +294,17 @@ export const ControlPanelsContainer = (props: ControlPanelsContainerProps) => {
const previousXAxis = usePrevious(x_axis); const previousXAxis = usePrevious(x_axis);
useEffect(() => { useEffect(() => {
if (x_axis && x_axis !== previousXAxis) { if (
x_axis &&
x_axis !== previousXAxis &&
isTemporalColumn(x_axis, props.exploreState.datasource)
) {
const noFilter = const noFilter =
!adhoc_filters || !adhoc_filters ||
!adhoc_filters.find( !adhoc_filters.find(
filter => filter =>
filter.expressionType === 'SIMPLE' && filter.expressionType === 'SIMPLE' &&
filter.operator === 'TEMPORAL_RANGE' && filter.operator === Operators.TEMPORAL_RANGE &&
filter.subject === x_axis, filter.subject === x_axis,
); );
if (noFilter) { if (noFilter) {
@ -314,7 +319,7 @@ export const ControlPanelsContainer = (props: ControlPanelsContainerProps) => {
{ {
clause: 'WHERE', clause: 'WHERE',
subject: x_axis, subject: x_axis,
operator: 'TEMPORAL_RANGE', operator: Operators.TEMPORAL_RANGE,
comparator: defaultTimeFilter || NO_TIME_RANGE, comparator: defaultTimeFilter || NO_TIME_RANGE,
expressionType: 'SIMPLE', expressionType: 'SIMPLE',
}, },
@ -329,6 +334,7 @@ export const ControlPanelsContainer = (props: ControlPanelsContainerProps) => {
setControlValue, setControlValue,
defaultTimeFilter, defaultTimeFilter,
previousXAxis, previousXAxis,
props.exploreState.datasource,
]); ]);
useEffect(() => { useEffect(() => {
@ -482,28 +488,21 @@ export const ControlPanelsContainer = (props: ControlPanelsContainerProps) => {
: baseDescription; : baseDescription;
if (name === 'adhoc_filters') { if (name === 'adhoc_filters') {
restProps.confirmDeletion = { restProps.canDelete = (
triggerCondition: ( valueToBeDeleted: Record<string, any>,
valueToBeDeleted: Record<string, any>, values: Record<string, any>[],
values: Record<string, any>[], ) => {
) => { const isTemporalRange = (filter: Record<string, any>) =>
const isTemporalRange = (filter: Record<string, any>) => filter.operator === Operators.TEMPORAL_RANGE;
filter.operator === Operators.TEMPORAL_RANGE; if (isTemporalRange(valueToBeDeleted)) {
if (isTemporalRange(valueToBeDeleted)) { const count = values.filter(isTemporalRange).length;
const count = values.filter(isTemporalRange).length; if (count === 1) {
if (count < 2) { return t(
return true; `You cannot delete the last temporal filter as it's used for time range filters in dashboards.`,
} );
} }
return false; }
}, return true;
confirmationTitle: t(
'Are you sure you want to remove the last temporal filter?',
),
confirmationText: t(
`This filter is the last temporal filter. If you proceed,
this chart won't be affected by time range filters in dashboards.`,
),
}; };
} }

View File

@ -59,7 +59,7 @@ import AdhocFilterControl from '../FilterControl/AdhocFilterControl';
import DndAdhocFilterOption from './DndAdhocFilterOption'; import DndAdhocFilterOption from './DndAdhocFilterOption';
import { useDefaultTimeFilter } from '../DateFilterControl/utils'; import { useDefaultTimeFilter } from '../DateFilterControl/utils';
const { confirm } = Modal; const { warning } = Modal;
const EMPTY_OBJECT = {}; const EMPTY_OBJECT = {};
const DND_ACCEPTED_TYPES = [ const DND_ACCEPTED_TYPES = [
@ -78,14 +78,10 @@ export interface DndFilterSelectProps
savedMetrics: Metric[]; savedMetrics: Metric[];
selectedMetrics: QueryFormMetric[]; selectedMetrics: QueryFormMetric[];
datasource: Datasource; datasource: Datasource;
confirmDeletion?: { canDelete?: (
triggerCondition: ( valueToBeDeleted: OptionValueType,
valueToBeDeleted: OptionValueType, values: OptionValueType[],
values: OptionValueType[], ) => true | string;
) => boolean;
confirmationTitle: string;
confirmationText: string;
};
} }
const DndFilterSelect = (props: DndFilterSelectProps) => { const DndFilterSelect = (props: DndFilterSelectProps) => {
@ -93,7 +89,7 @@ const DndFilterSelect = (props: DndFilterSelectProps) => {
datasource, datasource,
onChange = () => {}, onChange = () => {},
name: controlName, name: controlName,
confirmDeletion, canDelete,
} = props; } = props;
const propsValues = Array.from(props.value ?? []); const propsValues = Array.from(props.value ?? []);
@ -217,23 +213,14 @@ const DndFilterSelect = (props: DndFilterSelectProps) => {
const onClickClose = useCallback( const onClickClose = useCallback(
(index: number) => { (index: number) => {
if (confirmDeletion) { const result = canDelete?.(values[index], values);
const { confirmationText, confirmationTitle, triggerCondition } = if (typeof result === 'string') {
confirmDeletion; warning({ title: t('Warning'), content: result });
if (triggerCondition(values[index], values)) { return;
confirm({
title: confirmationTitle,
content: confirmationText,
onOk() {
removeValue(index);
},
});
return;
}
} }
removeValue(index); removeValue(index);
}, },
[confirmDeletion, removeValue, values], [canDelete, removeValue, values],
); );
const onShiftOptions = useCallback( const onShiftOptions = useCallback(

View File

@ -52,7 +52,7 @@ import AdhocFilter, {
import adhocFilterType from 'src/explore/components/controls/FilterControl/adhocFilterType'; import adhocFilterType from 'src/explore/components/controls/FilterControl/adhocFilterType';
import columnType from 'src/explore/components/controls/FilterControl/columnType'; import columnType from 'src/explore/components/controls/FilterControl/columnType';
const { confirm } = Modal; const { warning } = Modal;
const selectedMetricType = PropTypes.oneOfType([ const selectedMetricType = PropTypes.oneOfType([
PropTypes.string, PropTypes.string,
@ -74,11 +74,7 @@ const propTypes = {
PropTypes.arrayOf(selectedMetricType), PropTypes.arrayOf(selectedMetricType),
]), ]),
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
confirmDeletion: PropTypes.shape({ canDelete: PropTypes.func,
triggerCondition: PropTypes.func,
confirmationTitle: PropTypes.string,
confirmationText: PropTypes.string,
}),
}; };
const defaultProps = { const defaultProps = {
@ -196,22 +192,12 @@ class AdhocFilterControl extends React.Component {
} }
onRemoveFilter(index) { onRemoveFilter(index) {
const { confirmDeletion } = this.props; const { canDelete } = this.props;
const { values } = this.state; const { values } = this.state;
const { removeFilter } = this; const result = canDelete?.(values[index], values);
if (confirmDeletion) { if (typeof result === 'string') {
const { confirmationText, confirmationTitle, triggerCondition } = warning({ title: t('Warning'), content: result });
confirmDeletion; return;
if (triggerCondition(values[index], values)) {
confirm({
title: confirmationTitle,
content: confirmationText,
onOk() {
removeFilter(index);
},
});
return;
}
} }
this.removeFilter(index); this.removeFilter(index);
} }