Skip to content

Commit b480f9f

Browse files
committed
[FIX] Datavalidation: coreView plugin should not dispatch
See #5216 - CoreView plugins replay remote revisions and should not take the risk to dispatch during the replay. The code that was assigning arbitrary default values to the cells of a boolean datavalidation has been moved to a UI feature plugin, hence avoiding the problem raised in PR #5216. closes #5217 Task: 4241141 X-original-commit: fd31d5f Signed-off-by: Pierre Rousseau (pro) <[email protected]> Signed-off-by: Rémi Rahir (rar) <[email protected]>
1 parent 96d4b69 commit b480f9f

File tree

4 files changed

+82
-16
lines changed

4 files changed

+82
-16
lines changed

src/plugins/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
UIOptionsPlugin,
4343
} from "./ui_feature";
4444
import { CellComputedStylePlugin } from "./ui_feature/cell_computed_style";
45+
import { DataValidationInsertionPlugin } from "./ui_feature/datavalidation_insertion";
4546
import { HistoryPlugin } from "./ui_feature/local_history";
4647
import { SplitToColumnsPlugin } from "./ui_feature/split_to_columns";
4748
import { TableAutofillPlugin } from "./ui_feature/table_autofill";
@@ -89,7 +90,8 @@ export const featurePluginRegistry = new Registry<UIPluginConstructor>()
8990
.add("history", HistoryPlugin)
9091
.add("data_cleanup", DataCleanupPlugin)
9192
.add("table_autofill", TableAutofillPlugin)
92-
.add("table_ui_resize", TableResizeUI);
93+
.add("table_ui_resize", TableResizeUI)
94+
.add("datavalidation_insert", DataValidationInsertionPlugin);
9395

9496
// Plugins which have a state, but which should not be shared in collaborative
9597
export const statefulUIPluginRegistry = new Registry<UIPluginConstructor>()

src/plugins/ui_core_views/evaluation_data_validation.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,27 +56,12 @@ export class EvaluationDataValidationPlugin extends UIPlugin {
5656
}
5757
switch (cmd.type) {
5858
case "ADD_DATA_VALIDATION_RULE":
59-
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
60-
if (cmd.rule.criterion.type === "isBoolean") {
61-
this.setContentToBooleanCells({ ...cmd.rule, ranges });
62-
}
63-
delete this.validationResults[cmd.sheetId];
64-
break;
6559
case "REMOVE_DATA_VALIDATION_RULE":
6660
delete this.validationResults[cmd.sheetId];
6761
break;
6862
}
6963
}
7064

71-
private setContentToBooleanCells(rule: DataValidationRule) {
72-
for (const position of getCellPositionsInRanges(rule.ranges)) {
73-
const evaluatedCell = this.getters.getEvaluatedCell(position);
74-
if (evaluatedCell.type !== CellValueType.boolean) {
75-
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
76-
}
77-
}
78-
}
79-
8065
isDataValidationInvalid(cellPosition: CellPosition): boolean {
8166
return !this.getValidationResultForCell(cellPosition).isValid;
8267
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { getCellPositionsInRanges, isBoolean } from "../../helpers";
2+
import { CellValueType, Command, isMatrix } from "../../types/index";
3+
import { UIPlugin } from "../ui_plugin";
4+
5+
export class DataValidationInsertionPlugin extends UIPlugin {
6+
handle(cmd: Command) {
7+
switch (cmd.type) {
8+
case "ADD_DATA_VALIDATION_RULE":
9+
if (cmd.rule.criterion.type === "isBoolean") {
10+
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
11+
for (const position of getCellPositionsInRanges(ranges)) {
12+
const cell = this.getters.getCell(position);
13+
const evaluatedCell = this.getters.getEvaluatedCell(position);
14+
15+
if (!cell?.content) {
16+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
17+
// In this case, a cell has been updated in the core plugin but
18+
// not yet evaluated. This can occur after a paste operation.
19+
} else if (cell?.content && evaluatedCell.type === CellValueType.empty) {
20+
let value: string | undefined;
21+
if (cell.content.startsWith("=")) {
22+
const result = this.getters.evaluateFormula(position.sheetId, cell.content);
23+
value = (isMatrix(result) ? result[0][0] : result)?.toString();
24+
} else {
25+
value = cell.content;
26+
}
27+
if (!value || !isBoolean(value)) {
28+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
29+
}
30+
} else if (evaluatedCell.type !== CellValueType.boolean) {
31+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
32+
}
33+
}
34+
}
35+
}
36+
}
37+
}

tests/collaborative/collaborative_clipboard.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { LineChartDefinition } from "../../src/types/chart";
33
import { MockTransportService } from "../__mocks__/transport_service";
44
import {
55
addColumns,
6+
addDataValidation,
67
copy,
78
createChart,
89
createSheet,
@@ -123,4 +124,45 @@ describe("Collaborative range manipulation", () => {
123124
"D4"
124125
);
125126
});
127+
128+
test("Can copy boolean datavalidation while preserving the cell values", () => {
129+
setCellContent(alice, "A1", "TRUE");
130+
setCellContent(alice, "A3", "not a boolean");
131+
setCellContent(alice, "A4", "=TRANSPOSE(A1)");
132+
setCellContent(alice, "A5", "=TEXT(5)");
133+
setCellContent(alice, "A6", "=NOT(A1)");
134+
setCellContent(alice, "A7", "7");
135+
addDataValidation(alice, "A1:A7", "id", { type: "isBoolean", values: [] });
136+
137+
copy(alice, "A1:A7");
138+
paste(alice, "B1");
139+
expect([alice, bob, charlie]).toHaveSynchronizedValue(
140+
(user) => getCell(user, "B1")?.content,
141+
"TRUE"
142+
);
143+
expect([alice, bob, charlie]).toHaveSynchronizedValue(
144+
(user) => getCell(user, "B2")?.content,
145+
"FALSE" // A2 was empty, which is falsy
146+
);
147+
expect([alice, bob, charlie]).toHaveSynchronizedValue(
148+
(user) => getCell(user, "B3")?.content,
149+
"FALSE" // text is not a boolean -> falsy
150+
);
151+
expect([alice, bob, charlie]).toHaveSynchronizedValue(
152+
(user) => getCell(user, "B4")?.content,
153+
"=TRANSPOSE(B1)" // is truthy
154+
);
155+
expect([alice, bob, charlie]).toHaveSynchronizedValue(
156+
(user) => getCell(user, "B5")?.content,
157+
"FALSE" // text is not a boolean -> falsy
158+
);
159+
expect([alice, bob, charlie]).toHaveSynchronizedValue(
160+
(user) => getCell(user, "B6")?.content,
161+
"=NOT(B1)"
162+
);
163+
expect([alice, bob, charlie]).toHaveSynchronizedValue(
164+
(user) => getCell(user, "B7")?.content,
165+
"FALSE" // a number which does not represent a boolean is falsy
166+
);
167+
});
126168
});

0 commit comments

Comments
 (0)