fix(explore): Save button incorrectly disabled when adding new metric with dnd (#23000)

This commit is contained in:
Kamil Gabryjelski 2023-02-06 22:19:02 +01:00 committed by GitHub
parent 2dff0009e9
commit 7d5c86b44c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 47 additions and 26 deletions

View File

@ -328,7 +328,7 @@ const DndMetricSelect = (props: any) => {
}
return new AdhocMetric(config);
}
return new AdhocMetric({ isNew: true });
return new AdhocMetric({});
}, [droppedItem]);
const ghostButtonText = isFeatureEnabled(FeatureFlag.ENABLE_DND_WITH_CLICK_UX)
@ -370,6 +370,7 @@ const DndMetricSelect = (props: any) => {
visible={newMetricPopoverVisible}
togglePopover={togglePopover}
closePopover={closePopover}
isNew
>
<div />
</AdhocMetricPopoverTrigger>

View File

@ -75,7 +75,6 @@ export default class AdhocMetric {
this.column = null;
this.aggregate = null;
}
this.isNew = !!adhocMetric.isNew;
this.datasourceWarning = !!adhocMetric.datasourceWarning;
this.hasCustomLabel = !!(adhocMetric.hasCustomLabel && adhocMetric.label);
this.label = this.hasCustomLabel
@ -125,9 +124,6 @@ export default class AdhocMetric {
duplicateWith(nextFields) {
return new AdhocMetric({
...this,
// all duplicate metrics are not considered new by default
isNew: false,
// but still overridable by nextFields
...nextFields,
});
}

View File

@ -39,7 +39,6 @@ describe('AdhocMetric', () => {
hasCustomLabel: false,
optionName: adhocMetric.optionName,
sqlExpression: null,
isNew: false,
});
});
@ -47,7 +46,6 @@ describe('AdhocMetric', () => {
const adhocMetric1 = new AdhocMetric({
column: valueColumn,
aggregate: AGGREGATES.SUM,
isNew: true,
});
const adhocMetric2 = adhocMetric1.duplicateWith({
aggregate: AGGREGATES.AVG,
@ -58,10 +56,6 @@ describe('AdhocMetric', () => {
expect(adhocMetric1.aggregate).toBe(AGGREGATES.SUM);
expect(adhocMetric2.aggregate).toBe(AGGREGATES.AVG);
// duplicated clone should not be new
expect(adhocMetric1.isNew).toBe(true);
expect(adhocMetric2.isNew).toStrictEqual(false);
});
it('can verify equality', () => {

View File

@ -45,7 +45,7 @@ const createProps = () => ({
expression: 'sum(num)',
},
],
adhocMetric: new AdhocMetric({ isNew: true }),
adhocMetric: new AdhocMetric({}),
datasource: {
extra: '{}',
type: 'table',
@ -152,6 +152,16 @@ test('Clicking on "Save" should not call onChange and onClose', () => {
expect(props.onClose).toBeCalledTimes(0);
});
test('Clicking on "Save" should call onChange and onClose for new metric', () => {
const props = createProps();
render(<AdhocMetricEditPopover {...props} isNewMetric />);
expect(props.onChange).toBeCalledTimes(0);
expect(props.onClose).toBeCalledTimes(0);
userEvent.click(screen.getByRole('button', { name: 'Save' }));
expect(props.onChange).toBeCalledTimes(1);
expect(props.onClose).toBeCalledTimes(1);
});
test('Should switch to tab:Simple', () => {
const props = createProps();
props.getCurrentTab.mockImplementation(tab => {

View File

@ -19,7 +19,13 @@
/* eslint-disable camelcase */
import React from 'react';
import PropTypes from 'prop-types';
import { t, styled, ensureIsArray, DatasourceType } from '@superset-ui/core';
import {
isDefined,
t,
styled,
ensureIsArray,
DatasourceType,
} from '@superset-ui/core';
import Tabs from 'src/components/Tabs';
import Button from 'src/components/Button';
import { Select } from 'src/components';
@ -55,11 +61,13 @@ const propTypes = {
savedMetricsOptions: PropTypes.arrayOf(savedMetricType),
savedMetric: savedMetricType,
datasource: PropTypes.object,
isNewMetric: PropTypes.bool,
};
const defaultProps = {
columns: [],
getCurrentTab: noOp,
isNewMetric: false,
};
const StyledSelect = styled(Select)`
@ -78,12 +86,7 @@ export const SAVED_TAB_KEY = 'SAVED';
export default class AdhocMetricEditPopover extends React.PureComponent {
// "Saved" is a default tab unless there are no saved metrics for dataset
defaultActiveTabKey =
(this.props.savedMetric.metric_name || this.props.adhocMetric.isNew) &&
Array.isArray(this.props.savedMetricsOptions) &&
this.props.savedMetricsOptions.length > 0
? SAVED_TAB_KEY
: this.props.adhocMetric.expressionType;
defaultActiveTabKey = this.getDefaultTab();
constructor(props) {
super(props);
@ -99,6 +102,7 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
this.onTabChange = this.onTabChange.bind(this);
this.handleAceEditorRef = this.handleAceEditorRef.bind(this);
this.refreshAceEditor = this.refreshAceEditor.bind(this);
this.getDefaultTab = this.getDefaultTab.bind(this);
this.state = {
adhocMetric: this.props.adhocMetric,
@ -106,7 +110,6 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
width: POPOVER_INITIAL_WIDTH,
height: POPOVER_INITIAL_HEIGHT,
};
document.addEventListener('mouseup', this.onMouseUp);
}
@ -137,6 +140,22 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
document.removeEventListener('mousemove', this.onMouseMove);
}
getDefaultTab() {
const { adhocMetric, savedMetric, savedMetricsOptions, isNewMetric } =
this.props;
if (isDefined(adhocMetric.column) || isDefined(adhocMetric.sqlExpression)) {
return adhocMetric.expressionType;
}
if (
(isNewMetric || savedMetric.metric_name) &&
Array.isArray(savedMetricsOptions) &&
savedMetricsOptions.length > 0
) {
return SAVED_TAB_KEY;
}
return adhocMetric.expressionType;
}
onSave() {
const { adhocMetric, savedMetric } = this.state;
@ -279,6 +298,7 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
onClose,
onResize,
datasource,
isNewMetric,
...popoverProps
} = this.props;
const { adhocMetric, savedMetric } = this.state;
@ -325,6 +345,7 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
const stateIsValid = adhocMetric.isValid() || savedMetric?.metric_name;
const hasUnsavedChanges =
isNewMetric ||
!adhocMetric.equals(propsAdhocMetric) ||
(!(
typeof savedMetric?.metric_name === 'undefined' &&

View File

@ -44,6 +44,7 @@ export type AdhocMetricPopoverTriggerProps = {
visible?: boolean;
togglePopover?: (visible: boolean) => void;
closePopover?: () => void;
isNew?: boolean;
};
export type AdhocMetricPopoverTriggerState = {
@ -223,6 +224,7 @@ class AdhocMetricPopoverTrigger extends React.PureComponent<
onChange={this.onChange}
getCurrentTab={this.getCurrentTab}
getCurrentLabel={this.getCurrentLabel}
isNewMetric={this.props.isNew}
/>
</ExplorePopoverContent>
);

View File

@ -65,7 +65,7 @@ export default function MetricDefinitionValue({
if (option instanceof AdhocMetric || savedMetric) {
const adhocMetric =
option instanceof AdhocMetric ? option : new AdhocMetric({ isNew: true });
option instanceof AdhocMetric ? option : new AdhocMetric({});
const metricOptionProps = {
onMetricEdit,

View File

@ -217,10 +217,7 @@ const MetricsControl = ({
[propsValue, savedMetrics],
);
const newAdhocMetric = useMemo(
() => new AdhocMetric({ isNew: true }),
[value],
);
const newAdhocMetric = useMemo(() => new AdhocMetric({}), [value]);
const addNewMetricPopoverTrigger = useCallback(
trigger => {
if (isAddNewMetricDisabled()) {
@ -234,6 +231,7 @@ const MetricsControl = ({
savedMetricsOptions={savedMetricOptions}
savedMetric={emptySavedMetric}
datasource={datasource}
isNew
>
{trigger}
</AdhocMetricPopoverTrigger>

View File

@ -100,7 +100,6 @@ describe.skip('MetricsControl', () => {
hasCustomLabel: false,
optionName: 'blahblahblah',
sqlExpression: null,
isNew: false,
},
]);
});