Copilot commented on code in PR #44963:
URL: https://github.com/apache/superset/pull/44963#discussion_r4186882203
##########
superset-frontend/src/explore/components/ExploreViewContainer/ExploreViewContainer.test.tsx:
##########
@@ -854,6 +855,74 @@ test('shows error indicator with function labels', async
() => {
expect(await screen.findByText(/Metric is required/)).toBeInTheDocument();
});
+const renderAfterInitialQuery = async (initialState: object) => {
+ const triggerQuerySpy = jest.spyOn(chartActions, 'triggerQuery');
+ const store = createStore(initialState, reducerIndex);
+ renderWithRouter({ initialState, store: store as Store });
+ // the mocked chart panel queries on mount; finish it like a real chart would
+ await waitFor(() => expect(triggerQuerySpy).toHaveBeenCalled());
+ act(() => {
+ store.dispatch(chartActions.chartUpdateSucceeded([], 1));
+ });
+ triggerQuerySpy.mockClear();
+ return { store, triggerQuerySpy };
+};
+
+test('Ctrl+Enter runs the query when controls are valid', async () => {
+ const { triggerQuerySpy } = await renderAfterInitialQuery(reduxState);
+
+ await userEvent.keyboard('{Control>}{Enter}{/Control}');
+
+ expect(triggerQuerySpy).toHaveBeenCalledWith(true, 1);
+});
+
+test('Cmd+Enter runs the query when controls are valid', async () => {
+ const { triggerQuerySpy } = await renderAfterInitialQuery(reduxState);
+
+ await userEvent.keyboard('{Meta>}{Enter}{/Meta}');
+
+ expect(triggerQuerySpy).toHaveBeenCalledWith(true, 1);
+});
+
+test('Ctrl+Enter does not run the query when controls have validation errors',
async () => {
+ const { triggerQuerySpy } = await renderAfterInitialQuery({
+ ...reduxState,
+ explore: {
+ ...reduxState.explore,
+ controls: {
+ ...reduxState.explore.controls,
+ metric: {
+ value: '',
+ label: 'Metric',
+ validationErrors: ['Metric is required'],
+ },
+ },
Review Comment:
This test replaces the entire `metric` control object with a minimal shape,
which can make the test brittle if the component (or collaborators) later
relies on other `metric` fields being present. Prefer spreading the existing
control (e.g., `...reduxState.explore.controls.metric`) and overriding only
`value`/`validationErrors` so the state stays structurally realistic.
##########
superset-frontend/src/explore/components/ExploreViewContainer/index.tsx:
##########
@@ -580,14 +580,21 @@ function ExploreViewContainer(props:
ExploreViewContainerProps) {
const controlOrCommand = event.ctrlKey || event.metaKey;
if (controlOrCommand) {
const isEnter = event.key === 'Enter' || event.keyCode === 13;
- if (isEnter) {
+ // Match the Run button, which is disabled while any control has
+ // validation errors and swapped for Stop while the chart is loading
+ const hasValidationErrors = Object.values(props.controls).some(
+ control =>
+ control.validationErrors && control.validationErrors.length > 0,
+ );
+ const isLoading = props.chart.chartStatus === 'loading';
+ if (isEnter && !hasValidationErrors && !isLoading) {
onQuery();
}
// Note: Ctrl+S save functionality removed due to type
incompatibilities
// between Slice types. Use the save button instead.
}
},
- [onQuery],
+ [onQuery, props.controls, props.chart.chartStatus],
Review Comment:
Including `props.controls` (an object) in the callback deps will typically
change identity frequently, which can cause `handleKeydown` to be recreated
often and (if it’s used in an event-listener effect) repeatedly remove/re-add
the listener. Consider deriving `hasValidationErrors` and `isLoading` as stable
booleans (e.g., via `useMemo` with narrower deps) and depending on those, or
storing the latest values in refs so the keydown handler can stay stable while
still reading current state.
##########
superset-frontend/src/explore/components/ExploreViewContainer/ExploreViewContainer.test.tsx:
##########
@@ -854,6 +855,74 @@ test('shows error indicator with function labels', async
() => {
expect(await screen.findByText(/Metric is required/)).toBeInTheDocument();
});
+const renderAfterInitialQuery = async (initialState: object) => {
+ const triggerQuerySpy = jest.spyOn(chartActions, 'triggerQuery');
+ const store = createStore(initialState, reducerIndex);
+ renderWithRouter({ initialState, store: store as Store });
+ // the mocked chart panel queries on mount; finish it like a real chart would
+ await waitFor(() => expect(triggerQuerySpy).toHaveBeenCalled());
+ act(() => {
+ store.dispatch(chartActions.chartUpdateSucceeded([], 1));
+ });
+ triggerQuerySpy.mockClear();
+ return { store, triggerQuerySpy };
+};
+
+test('Ctrl+Enter runs the query when controls are valid', async () => {
+ const { triggerQuerySpy } = await renderAfterInitialQuery(reduxState);
+
+ await userEvent.keyboard('{Control>}{Enter}{/Control}');
+
+ expect(triggerQuerySpy).toHaveBeenCalledWith(true, 1);
+});
+
+test('Cmd+Enter runs the query when controls are valid', async () => {
+ const { triggerQuerySpy } = await renderAfterInitialQuery(reduxState);
+
+ await userEvent.keyboard('{Meta>}{Enter}{/Meta}');
+
+ expect(triggerQuerySpy).toHaveBeenCalledWith(true, 1);
+});
+
+test('Ctrl+Enter does not run the query when controls have validation errors',
async () => {
Review Comment:
The test name mentions only Ctrl+Enter, but the body also asserts
Cmd(=Meta)+Enter behavior. Rename the test to include both (e.g.,
'Ctrl/Cmd+Enter...') or split into two tests to keep intent clear.
##########
superset-frontend/src/explore/components/ExploreViewContainer/ExploreViewContainer.test.tsx:
##########
@@ -854,6 +855,74 @@ test('shows error indicator with function labels', async
() => {
expect(await screen.findByText(/Metric is required/)).toBeInTheDocument();
});
+const renderAfterInitialQuery = async (initialState: object) => {
+ const triggerQuerySpy = jest.spyOn(chartActions, 'triggerQuery');
+ const store = createStore(initialState, reducerIndex);
+ renderWithRouter({ initialState, store: store as Store });
+ // the mocked chart panel queries on mount; finish it like a real chart would
+ await waitFor(() => expect(triggerQuerySpy).toHaveBeenCalled());
+ act(() => {
+ store.dispatch(chartActions.chartUpdateSucceeded([], 1));
+ });
+ triggerQuerySpy.mockClear();
+ return { store, triggerQuerySpy };
+};
+
+test('Ctrl+Enter runs the query when controls are valid', async () => {
+ const { triggerQuerySpy } = await renderAfterInitialQuery(reduxState);
+
+ await userEvent.keyboard('{Control>}{Enter}{/Control}');
+
+ expect(triggerQuerySpy).toHaveBeenCalledWith(true, 1);
+});
+
+test('Cmd+Enter runs the query when controls are valid', async () => {
+ const { triggerQuerySpy } = await renderAfterInitialQuery(reduxState);
+
+ await userEvent.keyboard('{Meta>}{Enter}{/Meta}');
+
+ expect(triggerQuerySpy).toHaveBeenCalledWith(true, 1);
+});
+
+test('Ctrl+Enter does not run the query when controls have validation errors',
async () => {
+ const { triggerQuerySpy } = await renderAfterInitialQuery({
+ ...reduxState,
+ explore: {
+ ...reduxState.explore,
+ controls: {
+ ...reduxState.explore.controls,
+ metric: {
+ value: '',
+ label: 'Metric',
+ validationErrors: ['Metric is required'],
+ },
+ },
+ },
+ });
+
+ await userEvent.keyboard('{Control>}{Enter}{/Control}');
+ await userEvent.keyboard('{Meta>}{Enter}{/Meta}');
Review Comment:
The test name mentions only Ctrl+Enter, but the body also asserts
Cmd(=Meta)+Enter behavior. Rename the test to include both (e.g.,
'Ctrl/Cmd+Enter...') or split into two tests to keep intent clear.
##########
superset-frontend/src/explore/components/ExploreViewContainer/index.tsx:
##########
@@ -580,14 +580,21 @@ function ExploreViewContainer(props:
ExploreViewContainerProps) {
const controlOrCommand = event.ctrlKey || event.metaKey;
if (controlOrCommand) {
const isEnter = event.key === 'Enter' || event.keyCode === 13;
- if (isEnter) {
+ // Match the Run button, which is disabled while any control has
+ // validation errors and swapped for Stop while the chart is loading
+ const hasValidationErrors = Object.values(props.controls).some(
+ control =>
+ control.validationErrors && control.validationErrors.length > 0,
+ );
+ const isLoading = props.chart.chartStatus === 'loading';
+ if (isEnter && !hasValidationErrors && !isLoading) {
onQuery();
}
Review Comment:
Including `props.controls` (an object) in the callback deps will typically
change identity frequently, which can cause `handleKeydown` to be recreated
often and (if it’s used in an event-listener effect) repeatedly remove/re-add
the listener. Consider deriving `hasValidationErrors` and `isLoading` as stable
booleans (e.g., via `useMemo` with narrower deps) and depending on those, or
storing the latest values in refs so the keydown handler can stay stable while
still reading current state.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]