mirror of
https://github.com/apache/superset.git
synced 2026-07-24 23:56:22 +00:00
Compare commits
1 Commits
chore/impo
...
fix-deckgl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00b5744081 |
@@ -22,7 +22,7 @@ import { createEvent, fireEvent, render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { supersetTheme, ThemeProvider } from '@apache-superset/core/theme';
|
||||
import type { ReactElement } from 'react';
|
||||
import Legend from './Legend';
|
||||
import Legend, { type LegendProps } from './Legend';
|
||||
|
||||
const renderWithTheme = (component: ReactElement) =>
|
||||
render(<ThemeProvider theme={supersetTheme}>{component}</ThemeProvider>);
|
||||
@@ -31,6 +31,7 @@ test('formats interval-notation labels while preserving brackets', () => {
|
||||
renderWithTheme(
|
||||
<Legend
|
||||
format=",.2f"
|
||||
position="tr"
|
||||
categories={{
|
||||
'[1, 81)': { enabled: true, color: [0, 0, 0] },
|
||||
'[81, 212)': { enabled: true, color: [0, 0, 0] },
|
||||
@@ -48,6 +49,7 @@ test('still formats legacy "a - b" delimiter labels', () => {
|
||||
renderWithTheme(
|
||||
<Legend
|
||||
format=",.1f"
|
||||
position="tr"
|
||||
categories={{
|
||||
'0 - 100000': { enabled: true, color: [0, 0, 0] },
|
||||
'100001 - 200000': { enabled: true, color: [0, 0, 0] },
|
||||
@@ -63,6 +65,7 @@ test('leaves labels untouched when no format is provided', () => {
|
||||
renderWithTheme(
|
||||
<Legend
|
||||
format={null}
|
||||
position="tr"
|
||||
categories={{ '[1, 81)': { enabled: true, color: [0, 0, 0] } }}
|
||||
/>,
|
||||
);
|
||||
@@ -78,6 +81,7 @@ test('clicking a legend item toggles the category without triggering anchor navi
|
||||
renderWithTheme(
|
||||
<Legend
|
||||
format={null}
|
||||
position="tr"
|
||||
categories={{
|
||||
Positive: { enabled: true, color: [0, 255, 0] },
|
||||
Negative: { enabled: true, color: [255, 0, 0] },
|
||||
@@ -103,6 +107,7 @@ test('ctrl+clicking a legend item toggles the category without opening a new tab
|
||||
renderWithTheme(
|
||||
<Legend
|
||||
format={null}
|
||||
position="tr"
|
||||
categories={{
|
||||
cat1: { enabled: true, color: [255, 0, 0] },
|
||||
cat2: { enabled: false, color: [0, 0, 255] },
|
||||
@@ -124,3 +129,34 @@ test('ctrl+clicking a legend item toggles the category without opening a new tab
|
||||
expect(toggleCategory).toHaveBeenCalledTimes(1);
|
||||
expect(toggleCategory).toHaveBeenCalledWith('cat1');
|
||||
});
|
||||
|
||||
// Regression proof for the "Legend Position: None" control not hiding the
|
||||
// legend. The control's "None" choice has a null value, but the modernized
|
||||
// Select can hand it back as undefined or an empty string, so the legend must
|
||||
// hide for any falsy position rather than strictly matching null.
|
||||
test.each([null, undefined, ''])(
|
||||
'renders nothing when Legend Position is None (position=%p)',
|
||||
position => {
|
||||
renderWithTheme(
|
||||
<Legend
|
||||
format={null}
|
||||
position={position as LegendProps['position']}
|
||||
categories={{ Alpha: { enabled: true, color: [0, 0, 0] } }}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Alpha')).not.toBeInTheDocument();
|
||||
},
|
||||
);
|
||||
|
||||
test('renders the legend for a valid corner position', () => {
|
||||
renderWithTheme(
|
||||
<Legend
|
||||
format={null}
|
||||
position="tr"
|
||||
categories={{ Alpha: { enabled: true, color: [0, 0, 0] } }}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('Alpha')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -98,7 +98,7 @@ export type LegendProps = {
|
||||
const Legend = ({
|
||||
format: d3Format = null,
|
||||
forceCategorical = false,
|
||||
position = 'tr',
|
||||
position,
|
||||
categories: categoriesObject = {},
|
||||
toggleCategory = () => {},
|
||||
showSingleCategory = () => {},
|
||||
@@ -136,7 +136,12 @@ const Legend = ({
|
||||
return format(k);
|
||||
};
|
||||
|
||||
if (Object.keys(categoriesObject).length === 0 || position === null) {
|
||||
// Hide the legend when there are no categories, or when Legend Position is
|
||||
// "None". The control's "None" choice has a null value, but the Select can
|
||||
// round-trip it as undefined (or an empty string), so treat every falsy
|
||||
// position as "hidden" rather than strictly matching null. Valid corner
|
||||
// values ('tl'/'tr'/'bl'/'br') are truthy and fall through to render.
|
||||
if (Object.keys(categoriesObject).length === 0 || !position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
import type { ReactElement } from 'react';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render } from '@testing-library/react';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import '@testing-library/jest-dom';
|
||||
import { supersetTheme, ThemeProvider } from '@apache-superset/core/theme';
|
||||
@@ -53,15 +53,24 @@ jest.mock('../../utils/mapbox', () => ({
|
||||
hasMapboxApiKey: () => true,
|
||||
}));
|
||||
|
||||
jest.mock('../../components/Legend', () => ({ categories, position }: any) => (
|
||||
<div
|
||||
data-testid="legend"
|
||||
data-categories={JSON.stringify(categories)}
|
||||
data-position={position}
|
||||
>
|
||||
Legend Mock
|
||||
</div>
|
||||
));
|
||||
// Stand in for the real Legend while preserving its hide contract: a falsy
|
||||
// position (Legend Position "None") renders nothing. Without this the mock
|
||||
// would render unconditionally and the "None hides the legend" assertions
|
||||
// below would pass vacuously.
|
||||
jest.mock(
|
||||
'../../components/Legend',
|
||||
() =>
|
||||
({ categories, position }: any) =>
|
||||
position ? (
|
||||
<div
|
||||
data-testid="legend"
|
||||
data-categories={JSON.stringify(categories)}
|
||||
data-position={position}
|
||||
>
|
||||
Legend Mock
|
||||
</div>
|
||||
) : null,
|
||||
);
|
||||
|
||||
const mockProps = {
|
||||
formData: {
|
||||
@@ -369,20 +378,30 @@ describe('DeckGLPolygon Error Handling and Edge Cases', () => {
|
||||
expect(mockGetBuckets).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('handles null legend_position correctly', () => {
|
||||
const propsWithNullLegendPosition = {
|
||||
...mockProps,
|
||||
formData: {
|
||||
...mockProps.formData,
|
||||
legend_position: null,
|
||||
},
|
||||
};
|
||||
// The "None" choice can reach the layer as null or (after the Select
|
||||
// round-trip) undefined; either must hide the legend. queryByTestId is
|
||||
// configured to match data-test, not the mock's data-testid, so assert
|
||||
// against the container directly.
|
||||
test.each([null, undefined])(
|
||||
'hides the legend when legend_position is None (%p)',
|
||||
legendPosition => {
|
||||
const propsWithNoLegend = {
|
||||
...mockProps,
|
||||
formData: {
|
||||
...mockProps.formData,
|
||||
legend_position: legendPosition,
|
||||
},
|
||||
};
|
||||
|
||||
renderWithTheme(<DeckGLPolygon {...propsWithNullLegendPosition} />);
|
||||
const { container } = renderWithTheme(
|
||||
<DeckGLPolygon {...propsWithNoLegend} />,
|
||||
);
|
||||
|
||||
// Legend should not be rendered when position is null
|
||||
expect(screen.queryByTestId('legend')).not.toBeInTheDocument();
|
||||
});
|
||||
expect(
|
||||
container.querySelector('[data-testid="legend"]'),
|
||||
).not.toBeInTheDocument();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('DeckGLPolygon Legend Integration', () => {
|
||||
@@ -421,10 +440,14 @@ describe('DeckGLPolygon Legend Integration', () => {
|
||||
},
|
||||
};
|
||||
|
||||
renderWithTheme(<DeckGLPolygon {...propsWithoutMetric} />);
|
||||
const { container } = renderWithTheme(
|
||||
<DeckGLPolygon {...propsWithoutMetric} />,
|
||||
);
|
||||
|
||||
// Legend should not be rendered when no metric is defined
|
||||
expect(screen.queryByTestId('legend')).not.toBeInTheDocument();
|
||||
expect(
|
||||
container.querySelector('[data-testid="legend"]'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user