This is an automated email from the ASF dual-hosted git repository.
kgabryje 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 4ecfce98f6 feat(explore): Clear temporal filter value (#27788)
4ecfce98f6 is described below
commit 4ecfce98f6ba79361a78f2fd6c61f9428faceb79
Author: Kamil Gabryjelski <[email protected]>
AuthorDate: Tue Apr 9 11:27:21 2024 +0200
feat(explore): Clear temporal filter value (#27788)
---
.../explore/components/ControlPanelsContainer.tsx | 27 ++++++--
.../DndFilterSelect.test.tsx | 77 +++++++++++++++++++++-
.../DndColumnSelectControl/DndFilterSelect.tsx | 4 +-
3 files changed, 101 insertions(+), 7 deletions(-)
diff --git
a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
index cdc9259c0a..d47c1abaf8 100644
--- a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
+++ b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx
@@ -52,7 +52,7 @@ import {
} from '@superset-ui/chart-controls';
import { useSelector } from 'react-redux';
import { rgba } from 'emotion-rgba';
-import { kebabCase } from 'lodash';
+import { kebabCase, isEqual } from 'lodash';
import Collapse from 'src/components/Collapse';
import Tabs from 'src/components/Tabs';
@@ -283,7 +283,7 @@ export const ControlPanelsContainer = (props:
ControlPanelsContainerProps) => {
>(state => state.explore.controlsTransferred);
const defaultTimeFilter = useSelector<ExplorePageState>(
- state => state.common?.conf?.DEFAULT_TIME_FILTER,
+ state => state.common?.conf?.DEFAULT_TIME_FILTER || NO_TIME_RANGE,
);
const { form_data, actions } = props;
@@ -317,7 +317,7 @@ export const ControlPanelsContainer = (props:
ControlPanelsContainerProps) => {
clause: Clauses.Where,
subject: x_axis,
operator: Operators.TemporalRange,
- comparator: defaultTimeFilter || NO_TIME_RANGE,
+ comparator: defaultTimeFilter,
expressionType: 'SIMPLE',
},
]);
@@ -494,9 +494,26 @@ export const ControlPanelsContainer = (props:
ControlPanelsContainerProps) => {
if (!controls?.time_range?.value && isTemporalRange(valueToBeDeleted))
{
const count = values.filter(isTemporalRange).length;
if (count === 1) {
- return t(
- `You cannot delete the last temporal filter as it's used for
time range filters in dashboards.`,
+ // if temporal filter's value is "No filter", prevent deletion
+ // otherwise reset the value to "No filter"
+ if (valueToBeDeleted.comparator === defaultTimeFilter) {
+ return t(
+ `You cannot delete the last temporal filter as it's used for
time range filters in dashboards.`,
+ );
+ }
+ props.actions.setControlValue(
+ name,
+ values.map(val => {
+ if (isEqual(val, valueToBeDeleted)) {
+ return {
+ ...val,
+ comparator: defaultTimeFilter,
+ };
+ }
+ return val;
+ }),
);
+ return false;
}
}
return true;
diff --git
a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.test.tsx
b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.test.tsx
index 6be82472d9..035cacd810 100644
---
a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.test.tsx
+++
b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.test.tsx
@@ -37,6 +37,7 @@ import {
import type { AsyncAceEditorProps } from 'src/components/AsyncAceEditor';
import AdhocMetric from
'src/explore/components/controls/MetricControl/AdhocMetric';
import AdhocFilter from
'src/explore/components/controls/FilterControl/AdhocFilter';
+import { Operators } from 'src/explore/constants';
import {
DndFilterSelect,
DndFilterSelectProps,
@@ -78,11 +79,13 @@ function setup({
formData = baseFormData,
columns = [],
datasource = PLACEHOLDER_DATASOURCE,
+ additionalProps = {},
}: {
- value?: AdhocFilter;
+ value?: AdhocFilter | AdhocFilter[];
formData?: QueryFormData;
columns?: ColumnMeta[];
datasource?: Datasource;
+ additionalProps?: Partial<DndFilterSelectProps>;
} = {}) {
return (
<DndFilterSelect
@@ -91,10 +94,15 @@ function setup({
value={ensureIsArray(value)}
formData={formData}
columns={columns}
+ {...additionalProps}
/>
);
}
+beforeEach(() => {
+ jest.clearAllMocks();
+});
+
test('renders with default props', async () => {
render(setup(), { useDnd: true, store });
expect(
@@ -248,6 +256,73 @@ test('cannot drop a column that is not part of the simple
column selection', ()
).toHaveTextContent('AGG(metric_a)');
});
+test('calls onChange when close is clicked and canDelete is true', () => {
+ const value1 = new AdhocFilter({
+ sqlExpression: 'COUNT(*)',
+ expressionType: ExpressionTypes.Sql,
+ });
+ const value2 = new AdhocFilter({
+ expressionType: ExpressionTypes.Simple,
+ subject: 'col',
+ comparator: 'val',
+ operator: Operators.Equals,
+ });
+ const canDelete = jest.fn();
+ canDelete.mockReturnValue(true);
+ render(setup({ value: [value1, value2], additionalProps: { canDelete } }), {
+ useDnd: true,
+ store,
+ });
+ fireEvent.click(screen.getAllByTestId('remove-control-button')[0]);
+ expect(canDelete).toHaveBeenCalled();
+ expect(defaultProps.onChange).toHaveBeenCalledWith([value2]);
+});
+
+test('onChange is not called when close is clicked and canDelete is false', ()
=> {
+ const value1 = new AdhocFilter({
+ sqlExpression: 'COUNT(*)',
+ expressionType: ExpressionTypes.Sql,
+ });
+ const value2 = new AdhocFilter({
+ expressionType: ExpressionTypes.Simple,
+ subject: 'col',
+ comparator: 'val',
+ operator: Operators.Equals,
+ });
+ const canDelete = jest.fn();
+ canDelete.mockReturnValue(false);
+ render(setup({ value: [value1, value2], additionalProps: { canDelete } }), {
+ useDnd: true,
+ store,
+ });
+ fireEvent.click(screen.getAllByTestId('remove-control-button')[0]);
+ expect(canDelete).toHaveBeenCalled();
+ expect(defaultProps.onChange).not.toHaveBeenCalled();
+});
+
+test('onChange is not called when close is clicked and canDelete is string,
warning is displayed', async () => {
+ const value1 = new AdhocFilter({
+ sqlExpression: 'COUNT(*)',
+ expressionType: ExpressionTypes.Sql,
+ });
+ const value2 = new AdhocFilter({
+ expressionType: ExpressionTypes.Simple,
+ subject: 'col',
+ comparator: 'val',
+ operator: Operators.Equals,
+ });
+ const canDelete = jest.fn();
+ canDelete.mockReturnValue('Test warning');
+ render(setup({ value: [value1, value2], additionalProps: { canDelete } }), {
+ useDnd: true,
+ store,
+ });
+ fireEvent.click(screen.getAllByTestId('remove-control-button')[0]);
+ expect(canDelete).toHaveBeenCalled();
+ expect(defaultProps.onChange).not.toHaveBeenCalled();
+ expect(await screen.findByText('Test warning')).toBeInTheDocument();
+});
+
describe('when disallow_adhoc_metrics is set', () => {
test('can drop a column type from the simple column selection', () => {
const adhocMetric = new AdhocMetric({
diff --git
a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.tsx
b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.tsx
index 955c480724..b479ce4c87 100644
---
a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.tsx
+++
b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndFilterSelect.tsx
@@ -232,7 +232,9 @@ const DndFilterSelect = (props: DndFilterSelectProps) => {
warning({ title: t('Warning'), content: result });
return;
}
- removeValue(index);
+ if (result === true) {
+ removeValue(index);
+ }
},
[canDelete, removeValue, values],
);