Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 14 additions & 47 deletions superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,9 @@ const DEFAULT_LEGEND_ICON_WIDTH = 25;
const LEGEND_ICON_LABEL_GAP = 5;
const LEGEND_HORIZONTAL_SIDE_GUTTER = 16;
const LEGEND_HORIZONTAL_ROW_HEIGHT = 24;
const LEGEND_HORIZONTAL_MAX_ROWS = 2;
const LEGEND_HORIZONTAL_MAX_HEIGHT_RATIO = 0.25;
// Cap the reserved horizontal legend margin so an overflowing legend can't eat the plot.
const MAX_LEGEND_MARGIN_RATIO = 0.4;
Comment on lines +63 to +64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The comment says the cap is only for horizontal legends, but the same constant is now used for both horizontal and vertical layout code paths; this mismatch is misleading and can cause incorrect future maintenance changes. Update the comment to reflect both orientations or split constants/comments per orientation. [comment mismatch]

Severity Level: Minor 🧹
- ⚠️ Comment misstates cap usage across legend orientations.
- ⚠️ Could mislead maintainers making orientation-specific changes.
Steps of Reproduction ✅
1. A developer reads the comment above `MAX_LEGEND_MARGIN_RATIO` in
`superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts:63-64`, which states
the cap is for the reserved horizontal legend margin.

2. The same `MAX_LEGEND_MARGIN_RATIO` constant is used to cap margins in
`getHorizontalPlainLegendLayout()` at `series.ts:268-271` and also in
`getVerticalPlainLegendLayout()` at `series.ts:308-311`, affecting both horizontal and
vertical legends.

3. This means the comment at `series.ts:63` is factually inaccurate: the cap applies to
all plain legends, not only horizontal ones.

4. While this does not affect runtime behavior, the misleading comment can confuse future
maintainers and lead to incorrect, orientation-specific changes that overlook the vertical
usage of the constant.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
**Line:** 63:64
**Comment:**
	*Comment Mismatch: The comment says the cap is only for horizontal legends, but the same constant is now used for both horizontal and vertical layout code paths; this mismatch is misleading and can cause incorrect future maintenance changes. Update the comment to reflect both orientations or split constants/comments per orientation.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

const LEGEND_VERTICAL_SIDE_GUTTER = 16;
const LEGEND_VERTICAL_ROW_HEIGHT = 24;
const LEGEND_VERTICAL_MAX_WIDTH_RATIO = 0.4;
const LEGEND_SELECTOR_GAP = 10;
const LEGEND_MARGIN_GUTTER = 45;
// ECharts does not expose pre-render measurements for plain legends, so these
Expand All @@ -84,10 +82,6 @@ export type LegendLayoutResult = {
effectiveType: LegendType;
};

const SCROLL_LEGEND_LAYOUT: LegendLayoutResult = {
effectiveType: LegendType.Scroll,
};

function getLegendLabel(item: LegendDataItem): string {
if (typeof item === 'string' || typeof item === 'number') {
return String(item);
Expand Down Expand Up @@ -269,37 +263,30 @@ function getHorizontalPlainLegendLayout({
showSelectors,
theme,
);
const rowsForMargin = Number.isFinite(rowCount)
? rowCount
: legendLabels.length;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a single label is wider than the available width, rowCount is infinite but this still preserves Plain without constraining the text, so ECharts renders outside the viewport. Could we bound or truncate oversized horizontal labels while keeping the explicit List selection?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, a finite margin does not mean the label fits. The fallback keeps the arithmetic sane but leaves the text unconstrained, so it can still paint outside the viewport.

Truncating it properly means adding a width to the layout result and applying textStyle.width plus overflow in the legend builder for the 8 charts that use this helper (Radar, Gantt, Bubble, Pie, Timeseries, MixedTimeseries, Graph, Funnel). That about doubles this diff and pulls in six chart types unrelated to the original bug, so I would rather file it separately and pick it up next. Say the word if you want it here instead.

const requiredMargin =
defaultLegendPadding[orientation] +
Math.max(0, rowCount - 1) * LEGEND_HORIZONTAL_ROW_HEIGHT;
const maxLegendHeight =
Math.max(0, rowsForMargin - 1) * LEGEND_HORIZONTAL_ROW_HEIGHT;
Comment on lines +266 to +271

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: When row estimation overflows (Infinity), this fallback uses the full legend item count as row count with no cap, which can produce extremely large effectiveMargin values (thousands of pixels for large series lists) and collapse the plot area. Clamp the computed rows/margin against chart height (or a sane max rows threshold) before returning the margin. [logic error]

Severity Level: Major ⚠️
❌ Small-width timeseries charts can collapse to near-zero height.
❌ MixedTimeseries legends can reserve disproportionate vertical space.
⚠️ Gantt charts with many series may show barely visible plot.
Steps of Reproduction ✅
1. In the timeseries chart pipeline, `resolveLegendLayout()` at
`superset-frontend/plugins/plugin-chart-echarts/src/utils/legendLayout.ts:19-36` calls
`getLegendLayoutResult()` with `availableWidth` computed in
`Timeseries/transformProps.ts:28-38` via `getHorizontalLegendAvailableWidth({ chartWidth:
width, orientation: legendOrientation, padding, zoomable })`.

2. When the chart container is extremely narrow, or left padding from `getPadding()`
equals/exceeds `width`, `getHorizontalLegendAvailableWidth()` in
`src/utils/series.ts:62-79` returns `availableWidth = 0` (it does `chartWidth -
(padding?.left ?? 0)` then `Math.max(availableWidth, 0)`).

3. `getLegendLayoutResult()` in `src/utils/series.ts:110-161` receives this
`availableWidth` and, for a horizontal legend (`LegendOrientation.Top`/`Bottom`) with
`type: LegendType.Plain`, calls `getHorizontalPlainLegendLayout()` (lines 42-56) with
non-empty `legendItems` (e.g. many series names from
`Timeseries/transformProps.ts:40-43`).

4. Inside `getHorizontalPlainLegendLayout()`, `estimateHorizontalLegendRows()` at
`src/utils/series.ts:3-15` sees `availableWidth === 0` and returns `Infinity`. The new
fallback at lines 262-265 sets `rowsForMargin = legendLabels.length` when `rowCount` is
non-finite. For a legend with many items (e.g. 100 series), `requiredMargin =
defaultLegendPadding[orientation] + (rowsForMargin - 1) * LEGEND_HORIZONTAL_ROW_HEIGHT`
(lines 265-267) becomes thousands of pixels (99 * 24px ≈ 2376 + padding). This
`effectiveMargin` is propagated via `resolveLegendLayout()` and then used as
`effectiveLegendMargin` in `Timeseries/transformProps.ts:60-75` and
`MixedTimeseries/transformProps.ts:39-49` when computing grid padding, causing the ECharts
grid height to be squeezed down to a tiny or zero drawable area on small-width charts with
many legend items.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
**Line:** 262:267
**Comment:**
	*Logic Error: When row estimation overflows (`Infinity`), this fallback uses the full legend item count as row count with no cap, which can produce extremely large `effectiveMargin` values (thousands of pixels for large series lists) and collapse the plot area. Clamp the computed rows/margin against chart height (or a sane max rows threshold) before returning the margin.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid, and it took two passes to fully close. The horizontal path got a cap in 685fd8c (40% of the chart height). The vertical path had the same flaw and went unnoticed until a reviewer raised it, fixed today in 56f1ba4. Both now clamp against the available dimension.

const boundedMargin =
availableHeight > 0
? availableHeight * LEGEND_HORIZONTAL_MAX_HEIGHT_RATIO
: Infinity;

if (
!Number.isFinite(rowCount) ||
rowCount > LEGEND_HORIZONTAL_MAX_ROWS ||
requiredMargin > maxLegendHeight
) {
return SCROLL_LEGEND_LAYOUT;
}
? Math.min(requiredMargin, availableHeight * MAX_LEGEND_MARGIN_RATIO)
: requiredMargin;

return {
effectiveMargin: Math.max(currentMargin, requiredMargin),
effectiveMargin: Math.max(currentMargin, boundedMargin),
effectiveType: LegendType.Plain,
};
}

function getVerticalPlainLegendLayout({
availableHeight,
availableWidth,
currentMargin,
legendLabels,
showSelectors,
theme,
}: {
availableHeight: number;
availableWidth: number;
currentMargin: number;
legendLabels: string[];
Expand All @@ -313,17 +300,6 @@ function getVerticalPlainLegendLayout({
};
}

const selectorHeight = showSelectors
? LEGEND_VERTICAL_ROW_HEIGHT + LEGEND_SELECTOR_GAP
: 0;
const effectiveAvailableHeight = Math.max(
availableHeight - LEGEND_VERTICAL_SIDE_GUTTER - selectorHeight,
0,
);
const rowsPerColumn = Math.floor(
(effectiveAvailableHeight + DEFAULT_LEGEND_ITEM_GAP) /
(LEGEND_VERTICAL_ROW_HEIGHT + DEFAULT_LEGEND_ITEM_GAP),
);
const requiredSelectorMargin = showSelectors
? ESTIMATED_LEGEND_SELECTOR_WIDTH + LEGEND_VERTICAL_SIDE_GUTTER
: 0;
Expand All @@ -333,21 +309,13 @@ function getVerticalPlainLegendLayout({
requiredSelectorMargin,
),
);
const maxLegendWidth =
const boundedMargin =
availableWidth > 0
? availableWidth * LEGEND_VERTICAL_MAX_WIDTH_RATIO
: Infinity;

if (
rowsPerColumn <= 0 ||
legendLabels.length > rowsPerColumn ||
requiredMargin > maxLegendWidth
) {
return SCROLL_LEGEND_LAYOUT;
}
? Math.min(requiredMargin, availableWidth * MAX_LEGEND_MARGIN_RATIO)
: requiredMargin;

return {
effectiveMargin: Math.max(currentMargin, requiredMargin),
effectiveMargin: Math.max(currentMargin, boundedMargin),
effectiveType: LegendType.Plain,
};
}
Expand Down Expand Up @@ -404,7 +372,6 @@ export function getLegendLayoutResult({
}

return getVerticalPlainLegendLayout({
availableHeight: resolvedAvailableHeight,
availableWidth: resolvedAvailableWidth,
currentMargin: resolvedLegendMargin,
legendLabels,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
import { AxisType, ChartProps } from '@superset-ui/core';
import { supersetTheme } from '@apache-superset/core/theme';
import type { LegendComponentOption } from 'echarts/components';
import {
LegendOrientation,
LegendType,
Expand Down Expand Up @@ -304,7 +305,7 @@ describe('legend sorting', () => {
expect(legendData).toEqual(['series value 2', 'series value 1']);
});

test('falls back to scroll for plain legends with an overlong legend item', () => {
test('honors an explicit List selection for plain legends with an overlong legend item', () => {
const props = new ChartProps({
...chartPropsConfig,
width: 320,
Expand All @@ -321,7 +322,7 @@ describe('legend sorting', () => {
'Y Axis': 'first',
tooltip_column: 'tooltip value 1',
series:
'This is a ridiculously long legend label that should switch to scroll',
'This is a ridiculously long legend label that stays a plain List',
},
{
startTime: Date.UTC(2025, 1, 1, 18, 0, 0),
Expand All @@ -344,7 +345,9 @@ describe('legend sorting', () => {

const result = transformProps(props as EchartsGanttChartProps);

expect((result.echartOptions.legend as any).type).toBe(LegendType.Scroll);
expect((result.echartOptions.legend as LegendComponentOption).type).toBe(
LegendType.Plain,
);
});

test('keeps legend visibility driven by showLegend for single-series charts', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ describe('Pie transformProps', () => {
]);
});

test('falls back to scroll for plain legends with overlong labels', () => {
test('honors an explicit List selection for plain legends with overlong labels', () => {
const longLegendChartProps = new ChartProps({
formData: {
colorScheme: 'bnbColors',
Expand Down Expand Up @@ -182,7 +182,7 @@ describe('Pie transformProps', () => {
);

expect((transformed.echartOptions.legend as any).type).toBe(
LegendType.Scroll,
LegendType.Plain,
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ describe('Bar Chart X-axis Time Formatting', () => {
type: LegendType.Plain,
});

test('should fall back to scroll for horizontal bottom legends after margin expansion reduces available width', () => {
test('honors an explicit List selection for horizontal bottom legends and reserves margin', () => {
const legendLabels = [
'This is a long sales legend',
'This is a long marketing legend',
Expand Down Expand Up @@ -899,64 +899,30 @@ describe('Bar Chart X-axis Time Formatting', () => {
legendType: LegendType.Plain,
showLegend: true,
};
const baselineChartProps = createEchartsTimeseriesTestChartProps<
EchartsTimeseriesFormData,
EchartsTimeseriesChartProps
>({
defaultFormData: regressionFormData,
defaultVizType: 'echarts_timeseries_bar',
defaultQueriesData: longLegendData,
width: baseChartPropsConfig.width,
height: baseChartPropsConfig.height,
});
const baselineTransformed = transformProps(baselineChartProps);
const legendItems = (
(baselineTransformed.echartOptions.legend as LegendComponentOption)
.data as Array<string | { name: string }>
).map(item => (typeof item === 'string' ? item : item.name));
let chartWidth: number | undefined;
let expandedLegendMargin: number | null = null;

for (let width = 300; width <= 700; width += 1) {
const initialLayout = getBottomLegendLayout(width, legendItems, null);

if (initialLayout.effectiveType !== LegendType.Plain) {
continue;
}

const refinedLayout = getBottomLegendLayout(
width,
legendItems,
initialLayout.effectiveMargin ?? null,
);

if (refinedLayout.effectiveType === LegendType.Scroll) {
chartWidth = width;
expandedLegendMargin = initialLayout.effectiveMargin ?? null;
break;
}
}

expect(chartWidth).toBeDefined();
expect(expandedLegendMargin).not.toBeNull();
const resolvedChartWidth = chartWidth ?? baseChartPropsConfig.width;

// A narrow chart forces the long-label bottom legend to wrap onto
// multiple rows — the case that previously flipped List to scroll.
const chartWidth = 320;
const chartProps = createEchartsTimeseriesTestChartProps<
EchartsTimeseriesFormData,
EchartsTimeseriesChartProps
>({
defaultFormData: regressionFormData,
defaultVizType: 'echarts_timeseries_bar',
defaultQueriesData: longLegendData,
width: resolvedChartWidth,
width: chartWidth,
height: baseChartPropsConfig.height,
});

const transformedProps = transformProps(chartProps);
const legend = transformedProps.echartOptions
.legend as LegendComponentOption;
const grid = transformedProps.echartOptions.grid as GridComponentOption;
const expectedPadding = getPadding(
const legendItems = (legend.data as Array<string | { name: string }>).map(
item => (typeof item === 'string' ? item : item.name),
);

const layout = getBottomLegendLayout(chartWidth, legendItems, null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The layout helper is invoked with a null legend type, so this assertion path is not actually exercising the explicit List/Plain selection scenario described by the test. This can let a regression slip through because the helper call is testing default behavior instead of the user-selected type; pass the explicit plain type to the helper in this check. [logic error]

Severity Level: Major ⚠️
- ⚠️ Bottom legend List selection regression tests may miss failures.
- ⚠️ ECharts timeseries bar legend-layout helper less protected.
Steps of Reproduction ✅
1. Open
`superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Bar/transformProps.test.ts`
and locate the test `honors an explicit List selection for horizontal bottom legends and
reserves margin` starting around line 855.

2. At lines 895–901, observe `regressionFormData` is built with `legendType:
LegendType.Plain`, representing an explicit List/Plain legend selection by the user.

3. At lines 916–919, the test calls `transformProps(chartProps)` and extracts `legend`
from `transformedProps.echartOptions.legend`, then later asserts at line 943 that
`legend.type` is `LegendType.Plain`, confirming the ECharts config carries the explicit
plain type.

4. At line 924, the layout helper is invoked as `const layout =
getBottomLegendLayout(chartWidth, legendItems, null);`, passing `null` as the third
argument instead of `LegendType.Plain`, so the subsequent assertion at line 944 that
`layout.effectiveType` is `LegendType.Plain` only exercises the helper’s default/null
behavior; any regression specifically in how `getBottomLegendLayout` handles an explicit
`LegendType.Plain` (the bug described in the PR) would not be covered by this test path.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Bar/transformProps.test.ts
**Line:** 924:924
**Comment:**
	*Logic Error: The layout helper is invoked with a null legend type, so this assertion path is not actually exercising the explicit List/Plain selection scenario described by the test. This can let a regression slip through because the helper call is testing default behavior instead of the user-selected type; pass the explicit plain type to the helper in this check.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think this one holds. The third parameter of getBottomLegendLayout is legendMargin (line 826), not the legend type, and the helper hardcodes type: LegendType.Plain in the options it builds (line 852). Passing null means no user-set margin, so the default padding applies, which is the case this test is meant to cover. The explicit Plain selection is still exercised end to end.

const basePadding = getPadding(
true,
LegendOrientation.Bottom,
false,
Expand All @@ -968,30 +934,41 @@ describe('Bar Chart X-axis Time Formatting', () => {
undefined,
true,
);
[expectedPadding.bottom, expectedPadding.left] = [
expectedPadding.left,
expectedPadding.bottom,
[basePadding.bottom, basePadding.left] = [
basePadding.left,
basePadding.bottom,
];
Comment on lines +937 to 940

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swapped padding corrupts baseline

The destructuring-swap [basePadding.bottom, basePadding.left] = [basePadding.left, basePadding.bottom] on lines 937-940 mutates basePadding in-place, corrupting basePadding.bottom before it is used in the assertion on line 969. This makes the comparison toBeGreaterThan(basePadding.bottom) check the original left value instead of the original bottom value — defeating the intended regression check.

Code Review Run #12858b


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

const expandedPadding = getPadding(

// The explicit List selection is honored end-to-end (never flips).
expect(legend.type).toBe(LegendType.Plain);
expect(layout.effectiveType).toBe(LegendType.Plain);

// #38675's margin reservation is retained: the wrapped rows reserve a
// finite margin beyond the single-row baseline, so the grid shrinks to
// reduce clipping instead of the legend flipping to scroll.
expect(Number.isFinite(layout.effectiveMargin)).toBe(true);

const reservedPadding = getPadding(
true,
LegendOrientation.Bottom,
false,
false,
expandedLegendMargin,
layout.effectiveMargin,
false,
undefined,
undefined,
undefined,
true,
);
[expandedPadding.bottom, expandedPadding.left] = [
expandedPadding.left,
expandedPadding.bottom,
[reservedPadding.bottom, reservedPadding.left] = [
reservedPadding.left,
reservedPadding.bottom,
];
Comment on lines +963 to 966

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second swap on reservedPadding

The second destructuring-swap on lines 963-966 mutates reservedPadding after the first swap mutated basePadding. Since grid.bottom is asserted to equal reservedPadding.bottom on line 968, and both basePadding and reservedPadding are computed identically (both pass layout.effectiveMargin), swapping reservedPadding is unnecessary and conflates two separate concerns. It also means the value grid.bottom is checked against is reservedPadding.left, not reservedPadding.bottom.

Code Review Run #12858b


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them


expect(legend.type).toBe(LegendType.Scroll);
expect(grid.bottom).toBe(expectedPadding.bottom);
expect(grid.bottom).not.toBe(expandedPadding.bottom);
expect(grid.bottom).toBe(reservedPadding.bottom);
expect(grid.bottom as number).toBeGreaterThan(
basePadding.bottom as number,
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -916,40 +916,38 @@ describe('legend sorting', () => {
'Boston',
]);
});
});

test('falls back to scroll for zoomable top legends when toolbox space reduces available width', () => {
const narrowLegendData = [
createTestQueryData(
createTestData(
[
{
Alpha: 1,
Beta: 2,
Gamma: 3,
},
],
{ intervalMs: 300000000 },
),
test('honors an explicit List selection for zoomable top legends even when toolbox space reduces available width', () => {
const narrowLegendData = [
createTestQueryData(
createTestData(
[
{
Alpha: 1,
Beta: 2,
Gamma: 3,
},
],
{ intervalMs: 300000000 },
),
];
const chartProps = createTestChartProps({
width: 190 + TIMESERIES_CONSTANTS.legendTopRightOffset,
formData: {
...formData,
legendType: LegendType.Plain,
legendOrientation: LegendOrientation.Top,
showLegend: true,
zoomable: true,
},
queriesData: narrowLegendData,
});
),
];
const chartProps = createTestChartProps({
width: 190 + TIMESERIES_CONSTANTS.legendTopRightOffset,
formData: {
...formData,
legendType: LegendType.Plain,
legendOrientation: LegendOrientation.Top,
showLegend: true,
zoomable: true,
},
queriesData: narrowLegendData,
});

const transformed = transformProps(chartProps);
const transformed = transformProps(chartProps);

expect((transformed.echartOptions.legend as any).type).toBe(
LegendType.Scroll,
);
});
expect((transformed.echartOptions.legend as any).type).toBe(LegendType.Plain);
});

test('honors user-selected plain legend type for top orientation when space allows (#39540)', () => {
Expand Down
Loading
Loading