mirror of
https://github.com/apache/superset.git
synced 2024-09-12 00:29:39 -04:00
fix: Prevents last temporal filter removal (#22982)
This commit is contained in:
parent
32d497785a
commit
b63ea6386f
@ -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.`,
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user