From 0ccc94edd34ebae3308ab8dff61d151a35dc8ef7 Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Mon, 12 Oct 2026 08:32:47 +0200 Subject: [PATCH] Save a table that has only a header row and a totals row (#511) --- crates/xlsx/src/sheet_write.rs | 2 +- crates/xlsx/src/tests/roundtrip.rs | 36 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/crates/xlsx/src/sheet_write.rs b/crates/xlsx/src/sheet_write.rs index 0bfcb7d..0020adf 100644 --- a/crates/xlsx/src/sheet_write.rs +++ b/crates/xlsx/src/sheet_write.rs @@ -81,7 +81,7 @@ pub fn write_sheet(wb: &Workbook, si: usize, selected: bool, out: &mut Out) -> ( let tables: Vec<(&Table, Vec)> = sheet .tables .iter() - .filter(|t| t.range.height() > t.header_row as u32 + t.totals_row as u32 || (t.header_row && !t.totals_row && t.range.height() >= 1)) + .filter(|t| t.range.height() > t.header_row as u32 + t.totals_row as u32 || (t.header_row && t.range.height() > t.totals_row as u32)) .map(|t| (t, table_columns(t))) .collect(); for (t, cols) in &tables { diff --git a/crates/xlsx/src/tests/roundtrip.rs b/crates/xlsx/src/tests/roundtrip.rs index 9601572..6d50c9d 100644 --- a/crates/xlsx/src/tests/roundtrip.rs +++ b/crates/xlsx/src/tests/roundtrip.rs @@ -988,6 +988,42 @@ fn table_newline_column_and_backslash_name_survive() { assert!(gridcraft_formula::parse("COUNTA(\\_Prime.1[Name])").is_ok(), "backslash table name parses as a structured reference"); } +/// A table that is only a header row and a totals row (Insert Table on one row, then Total +/// Row) is written like the header-only and one-data-row shapes, not dropped on save. +#[test] +fn header_and_totals_table_without_data_row_survives() { + let mut wb = Workbook::new(); + let s = wb.sheet_mut(0).unwrap(); + s.tables.push(Table { + id: 1, + name: "Table1".into(), + range: rr("A1:B2"), + header_row: true, + totals_row: true, + columns: vec![ + TableColumn { name: "Name".into(), totals: TotalsFn::None, totals_label: Some("Total".into()), formula: None }, + TableColumn { name: "Amount".into(), totals: TotalsFn::Sum, totals_label: None, formula: None }, + ], + style: "TableStyleMedium2".into(), + banded_rows: true, + banded_cols: false, + first_col: true, + last_col: true, + filter_button: true, + filter: vec![], + }); + let (back, report) = read_xlsx(&write_xlsx(&wb).unwrap()).unwrap(); + assert!(report.warnings.is_empty(), "{:?}", report.warnings); + let tables = &back.sheet(0).unwrap().tables; + assert_eq!(tables.len(), 1); + let t = &tables[0]; + assert_eq!((t.name.as_str(), t.range.a1(), t.header_row, t.totals_row), ("Table1", "A1:B2".to_string(), true, true)); + assert_eq!((t.style.as_str(), t.banded_rows, t.first_col, t.last_col), ("TableStyleMedium2", true, true, true)); + assert_eq!(t.columns[0].totals_label.as_deref(), Some("Total")); + assert_eq!(t.columns[1].totals, TotalsFn::Sum); + assert_eq!(back.sheet(0).unwrap().value(at("A1")), Value::text("Name")); +} + /// Templates and macro-enabled files differ from `.xlsx` only in the workbook part's content /// type; a template saved with the workbook type is rejected by Excel. #[test]