Skip to content

Commit f3b73bc

Browse files
tomivirkkiclaude
andauthored
fix: set PlotOptionsFlags on flags data series (#9097)
## Summary - When the chart-level type differs from `flags` (e.g. `column`), a `DataSeries` containing `FlagItem` objects would not render as flags because it lacked `PlotOptionsFlags` - Adds `applyFlagsPlotOptions()` to `ChartRenderer` that auto-detects flag series by their data items and sets `PlotOptionsFlags` when missing, while preserving any pre-configured plot options 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 04b261d commit f3b73bc

2 files changed

Lines changed: 73 additions & 0 deletions

File tree

vaadin-ai-components-flow-parent/vaadin-ai-components-flow/src/main/java/com/vaadin/flow/component/ai/chart/ChartRenderer.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
import com.vaadin.flow.component.charts.model.Configuration;
3232
import com.vaadin.flow.component.charts.model.DataSeries;
3333
import com.vaadin.flow.component.charts.model.DataSeriesItem;
34+
import com.vaadin.flow.component.charts.model.FlagItem;
3435
import com.vaadin.flow.component.charts.model.GanttSeries;
36+
import com.vaadin.flow.component.charts.model.PlotOptionsFlags;
3537
import com.vaadin.flow.component.charts.model.Series;
3638

3739
/**
@@ -94,6 +96,7 @@ public static void renderChart(Chart chart,
9496
nameUnnamedSeries(config, allSeries);
9597

9698
applySeriesConfig(allSeries, seriesConfig);
99+
applyFlagsPlotOptions(allSeries);
97100

98101
// Apply axis defaults from series data after LLM config,
99102
// so that data-driven axis type detection (e.g. datetime)
@@ -222,6 +225,27 @@ private static void nameUnnamedSeries(Configuration config,
222225
}
223226
}
224227

228+
/**
229+
* Ensures any DataSeries containing FlagItem objects has PlotOptionsFlags
230+
* set, so flags render correctly even when the chart-level type is
231+
* different (e.g. column).
232+
*/
233+
private static void applyFlagsPlotOptions(List<Series> allSeries) {
234+
for (var series : allSeries) {
235+
if (!(series instanceof DataSeries ds)) {
236+
continue;
237+
}
238+
if (ds.getPlotOptions() instanceof PlotOptionsFlags) {
239+
continue;
240+
}
241+
boolean hasFlags = ds.getData().stream()
242+
.anyMatch(FlagItem.class::isInstance);
243+
if (hasFlags) {
244+
ds.setPlotOptions(new PlotOptionsFlags());
245+
}
246+
}
247+
}
248+
225249
/**
226250
* Extracts per-series configuration (plot options, y-axis) from the
227251
* configuration's current series, keyed by series name. These are

vaadin-ai-components-flow-parent/vaadin-ai-components-flow/src/test/java/com/vaadin/flow/component/ai/chart/ChartRenderingTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.vaadin.flow.component.charts.model.DataSeries;
3636
import com.vaadin.flow.component.charts.model.DataSeriesItem;
3737
import com.vaadin.flow.component.charts.model.OhlcItem;
38+
import com.vaadin.flow.component.charts.model.PlotOptionsFlags;
3839
import com.vaadin.flow.component.charts.util.ChartSerialization;
3940
import com.vaadin.tests.MockUIExtension;
4041

@@ -1046,6 +1047,54 @@ void waterfallWithSumItemsCategoriesIncludeSumNames() {
10461047

10471048
// --- Helpers ---
10481049

1050+
@Nested
1051+
class FlagsChart {
1052+
1053+
@Test
1054+
void flagsSeriesRendersAsFlagsOnColumnChart() {
1055+
databaseProvider.results = List
1056+
.of(row("_title", "Launch", "_text", "Product launch"));
1057+
1058+
updateConfiguration("{\"chart\":{\"type\":\"column\"}}");
1059+
updateData("SELECT title AS _title, text AS _text FROM flags");
1060+
controller.onRequestCompleted();
1061+
1062+
var series = chart.getConfiguration().getSeries();
1063+
Assertions.assertEquals(1, series.size());
1064+
1065+
var flagsSeries = (DataSeries) series.getFirst();
1066+
Assertions.assertInstanceOf(PlotOptionsFlags.class,
1067+
flagsSeries.getPlotOptions(),
1068+
"Flags series should have PlotOptionsFlags "
1069+
+ "regardless of chart-level type");
1070+
}
1071+
1072+
@Test
1073+
void flagsSeriesPreservesExistingPlotOptionsFlags() {
1074+
databaseProvider.results = List
1075+
.of(row("_title", "Launch", "_text", "Product launch"));
1076+
1077+
updateConfiguration("{\"chart\":{\"type\":\"line\"},"
1078+
+ "\"title\":{\"text\":\"Events\"},"
1079+
+ "\"series\":[{\"name\":\"Events\","
1080+
+ "\"type\":\"flags\","
1081+
+ "\"plotOptions\":{\"onSeries\":\"price\"}}]}");
1082+
updateData("SELECT title AS _title, text AS _text FROM flags");
1083+
controller.onRequestCompleted();
1084+
1085+
var series = chart.getConfiguration().getSeries();
1086+
Assertions.assertEquals(1, series.size());
1087+
1088+
var flagsSeries = (DataSeries) series.getFirst();
1089+
var plotOptions = flagsSeries.getPlotOptions();
1090+
Assertions.assertInstanceOf(PlotOptionsFlags.class, plotOptions,
1091+
"Flags series should have PlotOptionsFlags");
1092+
Assertions.assertEquals("price",
1093+
((PlotOptionsFlags) plotOptions).getOnSeries(),
1094+
"Pre-configured onSeries should be preserved");
1095+
}
1096+
}
1097+
10491098
private static Map<String, Object> row(Object... kvPairs) {
10501099
Map<String, Object> map = new LinkedHashMap<>();
10511100
for (int i = 0; i < kvPairs.length; i += 2) {

0 commit comments

Comments
 (0)