Skip to content

Commit e48889f

Browse files
committed
[IMP] Data filter: Trim whitespace
[Data filter] Trim whitespace and don't bother of caps vs low case when filtering Changes done in the filters by values and by criterion Task:5375214
1 parent 04b8575 commit e48889f

File tree

5 files changed

+51
-12
lines changed

5 files changed

+51
-12
lines changed

packages/o-spreadsheet-engine/src/helpers/text_helper.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,11 @@ export function toLowerCase(str: string | undefined): string {
297297
return str ? str.toLowerCase() : "";
298298
}
299299

300+
/** Transform a string to lowercase and removes whitespace from both ends of the string*/
301+
export function normalize(value: string): string {
302+
return toLowerCase(value).trim();
303+
}
304+
300305
/**
301306
* Extract the fontSize from a context font string
302307
* @param font The (context) font string to parse

packages/o-spreadsheet-engine/src/plugins/ui_stateful/filter_evaluation.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { isMultipleElementMatrix, toScalar } from "../../functions/helper_matric
22
import { parseLiteral } from "../../helpers/cells/cell_evaluation";
33
import { toXC } from "../../helpers/coordinates";
44
import { deepCopy, getUniqueText, range } from "../../helpers/misc";
5-
import { toLowerCase } from "../../helpers/text_helper";
5+
import { normalize } from "../../helpers/text_helper";
66
import { positions, toZone, zoneToDimension } from "../../helpers/zones";
77
import { criterionEvaluatorRegistry } from "../../registries/criterion_registry";
88
import { Command, CommandResult, LocalCommand, UpdateFilterCommand } from "../../types/commands";
@@ -163,7 +163,7 @@ export class FilterEvaluationPlugin extends UIPlugin {
163163
continue;
164164
}
165165
if (filterValue.filterType === "values") {
166-
const filteredValues = filterValue.hiddenValues?.map(toLowerCase);
166+
const filteredValues = filterValue.hiddenValues?.map(normalize);
167167
if (!filteredValues) continue;
168168
const filteredValuesSet = new Set(filteredValues);
169169
for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
@@ -205,7 +205,7 @@ export class FilterEvaluationPlugin extends UIPlugin {
205205

206206
private getCellValueAsString(sheetId: UID, col: number, row: number): string {
207207
const value = this.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue;
208-
return value.toLowerCase();
208+
return normalize(value);
209209
}
210210

211211
exportForExcel(data: ExcelWorkbookData) {

packages/o-spreadsheet-engine/src/registries/criterion_registry.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { detectLink } from "../helpers/links";
2020
import { localizeContent } from "../helpers/locale";
2121
import { isNumberBetween } from "../helpers/misc";
2222
import { rangeReference } from "../helpers/references";
23+
import { normalize } from "../helpers/text_helper";
2324
import { _t } from "../translation";
2425
import { CellValue } from "../types/cells";
2526
import {
@@ -115,8 +116,8 @@ criterionEvaluatorRegistry.add("notContainsText", {
115116
criterionEvaluatorRegistry.add("isEqualText", {
116117
type: "isEqualText",
117118
isValueValid: (value: CellValue, criterion: EvaluatedCriterion) => {
118-
const strValue = String(value);
119-
return strValue.toLowerCase() === String(criterion.values[0]).toLowerCase();
119+
const strValue = normalize(String(value));
120+
return strValue === normalize(String(criterion.values[0]));
120121
},
121122
getErrorString: (criterion: EvaluatedCriterion) => {
122123
return _t('The value must be exactly "%s"', String(criterion.values[0]));
@@ -663,8 +664,8 @@ criterionEvaluatorRegistry.add("customFormula", {
663664
criterionEvaluatorRegistry.add("beginsWithText", {
664665
type: "beginsWithText",
665666
isValueValid: (value: CellValue, criterion: EvaluatedCriterion) => {
666-
const strValue = String(value);
667-
return strValue.toLowerCase().startsWith(String(criterion.values[0]).toLowerCase());
667+
const strValue = normalize(String(value));
668+
return strValue.startsWith(normalize(String(criterion.values[0])));
668669
},
669670
getErrorString: (criterion: EvaluatedCriterion) => {
670671
return _t('The value must be a text that begins with "%s"', String(criterion.values[0]));
@@ -679,8 +680,8 @@ criterionEvaluatorRegistry.add("beginsWithText", {
679680
criterionEvaluatorRegistry.add("endsWithText", {
680681
type: "endsWithText",
681682
isValueValid: (value: CellValue, criterion: EvaluatedCriterion) => {
682-
const strValue = String(value);
683-
return strValue.toLowerCase().endsWith(String(criterion.values[0]).toLowerCase());
683+
const strValue = normalize(String(value));
684+
return strValue.endsWith(normalize(String(criterion.values[0])));
684685
},
685686
getErrorString: (criterion: EvaluatedCriterion) => {
686687
return _t('The value must be a text that ends with "%s"', String(criterion.values[0]));

src/components/filters/filter_menu_value_list/filter_menu_value_list.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SpreadsheetChildEnv } from "@odoo/o-spreadsheet-engine/types/spreadsheet_env";
22
import { Component, onWillUpdateProps, useRef, useState } from "@odoo/owl";
3-
import { deepEquals, positions, toLowerCase } from "../../../helpers";
3+
import { deepEquals, normalize, positions } from "../../../helpers";
44
import { fuzzyLookup } from "../../../helpers/search";
55
import { Position } from "../../../types";
66
import { FilterMenuValueItem } from "../filter_menu_item/filter_menu_value_item";
@@ -76,12 +76,12 @@ export class FilterMenuValueList extends Component<Props, SpreadsheetChildEnv> {
7676

7777
const cellValues = cells.map((val) => val.cellValue);
7878
const filterValues = filterValue?.filterType === "values" ? filterValue.hiddenValues : [];
79-
const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
79+
const normalizedFilteredValues = new Set(filterValues.map(normalize));
8080

8181
const set = new Set<string>();
8282
const values: (Value & { normalizedValue: string })[] = [];
8383
const addValue = (value: string) => {
84-
const normalizedValue = toLowerCase(value);
84+
const normalizedValue = normalize(value);
8585
if (!set.has(normalizedValue)) {
8686
values.push({
8787
string: value || "",

tests/table/filter_evaluation_plugin.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ describe("Filter Evaluation", () => {
161161
expect(model.getters.isRowHidden(sheetId, 2)).toEqual(true);
162162
});
163163

164+
test("Filters ignore whitespaces", () => {
165+
setCellContent(model, "A2", "a");
166+
setCellContent(model, "A3", " a");
167+
setCellContent(model, "A4", "a ");
168+
updateFilter(model, "A2", ["a"]);
169+
expect(model.getters.isRowHidden(sheetId, 1)).toEqual(true);
170+
expect(model.getters.isRowHidden(sheetId, 2)).toEqual(true);
171+
expect(model.getters.isRowHidden(sheetId, 3)).toEqual(true);
172+
});
164173
test("Header is not filtered", () => {
165174
updateFilter(model, "A1", ["A1"]);
166175
expect(model.getters.isRowHidden(sheetId, 0)).toEqual(false);
@@ -352,6 +361,30 @@ describe("Filter criterion test", () => {
352361
return range(0, 10).filter((row) => model.getters.isRowFiltered(sheetId, row));
353362
}
354363

364+
test.each([
365+
["beginsWithText", ["a"], []],
366+
["endsWithText", ["a"], []],
367+
["isEqualText", ["a"], []],
368+
])(
369+
"Can filter based on a text criterion %s ignoring lowercase/uppercase and whitespaces",
370+
(type: string, criterionValues: string[], expectedFilteredRows: number[]) => {
371+
const grid = {
372+
A2: "a",
373+
A3: " a",
374+
A4: "a ",
375+
A5: "A",
376+
};
377+
setGrid(model, grid);
378+
createTableWithFilter(model, "A1:A5");
379+
updateFilterCriterion(model, "A1", {
380+
type: type as FilterCriterionType,
381+
values: criterionValues,
382+
});
383+
384+
expect(getFilteredRows()).toEqual(expectedFilteredRows);
385+
}
386+
);
387+
355388
test.each([
356389
["isEmpty", [], [1, 2]],
357390
["isNotEmpty", [], [3]],

0 commit comments

Comments
 (0)