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()}