@odoo/o-spreadsheet 14.0.0 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +20 -0
- package/.husky/post-checkout +9 -0
- package/.husky/pre-commit +39 -0
- package/.prettierignore +1 -0
- package/astToFormula.patch +0 -0
- package/demo/data.js +255 -0
- package/demo/index.html +9 -0
- package/demo/libs/Chart.bundle.js +20755 -0
- package/demo/main.js +101 -0
- package/doc/readme.md +46 -0
- package/package.json +3 -17
- package/rollup.config.js +23 -0
- package/src/base_plugin.ts +102 -0
- package/src/components/autofill.ts +146 -0
- package/src/components/bottom_bar.ts +199 -0
- package/src/components/color_picker.ts +150 -0
- package/src/components/composer/autocomplete_dropdown.ts +135 -0
- package/src/components/composer/composer.ts +487 -0
- package/src/components/composer/content_editable_helper.ts +146 -0
- package/src/components/figures/chart.ts +70 -0
- package/src/components/figures/container.ts +268 -0
- package/src/components/figures/text_figure.ts +36 -0
- package/src/components/grid.ts +635 -0
- package/src/components/helpers/dom_helpers.ts +7 -0
- package/src/components/helpers/drag_and_drop.ts +17 -0
- package/src/components/icons.ts +36 -0
- package/src/components/index.ts +1 -0
- package/src/components/menu.ts +288 -0
- package/src/components/overlay.ts +457 -0
- package/src/components/scrollbar.ts +22 -0
- package/src/components/selection_input.ts +168 -0
- package/src/components/side_panel/cell_is_rule_editor.ts +169 -0
- package/src/components/side_panel/chart_panel.ts +104 -0
- package/src/components/side_panel/color_scale_rule_editor.ts +145 -0
- package/src/components/side_panel/conditional_formatting.ts +396 -0
- package/src/components/side_panel/side_panel.ts +127 -0
- package/src/components/side_panel/translations_terms.ts +45 -0
- package/src/components/spreadsheet.ts +198 -0
- package/src/components/top_bar.ts +535 -0
- package/src/constants.ts +26 -0
- package/src/data.ts +149 -0
- package/src/fonts.ts +41 -0
- package/src/formulas/compiler.ts +258 -0
- package/src/formulas/composer_tokenizer.ts +70 -0
- package/src/formulas/index.ts +15 -0
- package/src/formulas/normalize.ts +52 -0
- package/src/formulas/parser.ts +280 -0
- package/src/formulas/range_tokenizer.ts +97 -0
- package/src/formulas/tokenizer.ts +242 -0
- package/src/functions/arguments.ts +114 -0
- package/src/functions/dates.ts +302 -0
- package/src/functions/helpers.ts +592 -0
- package/src/functions/index.ts +52 -0
- package/src/functions/module_database.ts +282 -0
- package/src/functions/module_date.ts +743 -0
- package/src/functions/module_info.ts +69 -0
- package/src/functions/module_logical.ts +208 -0
- package/src/functions/module_lookup.ts +185 -0
- package/src/functions/module_math.ts +1252 -0
- package/src/functions/module_operators.ts +291 -0
- package/src/functions/module_statistical.ts +882 -0
- package/src/functions/module_text.ts +370 -0
- package/src/helpers/color.ts +30 -0
- package/src/helpers/coordinates.ts +71 -0
- package/src/helpers/grid_manipulation.ts +68 -0
- package/src/helpers/index.ts +7 -0
- package/src/helpers/misc.ts +76 -0
- package/src/helpers/numbers.ts +117 -0
- package/src/helpers/uuid.ts +19 -0
- package/src/helpers/zones.ts +263 -0
- package/src/history.ts +186 -0
- package/src/index.ts +84 -0
- package/src/model.ts +261 -0
- package/src/plugins/autofill.ts +412 -0
- package/src/plugins/chart.ts +404 -0
- package/src/plugins/clipboard.ts +461 -0
- package/src/plugins/conditional_format.ts +445 -0
- package/src/plugins/core.ts +1502 -0
- package/src/plugins/edition.ts +157 -0
- package/src/plugins/evaluation.ts +428 -0
- package/src/plugins/figures.ts +123 -0
- package/src/plugins/formatting.ts +630 -0
- package/src/plugins/highlight.ts +187 -0
- package/src/plugins/index.ts +32 -0
- package/src/plugins/merge.ts +524 -0
- package/src/plugins/renderer.ts +571 -0
- package/src/plugins/selection.ts +562 -0
- package/src/plugins/selection_inputs.ts +326 -0
- package/src/registries/autofill_modifiers.ts +76 -0
- package/src/registries/autofill_rules.ts +98 -0
- package/src/registries/figure_registry.ts +30 -0
- package/src/registries/index.ts +7 -0
- package/src/registries/menu_items_registry.ts +110 -0
- package/src/registries/menus/cell_menu_registry.ts +79 -0
- package/src/registries/menus/col_menu_registry.ts +63 -0
- package/src/registries/menus/index.ts +6 -0
- package/src/registries/menus/menu_items_actions.ts +425 -0
- package/src/registries/menus/row_menu_registry.ts +63 -0
- package/src/registries/menus/sheet_menu_registry.ts +75 -0
- package/src/registries/menus/topbar_menu_registry.ts +204 -0
- package/src/registries/side_panel_registry.ts +26 -0
- package/src/registries/topbar_component_registry.ts +12 -0
- package/src/registry.ts +51 -0
- package/src/translation.ts +44 -0
- package/src/types/autofill.ts +48 -0
- package/src/types/chart.ts +26 -0
- package/src/types/commands.ts +716 -0
- package/src/types/conditional_formatting.ts +124 -0
- package/src/types/env.ts +13 -0
- package/src/types/functions.ts +35 -0
- package/src/types/getters.ts +103 -0
- package/src/types/index.ts +20 -0
- package/src/types/misc.ts +143 -0
- package/src/types/rendering.ts +41 -0
- package/src/types/workbook_data.ts +62 -0
- package/tests/__mocks__/uuid.ts +7 -0
- package/tests/canvas.mock.ts +34 -0
- package/tests/components/__mocks__/content_editable_helper.ts +79 -0
- package/tests/components/__mocks__/scrollbar.ts +18 -0
- package/tests/components/__snapshots__/autocomplete_dropdown_test.ts.snap +29 -0
- package/tests/components/__snapshots__/bottom_bar_test.ts.snap +75 -0
- package/tests/components/__snapshots__/context_menu_test.ts.snap +88 -0
- package/tests/components/__snapshots__/grid_test.ts.snap +60 -0
- package/tests/components/__snapshots__/spreadsheet_test.ts.snap +455 -0
- package/tests/components/__snapshots__/top_bar_test.ts.snap +705 -0
- package/tests/components/autocomplete_dropdown_test.ts +315 -0
- package/tests/components/autofill_test.ts +167 -0
- package/tests/components/bottom_bar_test.ts +262 -0
- package/tests/components/chart_side_panel_test.ts +123 -0
- package/tests/components/composer_test.ts +400 -0
- package/tests/components/context_menu_test.ts +825 -0
- package/tests/components/grid_manipulation_test.ts +163 -0
- package/tests/components/grid_test.ts +581 -0
- package/tests/components/overlay_test.ts +541 -0
- package/tests/components/selection_input_test.ts +259 -0
- package/tests/components/side_panel_test.ts +148 -0
- package/tests/components/spreadsheet_test.ts +194 -0
- package/tests/components/top_bar_test.ts +427 -0
- package/tests/data_test.ts +175 -0
- package/tests/dom_helper.ts +42 -0
- package/tests/formulas/__snapshots__/compiler_test.ts.snap +302 -0
- package/tests/formulas/compiler_test.ts +266 -0
- package/tests/formulas/composer_tokenizer_test.ts +137 -0
- package/tests/formulas/expressions_test.ts +57 -0
- package/tests/formulas/formulas_test.ts +80 -0
- package/tests/formulas/normalize_test.ts +82 -0
- package/tests/formulas/parser_test.ts +224 -0
- package/tests/formulas/range_tokenizer_test.ts +187 -0
- package/tests/formulas/tokenizer_test.ts +164 -0
- package/tests/functions/arguments_test.ts +141 -0
- package/tests/functions/dates_test.ts +1081 -0
- package/tests/functions/functions_test.ts +53 -0
- package/tests/functions/helper_test.ts +98 -0
- package/tests/functions/module_database_test.ts +311 -0
- package/tests/functions/module_date_test.ts +941 -0
- package/tests/functions/module_info_test.ts +139 -0
- package/tests/functions/module_logical_test.ts +369 -0
- package/tests/functions/module_lookup_test.ts +541 -0
- package/tests/functions/module_math_test.ts +2482 -0
- package/tests/functions/module_operators_test.ts +955 -0
- package/tests/functions/module_statistical_test.ts +2014 -0
- package/tests/functions/module_text_test.ts +667 -0
- package/tests/helpers/coordinates_test.ts +25 -0
- package/tests/helpers/misc_test.ts +95 -0
- package/tests/helpers/numbers_test.ts +196 -0
- package/tests/helpers/zones_test.ts +97 -0
- package/tests/helpers.ts +384 -0
- package/tests/menu_items_registry_test.ts +540 -0
- package/tests/model/plugins/__snapshots__/chart_test.ts.snap +90 -0
- package/tests/model/plugins/chart_test.ts +562 -0
- package/tests/model_test.ts +82 -0
- package/tests/plugins/__snapshots__/conditional_formatting_test.ts.snap +131 -0
- package/tests/plugins/__snapshots__/renderer_test.ts.snap +371 -0
- package/tests/plugins/autofill_test.ts +457 -0
- package/tests/plugins/borders_test.ts +295 -0
- package/tests/plugins/clipboard_test.ts +1180 -0
- package/tests/plugins/conditional_formatting_test.ts +1253 -0
- package/tests/plugins/core_test.ts +614 -0
- package/tests/plugins/edition_test.ts +100 -0
- package/tests/plugins/evaluation_async_test.ts +332 -0
- package/tests/plugins/evaluation_dates_test.ts +79 -0
- package/tests/plugins/evaluation_test.ts +901 -0
- package/tests/plugins/figures_test.ts +327 -0
- package/tests/plugins/formatting_test.ts +391 -0
- package/tests/plugins/grid_manipulation_test.ts +1366 -0
- package/tests/plugins/highlight_test.ts +413 -0
- package/tests/plugins/history_test.ts +142 -0
- package/tests/plugins/import_export_test.ts +276 -0
- package/tests/plugins/merges_test.ts +490 -0
- package/tests/plugins/navigation_test.ts +181 -0
- package/tests/plugins/plugins/evaluation_test.ts +111 -0
- package/tests/plugins/renderer_test.ts +598 -0
- package/tests/plugins/resizing_test.ts +152 -0
- package/tests/plugins/selection_input_test.ts +409 -0
- package/tests/plugins/selection_test.ts +442 -0
- package/tests/plugins/sheets_test.ts +643 -0
- package/tests/plugins/style_test.ts +90 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
import { Component, hooks, tags } from "@odoo/owl";
|
|
2
|
+
import { Menu } from "../../src/components/menu";
|
|
3
|
+
import { toXC } from "../../src/helpers";
|
|
4
|
+
import { Model } from "../../src/model";
|
|
5
|
+
import { createFullMenuItem, FullMenuItem } from "../../src/registries";
|
|
6
|
+
import { cellMenuRegistry } from "../../src/registries/menus/cell_menu_registry";
|
|
7
|
+
import { ConditionalFormat, SpreadsheetEnv } from "../../src/types";
|
|
8
|
+
import { simulateClick, triggerMouseEvent } from "../dom_helper";
|
|
9
|
+
import { getCell, GridParent, makeTestFixture, nextTick, Touch } from "../helpers";
|
|
10
|
+
|
|
11
|
+
const { xml } = tags;
|
|
12
|
+
const { useSubEnv } = hooks;
|
|
13
|
+
|
|
14
|
+
let fixture: HTMLElement;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
fixture = makeTestFixture();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fixture.remove();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
jest.spyOn(HTMLDivElement.prototype, "clientWidth", "get").mockImplementation(() => 1000);
|
|
25
|
+
jest.spyOn(HTMLDivElement.prototype, "clientHeight", "get").mockImplementation(() => 1000);
|
|
26
|
+
|
|
27
|
+
function getActiveXc(model: Model): string {
|
|
28
|
+
return toXC(...model.getters.getPosition());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getPosition(selector: string): { top: number; left: number } {
|
|
32
|
+
const menu = fixture.querySelector(selector);
|
|
33
|
+
const { top, left } = window.getComputedStyle(menu!);
|
|
34
|
+
return {
|
|
35
|
+
top: parseInt(top.replace("px", "")),
|
|
36
|
+
left: parseInt(left.replace("px", "")),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getMenuPosition() {
|
|
41
|
+
return getPosition(".o-menu");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getSubMenuPosition() {
|
|
45
|
+
return getPosition(".o-menu + div .o-menu");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getItemSize() {
|
|
49
|
+
return 32;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getSize(menuItemsCount: number): { width: number; height: number } {
|
|
53
|
+
return {
|
|
54
|
+
width: 200,
|
|
55
|
+
height: getItemSize() * menuItemsCount,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getMenuSize() {
|
|
60
|
+
const menu = fixture.querySelector(".o-menu");
|
|
61
|
+
const menuItems = menu!.querySelectorAll(".o-menu-item");
|
|
62
|
+
return getSize(menuItems.length);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getSubMenuSize() {
|
|
66
|
+
const menu = fixture.querySelector(".o-menu + div .o-menu");
|
|
67
|
+
const menuItems = menu!.querySelectorAll(".o-menu-item");
|
|
68
|
+
return getSize(menuItems.length);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface ContextMenuTestConfig {
|
|
72
|
+
onClose?: () => void;
|
|
73
|
+
menuItems?: FullMenuItem[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function renderContextMenu(
|
|
77
|
+
x: number,
|
|
78
|
+
y: number,
|
|
79
|
+
testConfig: ContextMenuTestConfig = {},
|
|
80
|
+
width = 1000,
|
|
81
|
+
height = 1000
|
|
82
|
+
): Promise<[number, number]> {
|
|
83
|
+
const parent = new ContextMenuParent(x, y, width, height, new Model(), testConfig);
|
|
84
|
+
await parent.mount(fixture);
|
|
85
|
+
await nextTick();
|
|
86
|
+
return [x, y];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const subMenu: FullMenuItem[] = [
|
|
90
|
+
createFullMenuItem("root", {
|
|
91
|
+
name: "root",
|
|
92
|
+
sequence: 1,
|
|
93
|
+
children: () => [
|
|
94
|
+
createFullMenuItem("subMenu1", {
|
|
95
|
+
name: "subMenu1",
|
|
96
|
+
sequence: 1,
|
|
97
|
+
action() {},
|
|
98
|
+
}),
|
|
99
|
+
createFullMenuItem("subMenu2", {
|
|
100
|
+
name: "subMenu2",
|
|
101
|
+
sequence: 1,
|
|
102
|
+
action() {},
|
|
103
|
+
}),
|
|
104
|
+
],
|
|
105
|
+
}),
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
class ContextMenuParent extends Component<any, SpreadsheetEnv> {
|
|
109
|
+
static template = xml/* xml */ `
|
|
110
|
+
<Menu
|
|
111
|
+
t-on-close="onClose"
|
|
112
|
+
position="position"
|
|
113
|
+
menuItems="menus"
|
|
114
|
+
/>
|
|
115
|
+
`;
|
|
116
|
+
static components = { Menu };
|
|
117
|
+
menus: FullMenuItem[];
|
|
118
|
+
position: { x: number; y: number; width: number; height: number };
|
|
119
|
+
onClose: () => void;
|
|
120
|
+
|
|
121
|
+
constructor(
|
|
122
|
+
x: number,
|
|
123
|
+
y: number,
|
|
124
|
+
width: number,
|
|
125
|
+
height: number,
|
|
126
|
+
model: Model,
|
|
127
|
+
{ onClose, menuItems }: ContextMenuTestConfig = {}
|
|
128
|
+
) {
|
|
129
|
+
super();
|
|
130
|
+
useSubEnv({
|
|
131
|
+
getters: model.getters,
|
|
132
|
+
});
|
|
133
|
+
this.onClose = onClose || (() => {});
|
|
134
|
+
this.position = { x, y, width, height };
|
|
135
|
+
this.menus = menuItems || [
|
|
136
|
+
createFullMenuItem("Action", {
|
|
137
|
+
name: "Action",
|
|
138
|
+
sequence: 1,
|
|
139
|
+
action() {},
|
|
140
|
+
}),
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function simulateContextMenu(x, y) {
|
|
146
|
+
triggerMouseEvent("canvas", "mousedown", x, y, { button: 1, bubbles: true });
|
|
147
|
+
triggerMouseEvent("canvas", "mouseup", x, y, { button: 1, bubbles: true });
|
|
148
|
+
triggerMouseEvent("canvas", "contextmenu", x, y, { button: 1, bubbles: true });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
describe("Context Menu", () => {
|
|
152
|
+
test("context menu simple rendering", async () => {
|
|
153
|
+
const model = new Model();
|
|
154
|
+
const parent = new GridParent(model);
|
|
155
|
+
await parent.mount(fixture);
|
|
156
|
+
simulateContextMenu(300, 200);
|
|
157
|
+
await nextTick();
|
|
158
|
+
expect(fixture.querySelector(".o-menu")).toMatchSnapshot();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("right click on a cell opens a context menu", async () => {
|
|
162
|
+
const model = new Model();
|
|
163
|
+
|
|
164
|
+
const parent = new GridParent(model);
|
|
165
|
+
await parent.mount(fixture);
|
|
166
|
+
|
|
167
|
+
expect(getActiveXc(model)).toBe("A1");
|
|
168
|
+
expect(fixture.querySelector(".o-menu")).toBeFalsy();
|
|
169
|
+
simulateContextMenu(300, 200);
|
|
170
|
+
expect(getActiveXc(model)).toBe("C8");
|
|
171
|
+
await nextTick();
|
|
172
|
+
expect(fixture.querySelector(".o-menu")).toBeTruthy();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("right click on a cell, then left click elsewhere closes a context menu", async () => {
|
|
176
|
+
const model = new Model();
|
|
177
|
+
|
|
178
|
+
const parent = new GridParent(model);
|
|
179
|
+
await parent.mount(fixture);
|
|
180
|
+
|
|
181
|
+
simulateContextMenu(300, 200);
|
|
182
|
+
expect(getActiveXc(model)).toBe("C8");
|
|
183
|
+
await nextTick();
|
|
184
|
+
expect(fixture.querySelector(".o-menu")).toBeTruthy();
|
|
185
|
+
|
|
186
|
+
await simulateClick("canvas", 50, 50);
|
|
187
|
+
expect(fixture.querySelector(".o-menu")).toBeFalsy();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("can copy/paste with context menu", async () => {
|
|
191
|
+
const model = new Model();
|
|
192
|
+
model.dispatch("SET_VALUE", { xc: "B1", text: "b1" });
|
|
193
|
+
|
|
194
|
+
const parent = new GridParent(model);
|
|
195
|
+
await parent.mount(fixture);
|
|
196
|
+
|
|
197
|
+
// right click on B1
|
|
198
|
+
simulateContextMenu(230, 30);
|
|
199
|
+
expect(getActiveXc(model)).toBe("B1");
|
|
200
|
+
await nextTick();
|
|
201
|
+
|
|
202
|
+
// click on 'copy' menu item
|
|
203
|
+
await simulateClick(".o-menu div[data-name='copy']");
|
|
204
|
+
|
|
205
|
+
// right click on B2
|
|
206
|
+
simulateContextMenu(230, 50);
|
|
207
|
+
await nextTick();
|
|
208
|
+
expect(getActiveXc(model)).toBe("B2");
|
|
209
|
+
|
|
210
|
+
// click on 'paste' menu item
|
|
211
|
+
await simulateClick(".o-menu div[data-name='paste']");
|
|
212
|
+
expect(getCell(model, "B1")!.content).toBe("b1");
|
|
213
|
+
expect(getCell(model, "B2")!.content).toBe("b1");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("can cut/paste with context menu", async () => {
|
|
217
|
+
const model = new Model();
|
|
218
|
+
model.dispatch("SET_VALUE", { xc: "B1", text: "b1" });
|
|
219
|
+
|
|
220
|
+
const parent = new GridParent(model);
|
|
221
|
+
await parent.mount(fixture);
|
|
222
|
+
|
|
223
|
+
// right click on B1
|
|
224
|
+
simulateContextMenu(230, 30);
|
|
225
|
+
expect(getActiveXc(model)).toBe("B1");
|
|
226
|
+
await nextTick();
|
|
227
|
+
|
|
228
|
+
// click on 'cut' menu item
|
|
229
|
+
await simulateClick(".o-menu div[data-name='cut']");
|
|
230
|
+
|
|
231
|
+
// right click on B2
|
|
232
|
+
simulateContextMenu(230, 50);
|
|
233
|
+
await nextTick();
|
|
234
|
+
expect(getActiveXc(model)).toBe("B2");
|
|
235
|
+
|
|
236
|
+
// click on 'paste' menu item
|
|
237
|
+
await simulateClick(".o-menu div[data-name='paste']");
|
|
238
|
+
|
|
239
|
+
expect(getCell(model, "B1")).toBeNull();
|
|
240
|
+
expect(getCell(model, "B2")!.content).toBe("b1");
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("menu does not close when right click elsewhere", async () => {
|
|
244
|
+
const model = new Model();
|
|
245
|
+
|
|
246
|
+
const parent = new GridParent(model);
|
|
247
|
+
await parent.mount(fixture);
|
|
248
|
+
simulateContextMenu(100, 100);
|
|
249
|
+
await nextTick();
|
|
250
|
+
expect(fixture.querySelector(".o-menu")).toBeTruthy();
|
|
251
|
+
simulateContextMenu(300, 300);
|
|
252
|
+
await nextTick();
|
|
253
|
+
expect(fixture.querySelector(".o-menu")).toBeTruthy();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("close contextmenu when clicking on menubar", async () => {
|
|
257
|
+
const model = new Model();
|
|
258
|
+
const parent = new GridParent(model);
|
|
259
|
+
await parent.mount(fixture);
|
|
260
|
+
simulateContextMenu(100, 100);
|
|
261
|
+
await nextTick();
|
|
262
|
+
expect(fixture.querySelector(".o-menu .o-menu-item[data-name='cut']")).toBeTruthy();
|
|
263
|
+
triggerMouseEvent(".o-topbar-topleft", "click");
|
|
264
|
+
await nextTick();
|
|
265
|
+
expect(fixture.querySelector(".o-menu")).toBeFalsy();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("close contextmenu when clicking on menubar item", async () => {
|
|
269
|
+
const model = new Model();
|
|
270
|
+
const parent = new GridParent(model);
|
|
271
|
+
await parent.mount(fixture);
|
|
272
|
+
simulateContextMenu(100, 100);
|
|
273
|
+
await nextTick();
|
|
274
|
+
expect(fixture.querySelector(".o-menu .o-menu-item[data-name='cut']")).toBeTruthy();
|
|
275
|
+
triggerMouseEvent(".o-topbar-menu[data-id='insert']", "click");
|
|
276
|
+
await nextTick();
|
|
277
|
+
expect(fixture.querySelector(".o-menu .o-menu-item[data-name='cut']")).toBeFalsy();
|
|
278
|
+
});
|
|
279
|
+
test("close contextmenu when clicking on tools bar", async () => {
|
|
280
|
+
const model = new Model();
|
|
281
|
+
const parent = new GridParent(model);
|
|
282
|
+
await parent.mount(fixture);
|
|
283
|
+
simulateContextMenu(100, 100);
|
|
284
|
+
await nextTick();
|
|
285
|
+
expect(fixture.querySelector(".o-menu .o-menu-item[data-name='cut']")).toBeTruthy();
|
|
286
|
+
const fontSizeTool = fixture.querySelector('.o-tool[title="Font Size"]')!;
|
|
287
|
+
triggerMouseEvent(fontSizeTool, "click");
|
|
288
|
+
await nextTick();
|
|
289
|
+
expect(fixture.querySelector(".o-menu .o-menu-item[data-name='cut']")).toBeFalsy();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("menu can be hidden/displayed based on the env", async () => {
|
|
293
|
+
const menuDefinitions = Object.assign({}, cellMenuRegistry.content);
|
|
294
|
+
cellMenuRegistry
|
|
295
|
+
.add("visible_action", {
|
|
296
|
+
name: "visible_action",
|
|
297
|
+
sequence: 1,
|
|
298
|
+
isVisible: (env) => env.getters.getCell(1, 0)!.value === "b1",
|
|
299
|
+
action() {},
|
|
300
|
+
})
|
|
301
|
+
.add("hidden_action", {
|
|
302
|
+
name: "hidden_action",
|
|
303
|
+
sequence: 2,
|
|
304
|
+
isVisible: (env) => env.getters.getCell(1, 0)!.value !== "b1",
|
|
305
|
+
action() {},
|
|
306
|
+
});
|
|
307
|
+
const model = new Model();
|
|
308
|
+
model.dispatch("SET_VALUE", { xc: "B1", text: "b1" });
|
|
309
|
+
const parent = new GridParent(model);
|
|
310
|
+
await parent.mount(fixture);
|
|
311
|
+
simulateContextMenu(230, 30);
|
|
312
|
+
await nextTick();
|
|
313
|
+
expect(fixture.querySelector(".o-menu div[data-name='visible_action']")).toBeTruthy();
|
|
314
|
+
expect(fixture.querySelector(".o-menu div[data-name='hidden_action']")).toBeFalsy();
|
|
315
|
+
cellMenuRegistry.content = menuDefinitions;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("submenu opens and close when (un)overed", async () => {
|
|
319
|
+
const menuItems: FullMenuItem[] = [
|
|
320
|
+
createFullMenuItem("action", {
|
|
321
|
+
name: "action",
|
|
322
|
+
sequence: 1,
|
|
323
|
+
action() {},
|
|
324
|
+
}),
|
|
325
|
+
createFullMenuItem("root", {
|
|
326
|
+
name: "root",
|
|
327
|
+
sequence: 2,
|
|
328
|
+
children: () => [
|
|
329
|
+
createFullMenuItem("subMenu", {
|
|
330
|
+
name: "subMenu",
|
|
331
|
+
sequence: 1,
|
|
332
|
+
action() {},
|
|
333
|
+
}),
|
|
334
|
+
],
|
|
335
|
+
}),
|
|
336
|
+
];
|
|
337
|
+
await renderContextMenu(300, 300, { menuItems });
|
|
338
|
+
triggerMouseEvent(".o-menu div[data-name='root']", "mouseover");
|
|
339
|
+
await nextTick();
|
|
340
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu']")).toBeTruthy();
|
|
341
|
+
triggerMouseEvent(".o-menu div[data-name='action']", "mouseover");
|
|
342
|
+
await nextTick();
|
|
343
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu']")).toBeFalsy();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("submenu does not open when disabled", async () => {
|
|
347
|
+
const menuItems: FullMenuItem[] = [
|
|
348
|
+
createFullMenuItem("root", {
|
|
349
|
+
name: "root",
|
|
350
|
+
sequence: 1,
|
|
351
|
+
isEnabled: () => false,
|
|
352
|
+
children: () => [
|
|
353
|
+
createFullMenuItem("subMenu", {
|
|
354
|
+
name: "subMenu",
|
|
355
|
+
sequence: 1,
|
|
356
|
+
action() {},
|
|
357
|
+
}),
|
|
358
|
+
],
|
|
359
|
+
}),
|
|
360
|
+
];
|
|
361
|
+
await renderContextMenu(300, 300, { menuItems });
|
|
362
|
+
expect(fixture.querySelector(".o-menu div[data-name='root']")!.classList).toContain("disabled");
|
|
363
|
+
await simulateClick(".o-menu div[data-name='root']");
|
|
364
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu']")).toBeFalsy();
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test("submenu does not close when sub item overed", async () => {
|
|
368
|
+
await renderContextMenu(300, 300, { menuItems: subMenu });
|
|
369
|
+
triggerMouseEvent(".o-menu div[data-name='root']", "mouseover");
|
|
370
|
+
await nextTick();
|
|
371
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu1']")).toBeTruthy();
|
|
372
|
+
triggerMouseEvent(".o-menu div[data-name='subMenu1']", "mouseover");
|
|
373
|
+
await nextTick();
|
|
374
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu1']")).toBeTruthy();
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("menu does not close when root menu is clicked", async () => {
|
|
378
|
+
await renderContextMenu(300, 300, { menuItems: subMenu });
|
|
379
|
+
await simulateClick(".o-menu div[data-name='root']");
|
|
380
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu1']")).toBeTruthy();
|
|
381
|
+
expect(fixture.querySelector(".o-menu div[data-name='root']")).toBeTruthy();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test("menu closed when sub menu item is clicked", async () => {
|
|
385
|
+
const mockCallback = jest.fn(() => {});
|
|
386
|
+
await renderContextMenu(300, 300, {
|
|
387
|
+
onClose: mockCallback,
|
|
388
|
+
menuItems: subMenu,
|
|
389
|
+
});
|
|
390
|
+
await simulateClick(".o-menu div[data-name='root']");
|
|
391
|
+
await simulateClick(".o-menu div[data-name='subMenu1']");
|
|
392
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu1']")).toBeFalsy();
|
|
393
|
+
expect(mockCallback).toHaveBeenCalled();
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("it renders subsubmenus", async () => {
|
|
397
|
+
const menuItems: FullMenuItem[] = [
|
|
398
|
+
createFullMenuItem("root1", {
|
|
399
|
+
name: "root1",
|
|
400
|
+
sequence: 1,
|
|
401
|
+
children: () => [
|
|
402
|
+
createFullMenuItem("root2", {
|
|
403
|
+
name: "root2",
|
|
404
|
+
sequence: 1,
|
|
405
|
+
children: () => [
|
|
406
|
+
createFullMenuItem("subMenu", {
|
|
407
|
+
name: "subMenu",
|
|
408
|
+
sequence: 1,
|
|
409
|
+
action() {},
|
|
410
|
+
}),
|
|
411
|
+
],
|
|
412
|
+
}),
|
|
413
|
+
],
|
|
414
|
+
}),
|
|
415
|
+
];
|
|
416
|
+
await renderContextMenu(300, 990, { menuItems });
|
|
417
|
+
await simulateClick("div[data-name='root1']");
|
|
418
|
+
await simulateClick("div[data-name='root2']");
|
|
419
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu']")).toBeTruthy();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test("Submenus are correctly hidden", async () => {
|
|
423
|
+
const menuItems: FullMenuItem[] = [
|
|
424
|
+
createFullMenuItem("root_1", {
|
|
425
|
+
name: "root_1",
|
|
426
|
+
sequence: 1,
|
|
427
|
+
children: () => [
|
|
428
|
+
createFullMenuItem("root_1_1", {
|
|
429
|
+
name: "root_1_1",
|
|
430
|
+
sequence: 1,
|
|
431
|
+
children: () => [
|
|
432
|
+
createFullMenuItem("subMenu_1", {
|
|
433
|
+
name: "subMenu_1",
|
|
434
|
+
sequence: 1,
|
|
435
|
+
action() {},
|
|
436
|
+
}),
|
|
437
|
+
],
|
|
438
|
+
}),
|
|
439
|
+
],
|
|
440
|
+
}),
|
|
441
|
+
createFullMenuItem("root_2", {
|
|
442
|
+
name: "root_2",
|
|
443
|
+
sequence: 2,
|
|
444
|
+
children: () => [
|
|
445
|
+
createFullMenuItem("root_2_1", {
|
|
446
|
+
name: "root_2_1",
|
|
447
|
+
sequence: 1,
|
|
448
|
+
children: () => [
|
|
449
|
+
createFullMenuItem("subMenu_2", {
|
|
450
|
+
name: "subMenu_2",
|
|
451
|
+
sequence: 1,
|
|
452
|
+
action() {},
|
|
453
|
+
}),
|
|
454
|
+
],
|
|
455
|
+
}),
|
|
456
|
+
],
|
|
457
|
+
}),
|
|
458
|
+
];
|
|
459
|
+
await renderContextMenu(300, 300, { menuItems });
|
|
460
|
+
|
|
461
|
+
triggerMouseEvent(".o-menu div[data-name='root_1']", "mouseover");
|
|
462
|
+
await nextTick();
|
|
463
|
+
triggerMouseEvent(".o-menu div[data-name='root_1_1']", "mouseover");
|
|
464
|
+
await nextTick();
|
|
465
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu_1']")).toBeTruthy();
|
|
466
|
+
triggerMouseEvent(".o-menu div[data-name='root_2']", "mouseover");
|
|
467
|
+
await nextTick();
|
|
468
|
+
expect(fixture.querySelector(".o-menu div[data-name='subMenu_1']")).toBeFalsy();
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test("scroll through the menu with the wheel / scrollbar prevents the grid from scrolling", async () => {
|
|
472
|
+
const model = new Model();
|
|
473
|
+
const parent = new GridParent(model);
|
|
474
|
+
await parent.mount(fixture);
|
|
475
|
+
|
|
476
|
+
// grid initially at (0, 0) scroll position
|
|
477
|
+
expect(parent.grid.comp.vScrollbar.scroll).toBe(0);
|
|
478
|
+
expect(parent.grid.comp.hScrollbar.scroll).toBe(0);
|
|
479
|
+
|
|
480
|
+
simulateContextMenu(300, 200);
|
|
481
|
+
await nextTick();
|
|
482
|
+
|
|
483
|
+
const menu = fixture.querySelector(".o-menu")!;
|
|
484
|
+
// scroll
|
|
485
|
+
menu.dispatchEvent(
|
|
486
|
+
new WheelEvent("wheel", { deltaY: 300, deltaX: 300, deltaMode: 0, bubbles: true })
|
|
487
|
+
);
|
|
488
|
+
menu.dispatchEvent(new Event("scroll", { bubbles: true }));
|
|
489
|
+
await nextTick();
|
|
490
|
+
|
|
491
|
+
// grid always at (0, 0) scroll position
|
|
492
|
+
expect(parent.grid.comp.vScrollbar.scroll).toBe(0);
|
|
493
|
+
expect(parent.grid.comp.hScrollbar.scroll).toBe(0);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
test("scroll through the menu with the touch device prevents the grid from scrolling", async () => {
|
|
497
|
+
const model = new Model();
|
|
498
|
+
const parent = new GridParent(model);
|
|
499
|
+
await parent.mount(fixture);
|
|
500
|
+
|
|
501
|
+
// grid initially at (0, 0) scroll position
|
|
502
|
+
expect(parent.grid.comp.vScrollbar.scroll).toBe(0);
|
|
503
|
+
expect(parent.grid.comp.hScrollbar.scroll).toBe(0);
|
|
504
|
+
|
|
505
|
+
simulateContextMenu(300, 200);
|
|
506
|
+
await nextTick();
|
|
507
|
+
|
|
508
|
+
const menu = fixture.querySelector(".o-menu")!;
|
|
509
|
+
|
|
510
|
+
// start move at (310, 210) touch position
|
|
511
|
+
menu.dispatchEvent(
|
|
512
|
+
new TouchEvent("touchstart", {
|
|
513
|
+
bubbles: true,
|
|
514
|
+
cancelable: true,
|
|
515
|
+
touches: [
|
|
516
|
+
new Touch({
|
|
517
|
+
clientX: 310,
|
|
518
|
+
clientY: 210,
|
|
519
|
+
identifier: 1,
|
|
520
|
+
target: menu,
|
|
521
|
+
}),
|
|
522
|
+
],
|
|
523
|
+
})
|
|
524
|
+
);
|
|
525
|
+
// move down;
|
|
526
|
+
menu.dispatchEvent(
|
|
527
|
+
new TouchEvent("touchmove", {
|
|
528
|
+
bubbles: true,
|
|
529
|
+
cancelable: true,
|
|
530
|
+
touches: [
|
|
531
|
+
new Touch({
|
|
532
|
+
clientX: 310,
|
|
533
|
+
clientY: 180,
|
|
534
|
+
identifier: 2,
|
|
535
|
+
target: menu,
|
|
536
|
+
}),
|
|
537
|
+
],
|
|
538
|
+
})
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
await nextTick();
|
|
542
|
+
// grid always at (0, 0) scroll position
|
|
543
|
+
expect(parent.grid.comp.vScrollbar.scroll).toBe(0);
|
|
544
|
+
expect(parent.grid.comp.hScrollbar.scroll).toBe(0);
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
describe("Context Menu position on large screen 1000px/1000px", () => {
|
|
549
|
+
test("it renders menu on the bottom right if enough space", async () => {
|
|
550
|
+
const [clickX, clickY] = await renderContextMenu(300, 300);
|
|
551
|
+
const { left, top } = getMenuPosition();
|
|
552
|
+
expect(left).toBe(clickX);
|
|
553
|
+
expect(top).toBe(clickY);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
test("it renders menu on the top right if not enough space", async () => {
|
|
557
|
+
const [clickX, clickY] = await renderContextMenu(300, 990);
|
|
558
|
+
const { left, top } = getMenuPosition();
|
|
559
|
+
const { height } = getMenuSize();
|
|
560
|
+
expect(left).toBe(clickX);
|
|
561
|
+
expect(top).toBe(clickY - height);
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
test("it renders menu on the bottom left if not enough space", async () => {
|
|
565
|
+
const [clickX, clickY] = await renderContextMenu(990, 300);
|
|
566
|
+
const { left, top } = getMenuPosition();
|
|
567
|
+
const { width } = getMenuSize();
|
|
568
|
+
expect(left).toBe(clickX - width);
|
|
569
|
+
expect(top).toBe(clickY);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
test("it renders menu on the top left if not enough space", async () => {
|
|
573
|
+
const [clickX, clickY] = await renderContextMenu(990, 990);
|
|
574
|
+
const { left, top } = getMenuPosition();
|
|
575
|
+
const { width, height } = getMenuSize();
|
|
576
|
+
expect(left).toBe(clickX - width);
|
|
577
|
+
expect(top).toBe(clickY - height);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
test("it renders submenu on the bottom right if enough space", async () => {
|
|
581
|
+
const [clickX, clickY] = await renderContextMenu(300, 300, { menuItems: subMenu });
|
|
582
|
+
await simulateClick("div[data-name='root']");
|
|
583
|
+
const { left, top } = getSubMenuPosition();
|
|
584
|
+
const { width } = getMenuSize();
|
|
585
|
+
expect(left).toBe(clickX + width);
|
|
586
|
+
expect(top).toBe(clickY);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
test("it renders submenu on the bottom left if not enough space", async () => {
|
|
590
|
+
const [clickX, clickY] = await renderContextMenu(780, 300, { menuItems: subMenu });
|
|
591
|
+
await simulateClick("div[data-name='root']");
|
|
592
|
+
const { left, top } = getSubMenuPosition();
|
|
593
|
+
const { width } = getMenuSize();
|
|
594
|
+
const { left: rootLeft } = getMenuPosition();
|
|
595
|
+
expect(rootLeft).toBe(clickX);
|
|
596
|
+
expect(left).toBe(clickX - width);
|
|
597
|
+
expect(top).toBe(clickY);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
test("it renders all menus on the bottom left if not enough space", async () => {
|
|
601
|
+
const [clickX, clickY] = await renderContextMenu(990, 300, { menuItems: subMenu });
|
|
602
|
+
await simulateClick("div[data-name='root']");
|
|
603
|
+
const { left, top } = getSubMenuPosition();
|
|
604
|
+
const { width } = getMenuSize();
|
|
605
|
+
const { left: rootLeft } = getMenuPosition();
|
|
606
|
+
expect(rootLeft).toBe(clickX - width);
|
|
607
|
+
expect(left).toBe(clickX - 2 * width);
|
|
608
|
+
expect(top).toBe(clickY);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
test("it renders submenu on the top right if not enough space", async () => {
|
|
612
|
+
const [clickX, clickY] = await renderContextMenu(300, 960, { menuItems: subMenu });
|
|
613
|
+
await simulateClick("div[data-name='root']");
|
|
614
|
+
const { left, top } = getSubMenuPosition();
|
|
615
|
+
const { height } = getSubMenuSize();
|
|
616
|
+
const { width } = getMenuSize();
|
|
617
|
+
expect(top).toBe(clickY - height + getItemSize());
|
|
618
|
+
expect(left).toBe(clickX + width);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
test("it renders all menus on the top right if not enough space", async () => {
|
|
622
|
+
const [clickX, clickY] = await renderContextMenu(300, 990, { menuItems: subMenu });
|
|
623
|
+
await simulateClick("div[data-name='root']");
|
|
624
|
+
const { left, top } = getSubMenuPosition();
|
|
625
|
+
const { top: rootTop } = getMenuPosition();
|
|
626
|
+
const { height } = getSubMenuSize();
|
|
627
|
+
const { height: rootHeight } = getMenuSize();
|
|
628
|
+
const { width } = getSubMenuSize();
|
|
629
|
+
expect(rootTop).toBe(clickY - rootHeight);
|
|
630
|
+
expect(top).toBe(clickY - height);
|
|
631
|
+
expect(left).toBe(clickX + width);
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
describe("Context Menu position on small screen 1000px/300px", () => {
|
|
636
|
+
// prettier-ignore
|
|
637
|
+
const longMenuItems: FullMenuItem[] = [ // menu height 6*32 => 192
|
|
638
|
+
createFullMenuItem("root_1", {name: "root_1", sequence: 1, action() {},}),
|
|
639
|
+
createFullMenuItem("root_2", {name: "root_2", sequence: 2, action() {},}),
|
|
640
|
+
createFullMenuItem("root_3", {name: "root_3", sequence: 3, action() {},}),
|
|
641
|
+
createFullMenuItem("root_4", {name: "root_4", sequence: 4,
|
|
642
|
+
children: () => [ // sub menu height 6*32 => 192
|
|
643
|
+
createFullMenuItem("subMenu_1", {name: "subMenu_1", sequence: 1, action() {},}),
|
|
644
|
+
createFullMenuItem("subMenu_2", {name: "subMenu_2", sequence: 2, action() {},}),
|
|
645
|
+
createFullMenuItem("subMenu_3", {name: "subMenu_3", sequence: 3, action() {},}),
|
|
646
|
+
createFullMenuItem("subMenu_4", {name: "subMenu_4", sequence: 4, action() {},}),
|
|
647
|
+
createFullMenuItem("subMenu_5", {name: "subMenu_5", sequence: 5, action() {},}),
|
|
648
|
+
createFullMenuItem("subMenu_6", {name: "subMenu_6", sequence: 6, action() {},}),
|
|
649
|
+
],
|
|
650
|
+
}),
|
|
651
|
+
createFullMenuItem("root_5", {name: "root_5", sequence: 5, action() {},}),
|
|
652
|
+
createFullMenuItem("root_6", {name: "root_6", sequence: 6, action() {},}),
|
|
653
|
+
];
|
|
654
|
+
|
|
655
|
+
test("it renders menu at the top of the screen on the right, if not enough space above and below", async () => {
|
|
656
|
+
const [clickX] = await renderContextMenu(300, 150, { menuItems: longMenuItems }, 1000, 300);
|
|
657
|
+
const { left, top } = getMenuPosition();
|
|
658
|
+
expect(left).toBe(clickX);
|
|
659
|
+
expect(top).toBe(32);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
test("it renders menu at the top of the screen on the left, if not enough space above, below and on the right", async () => {
|
|
663
|
+
const [clickX] = await renderContextMenu(990, 150, { menuItems: longMenuItems }, 1000, 300);
|
|
664
|
+
const { left, top } = getMenuPosition();
|
|
665
|
+
const { width } = getMenuSize();
|
|
666
|
+
expect(left).toBe(clickX - width);
|
|
667
|
+
expect(top).toBe(32);
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
test("it renders submenu at the top of the screen on the right, if not enough space above and below", async () => {
|
|
671
|
+
const [clickX] = await renderContextMenu(300, 150, { menuItems: longMenuItems }, 1000, 300);
|
|
672
|
+
await simulateClick("div[data-name='root_4']");
|
|
673
|
+
const { left, top } = getSubMenuPosition();
|
|
674
|
+
const { width } = getMenuSize();
|
|
675
|
+
expect(left).toBe(clickX + width);
|
|
676
|
+
expect(top).toBe(32);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
test("it renders submenu at the top of the screen on the left, if not enough space above, below and on the right", async () => {
|
|
680
|
+
const [clickX] = await renderContextMenu(780, 150, { menuItems: longMenuItems }, 1000, 300);
|
|
681
|
+
await simulateClick("div[data-name='root_4']");
|
|
682
|
+
const { left, top } = getSubMenuPosition();
|
|
683
|
+
const { width } = getMenuSize();
|
|
684
|
+
expect(left).toBe(clickX - width);
|
|
685
|
+
expect(top).toBe(32);
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
describe("Context Menu - CF", () => {
|
|
690
|
+
test("open sidepanel with no CF in selected zone", async () => {
|
|
691
|
+
const model = new Model();
|
|
692
|
+
const parent = new GridParent(model);
|
|
693
|
+
await parent.mount(fixture);
|
|
694
|
+
simulateContextMenu(240, 110);
|
|
695
|
+
await nextTick();
|
|
696
|
+
await simulateClick(".o-menu div[data-name='conditional_formatting']");
|
|
697
|
+
expect(
|
|
698
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-preview-list")
|
|
699
|
+
).toBeTruthy();
|
|
700
|
+
expect(
|
|
701
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-ruleEditor")
|
|
702
|
+
).toBeFalsy();
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
test("open sidepanel with one CF in selected zone", async () => {
|
|
706
|
+
const model = new Model();
|
|
707
|
+
const parent = new GridParent(model);
|
|
708
|
+
await parent.mount(fixture);
|
|
709
|
+
|
|
710
|
+
const cfRule: ConditionalFormat = {
|
|
711
|
+
ranges: ["A1:C7"],
|
|
712
|
+
id: "1",
|
|
713
|
+
rule: {
|
|
714
|
+
values: ["2"],
|
|
715
|
+
operator: "Equal",
|
|
716
|
+
type: "CellIsRule",
|
|
717
|
+
style: { fillColor: "#FF0000" },
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
model.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
721
|
+
cf: cfRule,
|
|
722
|
+
sheet: model.getters.getActiveSheet(),
|
|
723
|
+
});
|
|
724
|
+
const zone = { left: 0, top: 0, bottom: 10, right: 10 };
|
|
725
|
+
model.dispatch("SET_SELECTION", { zones: [zone], anchor: [0, 0] });
|
|
726
|
+
simulateContextMenu(240, 110); //click on C5
|
|
727
|
+
await nextTick();
|
|
728
|
+
await simulateClick(".o-menu div[data-name='conditional_formatting']");
|
|
729
|
+
expect(
|
|
730
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-preview-list")
|
|
731
|
+
).toBeFalsy();
|
|
732
|
+
expect(
|
|
733
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-ruleEditor")
|
|
734
|
+
).toBeTruthy();
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
test("open sidepanel with more then one CF in selected zone", async () => {
|
|
738
|
+
const model = new Model();
|
|
739
|
+
const parent = new GridParent(model);
|
|
740
|
+
await parent.mount(fixture);
|
|
741
|
+
|
|
742
|
+
const cfRule1: ConditionalFormat = {
|
|
743
|
+
ranges: ["A1:C7"],
|
|
744
|
+
id: "1",
|
|
745
|
+
rule: {
|
|
746
|
+
values: ["2"],
|
|
747
|
+
operator: "Equal",
|
|
748
|
+
type: "CellIsRule",
|
|
749
|
+
style: { fillColor: "#FF0000" },
|
|
750
|
+
},
|
|
751
|
+
};
|
|
752
|
+
const cfRule2: ConditionalFormat = {
|
|
753
|
+
ranges: ["A1:C7"],
|
|
754
|
+
id: "2",
|
|
755
|
+
rule: {
|
|
756
|
+
values: ["3"],
|
|
757
|
+
operator: "Equal",
|
|
758
|
+
type: "CellIsRule",
|
|
759
|
+
style: { fillColor: "#FE0001" },
|
|
760
|
+
},
|
|
761
|
+
};
|
|
762
|
+
model.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
763
|
+
cf: cfRule1,
|
|
764
|
+
sheet: model.getters.getActiveSheet(),
|
|
765
|
+
});
|
|
766
|
+
model.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
767
|
+
cf: cfRule2,
|
|
768
|
+
sheet: model.getters.getActiveSheet(),
|
|
769
|
+
});
|
|
770
|
+
const zone = { left: 0, top: 0, bottom: 10, right: 10 };
|
|
771
|
+
model.dispatch("SET_SELECTION", { zones: [zone], anchor: [0, 0] });
|
|
772
|
+
simulateContextMenu(240, 110); //click on C5
|
|
773
|
+
await nextTick();
|
|
774
|
+
await simulateClick(".o-menu div[data-name='conditional_formatting']");
|
|
775
|
+
expect(
|
|
776
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-preview-list")
|
|
777
|
+
).toBeTruthy();
|
|
778
|
+
expect(
|
|
779
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-ruleEditor")
|
|
780
|
+
).toBeFalsy();
|
|
781
|
+
});
|
|
782
|
+
test("will update sidepanel if we reopen it from other cell", async () => {
|
|
783
|
+
const model = new Model();
|
|
784
|
+
const parent = new GridParent(model);
|
|
785
|
+
await parent.mount(fixture);
|
|
786
|
+
|
|
787
|
+
const cfRule1: ConditionalFormat = {
|
|
788
|
+
ranges: ["A1:A10"],
|
|
789
|
+
id: "1",
|
|
790
|
+
rule: {
|
|
791
|
+
values: ["2"],
|
|
792
|
+
operator: "Equal",
|
|
793
|
+
type: "CellIsRule",
|
|
794
|
+
style: { fillColor: "#FF1200" },
|
|
795
|
+
},
|
|
796
|
+
};
|
|
797
|
+
model.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
798
|
+
cf: cfRule1,
|
|
799
|
+
sheet: model.getters.getActiveSheet(),
|
|
800
|
+
});
|
|
801
|
+
let zone = { left: 0, top: 0, bottom: 10, right: 0 };
|
|
802
|
+
model.dispatch("SET_SELECTION", { zones: [zone], anchor: [0, 0] });
|
|
803
|
+
simulateContextMenu(80, 90);
|
|
804
|
+
await nextTick();
|
|
805
|
+
await simulateClick(".o-menu div[data-name='conditional_formatting']");
|
|
806
|
+
expect(
|
|
807
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-preview-list")
|
|
808
|
+
).toBeFalsy();
|
|
809
|
+
expect(
|
|
810
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-ruleEditor")
|
|
811
|
+
).toBeTruthy();
|
|
812
|
+
|
|
813
|
+
zone = { left: 5, top: 5, bottom: 5, right: 5 };
|
|
814
|
+
model.dispatch("SET_SELECTION", { zones: [zone], anchor: [5, 5] });
|
|
815
|
+
simulateContextMenu(530, 125);
|
|
816
|
+
await nextTick();
|
|
817
|
+
await simulateClick(".o-menu div[data-name='conditional_formatting']");
|
|
818
|
+
expect(
|
|
819
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-preview-list")
|
|
820
|
+
).toBeTruthy();
|
|
821
|
+
expect(
|
|
822
|
+
fixture.querySelector(".o-sidePanel .o-sidePanelBody .o-cf .o-cf-ruleEditor")
|
|
823
|
+
).toBeFalsy();
|
|
824
|
+
});
|
|
825
|
+
});
|