diff --git a/crates/engine/src/cmd/edit/paste_special.rs b/crates/engine/src/cmd/edit/paste_special.rs index 6c3f6baa4..c9e296ba5 100644 --- a/crates/engine/src/cmd/edit/paste_special.rs +++ b/crates/engine/src/cmd/edit/paste_special.rs @@ -358,12 +358,12 @@ pub(crate) fn rewrite_all_formulas(wb: &mut gridcraft_model::Workbook, target: & rewrite_cell_links(wb, |host, expr| gridcraft_formula::adjust::adjust(expr, host, target, e)); } -/// Runs `f` over every reference a floating object holds (a chart's series values, categories -/// and name, and the source block it was made from). Every edit that moves references calls it: -/// inserting and deleting lines and cells and cut-moves (through [`rewrite_all_formulas`]), and -/// renaming and deleting sheets. `f` maps a reference held by an object on the sheet named `host` -/// to its new form; text that does not parse is kept. A source block that no longer exists is -/// dropped. +/// Runs `f` over every reference a floating object holds (a chart's series values, categories, +/// name and bubble sizes, and the source block it was made from). Every edit that moves +/// references calls it: inserting and deleting lines and cells and cut-moves (through +/// [`rewrite_all_formulas`]), and renaming and deleting sheets. `f` maps a reference held by an +/// object on the sheet named `host` to its new form; text that does not parse is kept. A source +/// block that no longer exists is dropped. pub(crate) fn rewrite_cell_links(wb: &mut gridcraft_model::Workbook, f: impl Fn(&str, gridcraft_formula::Expr) -> gridcraft_formula::Expr) { for si in 0..wb.sheets.len() { let Some(host) = wb.sheets.get(si).filter(|s| !s.charts.is_empty()).map(|s| s.name.clone()) else { continue }; @@ -380,6 +380,7 @@ pub(crate) fn rewrite_cell_links(wb: &mut gridcraft_model::Workbook, f: impl Fn( se.values = fix(&se.values); se.categories = se.categories.as_deref().map(fix); se.name = se.name.as_deref().map(fix); + se.bubble_sizes = se.bubble_sizes.as_deref().map(fix); } } } diff --git a/crates/engine/src/tests/objects.rs b/crates/engine/src/tests/objects.rs index d0c2eba44..7082b9054 100644 --- a/crates/engine/src/tests/objects.rs +++ b/crates/engine/src/tests/objects.rs @@ -190,3 +190,20 @@ fn a_copied_sheet_gives_its_objects_fresh_ids() { } assert!(s.doc().unwrap().wb.next_object_id() > *ids.last().unwrap_or(&0)); } + +#[test] +fn bubble_sizes_follow_edits_like_the_other_series_references() { + let mut s = s(); + s.execute("range.setValues", json!({"range": "A1", "values": [[1, 3, 10], [2, 5, 20], [3, 2, 30]]})).unwrap(); + let chart = id_of(&s.execute("insert.chart", json!({"range": "A1:B3", "type": "bubble"})).unwrap(), "chart"); + std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().wb).sheet_mut(0).unwrap().charts.iter_mut().find(|c| c.id == chart).unwrap().series[0] + .bubble_sizes = Some("Sheet1!$C$1:$C$3".into()); + let sizes = |s: &Session| sheet(s, 0).charts.iter().find(|c| c.id == chart).and_then(|c| c.series[0].bubble_sizes.clone()).unwrap_or_default(); + s.execute("home.insertRows", json!({"rows": "1:1"})).unwrap(); + assert_eq!(sizes(&s), "Sheet1!$C$2:$C$4", "an inserted row moves the bubble sizes with their cells"); + s.execute("edit.cut", json!({"range": "C2:C4"})).unwrap(); + s.execute("edit.paste", json!({"at": "F2"})).unwrap(); + assert_eq!(sizes(&s), "Sheet1!$F$2:$F$4", "a cut-move takes the bubble sizes with the cells"); + s.execute("sheet.rename", json!({"sheet": 0, "name": "Data"})).unwrap(); + assert_eq!(sizes(&s), "Data!$F$2:$F$4", "a renamed sheet is renamed in the bubble sizes"); +}