diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.test.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.test.tsx
index d59f483bc6..e16c1d0d25 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.test.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndMetricSelect.test.tsx
@@ -17,11 +17,13 @@
* under the License.
*/
import React from 'react';
+import userEvent from '@testing-library/user-event';
import {
render,
screen,
within,
fireEvent,
+ waitFor,
} from 'spec/helpers/testing-library';
import { DndMetricSelect } from 'src/explore/components/controls/DndColumnSelectControl/DndMetricSelect';
import { AGGREGATES } from 'src/explore/constants';
@@ -317,3 +319,81 @@ test('can drag metrics', async () => {
expect(within(firstMetric).getByText('SUM(Column B)')).toBeVisible();
expect(within(lastMetric).getByText('metric_a')).toBeVisible();
});
+
+test('title changes on custom SQL text change', async () => {
+ let metricValues = [adhocMetricA, 'metric_b'];
+ const onChange = (val: any[]) => {
+ metricValues = [...val];
+ };
+
+ const { rerender } = render(
+ ,
+ {
+ useDnd: true,
+ },
+ );
+
+ expect(screen.getByText('SUM(column_a)')).toBeVisible();
+
+ metricValues = [adhocMetricA, 'metric_b', 'metric_a'];
+ rerender(
+ ,
+ );
+
+ expect(screen.getByText('SUM(column_a)')).toBeVisible();
+ expect(screen.getByText('metric_a')).toBeVisible();
+
+ fireEvent.click(screen.getByText('metric_a'));
+ expect(await screen.findByText('Custom SQL')).toBeInTheDocument();
+
+ fireEvent.click(screen.getByText('Custom SQL'));
+ expect(screen.getByText('Custom SQL').parentElement).toHaveClass(
+ 'ant-tabs-tab-active',
+ );
+
+ const container = screen.getByTestId('adhoc-metric-edit-tabs');
+ await waitFor(() => {
+ const textArea = container.getElementsByClassName(
+ 'ace_text-input',
+ ) as HTMLCollectionOf;
+ expect(textArea.length).toBe(1);
+ expect(textArea[0].value).toBe('');
+ });
+
+ expect(screen.getByTestId('AdhocMetricEditTitle#trigger')).toHaveTextContent(
+ 'metric_a',
+ );
+
+ const textArea = container.getElementsByClassName(
+ 'ace_text-input',
+ )[0] as HTMLTextAreaElement;
+
+ // Changing the ACE editor via pasting, since the component
+ // handles the textarea value internally, and changing it doesn't
+ // trigger the onChange
+ await userEvent.paste(textArea, 'New metric');
+
+ await waitFor(() => {
+ expect(
+ screen.getByTestId('AdhocMetricEditTitle#trigger'),
+ ).toHaveTextContent('New metric');
+ });
+
+ // Ensure the title does not reset on mouse over
+ fireEvent.mouseEnter(screen.getByTestId('AdhocMetricEditTitle#trigger'));
+ fireEvent.mouseOut(screen.getByTestId('AdhocMetricEditTitle#trigger'));
+
+ expect(screen.getByTestId('AdhocMetricEditTitle#trigger')).toHaveTextContent(
+ 'New metric',
+ );
+});
diff --git a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndSelectLabel.tsx b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndSelectLabel.tsx
index 3947fb3bef..467a440a4c 100644
--- a/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndSelectLabel.tsx
+++ b/superset-frontend/src/explore/components/controls/DndColumnSelectControl/DndSelectLabel.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React, { ReactNode } from 'react';
+import React, { ReactNode, useMemo } from 'react';
import { useDrop } from 'react-dnd';
import { t, useTheme } from '@superset-ui/core';
import ControlHeader from 'src/explore/components/ControlHeader';
@@ -48,6 +48,7 @@ export type DndSelectLabelProps = {
export default function DndSelectLabel({
displayGhostButton = true,
accept,
+ valuesRenderer,
...props
}: DndSelectLabelProps) {
const theme = useTheme();
@@ -70,6 +71,8 @@ export default function DndSelectLabel({
}),
});
+ const values = useMemo(() => valuesRenderer(), [valuesRenderer]);
+
function renderGhostButton() {
return (
- {props.valuesRenderer()}
+ {values}
{displayGhostButton && renderGhostButton()}