fix: Invalid error tooltip if control label is function (#34698)

This commit is contained in:
Kamil Gabryjelski
2025-08-14 16:52:50 +02:00
committed by GitHub
parent f6353bd1e8
commit 47874318df
4 changed files with 173 additions and 12 deletions

View File

@@ -700,6 +700,7 @@ export const ControlPanelsContainer = (props: ControlPanelsContainerProps) => {
title={props.errorMessage}
>
<Icons.CloseCircleOutlined
data-test="query-error-tooltip-trigger"
iconColor={theme.colorErrorText}
iconSize="s"
/>

View File

@@ -299,3 +299,170 @@ test('does omit hiddenFormData when query_mode is not enabled', async () => {
expect(formData[key]).toBeUndefined();
});
});
// Component tests for the errorMessage behavior
test('does not show error indicator when no controls have validation errors', async () => {
getChartControlPanelRegistry().registerValue('table', {
controlPanelSections: [],
});
const customState = {
...reduxState,
explore: {
...reduxState.explore,
controls: {
...reduxState.explore.controls,
metric: { value: 'count', label: 'Metric' },
groupby: { value: ['category'], label: 'Group by' },
},
},
};
renderWithRouter({ initialState: customState });
await waitFor(() => {
const errorIndicator = screen.queryByTestId('query-error-tooltip-trigger');
expect(errorIndicator).not.toBeInTheDocument();
});
});
test('shows error indicator when controls have validation errors', async () => {
getChartControlPanelRegistry().registerValue('table', {
controlPanelSections: [],
});
const customState = {
...reduxState,
explore: {
...reduxState.explore,
controls: {
...reduxState.explore.controls,
metric: {
value: '',
label: 'Metric',
validationErrors: ['Metric is required'],
},
},
},
};
renderWithRouter({ initialState: customState });
const errorIndicator = await screen.findByTestId(
'query-error-tooltip-trigger',
);
userEvent.hover(errorIndicator);
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toBeInTheDocument();
const errorMessage = await screen.findByText(/Metric is required/);
expect(errorMessage).toBeInTheDocument();
});
test('shows error indicator for multiple controls with validation errors', async () => {
getChartControlPanelRegistry().registerValue('table', {
controlPanelSections: [],
});
const customState = {
...reduxState,
explore: {
...reduxState.explore,
controls: {
...reduxState.explore.controls,
metric: {
value: '',
label: 'Metric',
validationErrors: ['Field is required'],
},
groupby: {
value: [],
label: 'Group by',
validationErrors: ['Field is required'],
},
},
},
};
renderWithRouter({ initialState: customState });
const errorIndicator = await screen.findByTestId(
'query-error-tooltip-trigger',
);
userEvent.hover(errorIndicator);
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toBeInTheDocument();
expect(await screen.findByText(/Field is required/)).toBeInTheDocument();
});
test('shows error indicator for control with multiple validation errors', async () => {
getChartControlPanelRegistry().registerValue('table', {
controlPanelSections: [],
});
const customState = {
...reduxState,
explore: {
...reduxState.explore,
controls: {
...reduxState.explore.controls,
metric: {
value: '',
label: 'Metric',
validationErrors: ['Field is required', 'Invalid format'],
},
},
},
};
renderWithRouter({ initialState: customState });
const errorIndicator = await screen.findByTestId(
'query-error-tooltip-trigger',
);
userEvent.hover(errorIndicator);
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toBeInTheDocument();
expect(await screen.findByText(/Field is required/)).toBeInTheDocument();
expect(await screen.findByText(/Invalid format/)).toBeInTheDocument();
});
test('shows error indicator with function labels', async () => {
getChartControlPanelRegistry().registerValue('table', {
controlPanelSections: [],
});
// Ensure the explore state passed to the label function contains the expected property
const customState = {
...reduxState,
explore: {
...reduxState.explore,
someState: 'test',
controls: {
...reduxState.explore.controls,
metric: {
value: '',
label: (exploreState: { someState: string }) =>
`Dynamic Metric (${exploreState.someState})`,
validationErrors: ['Metric is required'],
},
},
},
};
renderWithRouter({ initialState: customState });
const errorIndicator = await screen.findByTestId(
'query-error-tooltip-trigger',
);
userEvent.hover(errorIndicator);
const tooltip = await screen.findByRole('tooltip');
expect(tooltip).toBeInTheDocument();
expect(await screen.findByText(/Metric is required/)).toBeInTheDocument();
});

View File

@@ -541,7 +541,11 @@ function ExploreViewContainer(props) {
.map(message => {
const matchingLabels = controlsWithErrors
.filter(control => control.validationErrors?.includes(message))
.map(control => control.label);
.map(control =>
typeof control.label === 'function'
? control.label(props.exploreState)
: control.label,
);
return [matchingLabels, message];
})
.map(([labels, message]) => (

View File

@@ -87,17 +87,6 @@ export const useHeaderReportMenuItems = ({
const resourceTypeReports = reportsState[resourceType] || {};
const reportData = resourceTypeReports[resourceId];
// Debug logging to understand what's happening
console.log('Report selector called:', {
resourceId,
resourceType,
reportsState: Object.keys(reportsState),
resourceTypeReports: Object.keys(resourceTypeReports),
reportData: reportData
? { id: reportData.id, name: reportData.name }
: null,
});
return reportData || null;
});