From 92b5ecdd9c32c71e0df029ba98837309aa130b03 Mon Sep 17 00:00:00 2001 From: Paola De Bartolo Date: Tue, 26 May 2026 16:33:30 -0300 Subject: [PATCH 1/3] fix: use last header row in CSV/DOCX/PDF exports --- .../com/flowingcode/vaadin/addons/gridexporter/GridHeader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridHeader.java b/src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridHeader.java index a635148..3481d71 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridHeader.java +++ b/src/main/java/com/flowingcode/vaadin/addons/gridexporter/GridHeader.java @@ -14,7 +14,7 @@ final class GridHeader implements GridHeaderOrFooter { @Override public String getText() { - return texts.get(0); + return texts.get(texts.size() - 1); } } From d9fae73298d783afca44c9921e4c9439d9097fc3 Mon Sep 17 00:00:00 2001 From: Paola De Bartolo Date: Tue, 26 May 2026 16:35:18 -0300 Subject: [PATCH 2/3] fix: apply custom header only to last header row in Excel --- .../gridexporter/BaseStreamResourceWriter.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/flowingcode/vaadin/addons/gridexporter/BaseStreamResourceWriter.java b/src/main/java/com/flowingcode/vaadin/addons/gridexporter/BaseStreamResourceWriter.java index 25ff8ff..b45c375 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/gridexporter/BaseStreamResourceWriter.java +++ b/src/main/java/com/flowingcode/vaadin/addons/gridexporter/BaseStreamResourceWriter.java @@ -103,9 +103,11 @@ protected List> getGridHeaders(Grid grid) { private GridHeader getGridHeader(Grid grid, Column column) { List headerTexts = new ArrayList<>(); List headerRows = grid.getHeaderRows(); - for (HeaderRow headerRow : headerRows) { - String headerText = renderHeaderCellTextContent(grid, headerRow, column); - headerTexts.add(headerText); + int lastIndex = headerRows.size() - 1; + for (int i = 0; i < headerRows.size(); i++) { + boolean isLastRow = (i == lastIndex); + String headerText = renderHeaderCellTextContent(grid, headerRows.get(i), column, isLastRow); + headerTexts.add(headerText); } return new GridHeader<>(headerTexts, column); } @@ -132,8 +134,11 @@ private String obtainCellFunction(HeaderCell headerCell, Column column) { return value; } - private String renderHeaderCellTextContent(Grid grid, HeaderRow headerRow, Column column) { - String header = (String) ComponentUtil.getData(column, GridExporter.COLUMN_HEADER); + private String renderHeaderCellTextContent(Grid grid, HeaderRow headerRow, Column column, + boolean isLastRow) { + String header = isLastRow + ? (String) ComponentUtil.getData(column, GridExporter.COLUMN_HEADER) + : null; if (Strings.isBlank(header)) { HeaderCell headerCell = headerRow.getCell(column); From 7ac9db7abad8ec67559759a35c4b729dc832c358 Mon Sep 17 00:00:00 2001 From: Paola De Bartolo Date: Tue, 26 May 2026 16:36:12 -0300 Subject: [PATCH 3/3] feat(demo): add demo for joined headers with custom header --- .../gridexporter/GridExporterDemoView.java | 1 + ...rterJoinedHeadersWithCustomHeaderDemo.java | 72 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterJoinedHeadersWithCustomHeaderDemo.java diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterDemoView.java b/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterDemoView.java index 288aeb7..496191d 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterDemoView.java +++ b/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterDemoView.java @@ -40,6 +40,7 @@ public GridExporterDemoView() { addDemo(GridExporterHierarchicalDataDemo.class); addDemo(GridExporterBigDatasetDemo.class); addDemo(GridExporterMultipleHeaderRowsDemo.class); + addDemo(GridExporterJoinedHeadersWithCustomHeaderDemo.class); setSizeFull(); } } diff --git a/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterJoinedHeadersWithCustomHeaderDemo.java b/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterJoinedHeadersWithCustomHeaderDemo.java new file mode 100644 index 0000000..47fc78e --- /dev/null +++ b/src/test/java/com/flowingcode/vaadin/addons/gridexporter/GridExporterJoinedHeadersWithCustomHeaderDemo.java @@ -0,0 +1,72 @@ +/*- + * #%L + * Grid Exporter Add-on + * %% + * Copyright (C) 2022 - 2026 Flowing Code + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.flowingcode.vaadin.addons.gridexporter; + +import com.flowingcode.vaadin.addons.demo.DemoSource; +import com.github.javafaker.Faker; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.grid.Grid.Column; +import com.vaadin.flow.component.grid.HeaderRow; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@DemoSource +@PageTitle("Joined Headers with Custom Header") +@Route(value = "gridexporter/joined-custom-header", layout = GridExporterDemoView.class) +@SuppressWarnings("serial") +public class GridExporterJoinedHeadersWithCustomHeaderDemo extends Div { + + public GridExporterJoinedHeadersWithCustomHeaderDemo() { + Grid grid = new Grid<>(Person.class); + grid.removeAllColumns(); + + Column firstNameCol = grid.addColumn("name").setHeader("First Name"); + Column lastNameCol = grid.addColumn("lastName").setHeader("Last Name"); + + Faker faker = FakerInstance.get(); + List people = IntStream.range(0, 20) + .mapToObj(i -> new Person(faker.name().firstName(), faker.name().lastName(), + faker.number().numberBetween(15, 50), + faker.number().randomDouble(2, 10000, 100000))) + .collect(Collectors.toList()); + grid.setItems(people); + + HeaderRow joinedHeaderRow = grid.prependHeaderRow(); + Div joinedCell = new Div("Full name"); + joinedCell.getStyle().set("text-align", "center"); + joinedHeaderRow.join(firstNameCol, lastNameCol).setComponent(joinedCell); + + GridExporter exporter = GridExporter.createFor(grid); + exporter.setTitle("People information"); + + // Custom headers are applied only to the header row closest to the data. + // CSV/DOCX/PDF export this single row; Excel keeps the joined "Full name" above it. + exporter.setCustomHeader(firstNameCol, "Given name"); + exporter.setCustomHeader(lastNameCol, "Family name"); + + grid.setWidthFull(); + setSizeFull(); + add(grid); + } +}