AutoFilter: grow the range when a value is entered in the row below it (#2342)

This commit is contained in:
MarekSpodBiedry
2026-10-23 17:36:39 +02:00
parent 168acc5889
commit 599762f534
2 changed files with 47 additions and 1 deletions
+42
View File
@@ -652,6 +652,24 @@ fn filter_blank(v: &Value) -> bool {
v.is_empty() || matches!(v, Value::Text(t) if t.is_empty())
}
/// Excel grows an AutoFilter range when a value is entered in the row right below it, in one of its columns (#2342).
/// A table's own filter is left to the table, and a row typed after a blank row does not count.
/// A formula counts before it is calculated: this runs inside the edit, ahead of the recalc.
pub(crate) fn grow_autofilter(sh: &mut Sheet, at: CellRef) {
if !sh.cells.get(at).is_some_and(|c| !c.value.is_empty() || c.formula.is_some()) {
return;
}
let Some(af) = sh.autofilter.as_ref() else { return };
let r = af.range;
if r.end.row.checked_add(1) != Some(at.row) || at.col < r.start.col || at.col > r.end.col || sh.tables.iter().any(|t| t.range == r) {
return;
}
let grown = RangeRef::new(r.start, CellRef::new(at.row, r.end.col));
if let Some(af) = sh.autofilter.as_mut() {
af.range = grown;
}
}
/// Hides rows of the autofilter range that fail any criterion.
pub(crate) fn apply_filter(wb: &mut Workbook, sheet: usize) {
let Some(sh) = wb.sheet(sheet) else { return };
@@ -1554,6 +1572,30 @@ mod tests {
}
}
#[test]
fn autofilter_range_grows_when_a_value_is_entered_in_the_row_below() {
let mut s = Session::new();
s.new_workbook();
s.execute("range.setValues", json!({"range": "A1", "values": [["Name", "Qty"], ["d", 4], ["b", 2]]})).unwrap();
s.execute("data.filter", json!({"range": "A1:B3"})).unwrap();
let range = |s: &Session| s.doc().unwrap().wb.active().unwrap().autofilter.as_ref().map(|a| a.range.a1());
s.execute("cell.set", json!({"cell": "A4", "input": "e"})).unwrap();
assert_eq!(range(&s), Some("A1:B4".to_string()));
s.execute("range.setValues", json!({"range": "A5", "values": [["f", 6]]})).unwrap();
assert_eq!(range(&s), Some("A1:B5".to_string()));
// A row typed after a blank row does not grow it.
s.execute("cell.set", json!({"cell": "A7", "input": "x"})).unwrap();
assert_eq!(range(&s), Some("A1:B5".to_string()));
// A cell outside the filter's columns does not either.
s.execute("cell.set", json!({"cell": "C6", "input": "x"})).unwrap();
assert_eq!(range(&s), Some("A1:B5".to_string()));
// A formula and Ctrl+Enter (range.fill) are entries too.
s.execute("cell.set", json!({"cell": "B6", "input": "=1+1"})).unwrap();
assert_eq!(range(&s), Some("A1:B6".to_string()));
s.execute("range.fill", json!({"range": "A7:B7", "input": "g"})).unwrap();
assert_eq!(range(&s), Some("A1:B7".to_string()));
}
#[test]
fn list_validation_without_formula1_is_an_error() {
let mut s = Session::new();
+5 -1
View File
@@ -272,6 +272,7 @@ fn cell_set(s: &mut Session, p: &Json) -> Result<Json> {
sh.remove_cell(at);
}
}
super::data::grow_autofilter(sh, at);
cx.touch(sheet, at);
Ok(Json::Null)
})?;
@@ -350,6 +351,7 @@ fn range_set_values(s: &mut Session, p: &Json) -> Result<Json> {
sh.remove_cell(at);
}
}
super::data::grow_autofilter(sh, at);
cx.touch(sheet, at);
count += 1;
}
@@ -390,7 +392,9 @@ fn range_fill(s: &mut Session, p: &Json) -> Result<Json> {
let shifted = gridcraft_formula::adjust::shift_relative(e, at.row as i64 - origin.row as i64, at.col as i64 - origin.col as i64);
cell.formula = Some(std::sync::Arc::new(Formula::from_expr(shifted)));
}
cx.sheet_mut(sheet)?.set_cell(at, cell);
let sh = cx.sheet_mut(sheet)?;
sh.set_cell(at, cell);
super::data::grow_autofilter(sh, at);
cx.touch(sheet, at);
}
}