@yuneta/gobj-ui 7.26.34 → 7.26.35

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.
Files changed (43) hide show
  1. package/README.md +223 -53
  2. package/dist/gobj-ui.cjs.js +1035 -141
  3. package/dist/gobj-ui.es.js +1035 -141
  4. package/package.json +2 -2
  5. package/scripts/validate-locales.mjs +672 -96
  6. package/src/c_yui_form.js +186 -9
  7. package/src/c_yui_gclass.js +2 -0
  8. package/src/c_yui_gobj_tree_js.js +2 -0
  9. package/src/c_yui_json.js +2 -0
  10. package/src/c_yui_json_graph.js +2 -0
  11. package/src/c_yui_nav.js +23 -5
  12. package/src/c_yui_shell.css +16 -0
  13. package/src/c_yui_shell.js +282 -26
  14. package/src/c_yui_treedb_graph.js +26 -8
  15. package/src/c_yui_treedb_topic_with_form.js +40 -18
  16. package/src/c_yui_treedb_topics.js +34 -11
  17. package/src/c_yui_window.js +132 -32
  18. package/src/c_yui_window_manager.js +156 -44
  19. package/src/form_json_editor_names.js +448 -0
  20. package/src/form_json_editor_names.wiring.test.js +595 -0
  21. package/src/form_label_i18n.wiring.test.js +169 -0
  22. package/src/i18n_key_functions.js +14 -6
  23. package/src/i18n_key_functions.test.js +124 -2
  24. package/src/lib_maplibre.js +26 -2
  25. package/src/lib_maplibre_relocalize.test.js +78 -0
  26. package/src/named_controls.wiring.test.js +468 -0
  27. package/src/nav_cards_helpers.js +9 -1
  28. package/src/nav_item_badge.wiring.test.js +182 -1
  29. package/src/shell_route_map.js +25 -8
  30. package/src/table_editor_name.wiring.test.js +201 -0
  31. package/src/treedb_column_title.wiring.test.js +151 -0
  32. package/src/treedb_json_drill.wiring.test.js +41 -1
  33. package/src/window_layer.wiring.test.js +308 -43
  34. package/src/window_show.wiring.test.js +6 -2
  35. package/src/window_stacking.wiring.test.js +334 -0
  36. package/src/yui_badge.js +58 -13
  37. package/src/yui_badge.test.js +40 -3
  38. package/src/yui_dev.js +2 -1
  39. package/src/yui_file_field.js +2 -0
  40. package/src/yui_frontend_view.js +2 -1
  41. package/src/yui_gclass_view.js +9 -6
  42. package/src/yui_tabulator_i18n.js +42 -0
  43. package/src/yui_tabulator_i18n.test.js +48 -1
@@ -53,11 +53,13 @@ const {
53
53
  const logged = [];
54
54
  const commands = []; /* what the fake transport was asked */
55
55
  let refuse_in_session = false;
56
+ let refuse_silently = false;
56
57
 
57
58
  /*
58
59
  * The transport. Out of session it refuses, as gui_agent's routing
59
60
  * adapter (C_AGENT_TREEDB_LINK) does; in session it refuses only when
60
- * the test says so (a transport that cannot route the command).
61
+ * the test says so (a transport that cannot route the command) -- with
62
+ * its words, or with a bare -1 that says nothing.
61
63
  */
62
64
  function remote_command_parser(gobj, command, kw, src)
63
65
  {
@@ -65,6 +67,9 @@ function remote_command_parser(gobj, command, kw, src)
65
67
  if(gobj_current_state(gobj) !== "ST_SESSION") {
66
68
  return `cannot route '${command}' -- not in session`;
67
69
  }
70
+ if(refuse_silently) {
71
+ return -1;
72
+ }
68
73
  if(refuse_in_session) {
69
74
  return `cannot route '${command}'`;
70
75
  }
@@ -148,6 +153,7 @@ beforeEach(() => {
148
153
  logged.length = 0;
149
154
  commands.length = 0;
150
155
  refuse_in_session = false;
156
+ refuse_silently = false;
151
157
  });
152
158
 
153
159
  /*
@@ -305,6 +311,40 @@ for(const gclass of ["C_YUI_TREEDB_TOPICS", "C_YUI_TREEDB_GRAPH"]) {
305
311
  expect(logged.filter((l) => l.level === "error")).toEqual([]);
306
312
  });
307
313
 
314
+ test("a failure with no word from the backend draws the fallback KEY, translated", async () => {
315
+ /* "print-tranger failed" travelled as `error`, which the
316
+ * viewer draws as it came: the raw key, in English, in every
317
+ * language (REV-CLOUD V23.5.4). As `i18n` it is drawn with
318
+ * t() and carries data-i18n, so it changes language too. */
319
+ const i18next = (await import("i18next")).default;
320
+ i18next.addResourceBundle("en", "translation",
321
+ {"print-tranger failed": "the tranger could not be read"}, true, true);
322
+ const {remote, host, viewer} = build(`${gclass}_i`, gclass);
323
+ click_stub(viewer);
324
+ const request = commands.find((c) => c.command === "print-tranger");
325
+ gobj_send_event(host, "EV_MT_COMMAND_ANSWER", {
326
+ result: -1, comment: "", data: null,
327
+ __md_iev__: {
328
+ command_stack: [{command: "print-tranger", kw: request.kw.__md_command__}]
329
+ }
330
+ }, remote);
331
+ expect(viewer.priv.errors.get("topics")).toEqual(
332
+ {error: "print-tranger failed", is_key: true});
333
+ const $err = viewer.priv.$tree.querySelector(".JSON_STUB_ERR");
334
+ expect($err.getAttribute("data-i18n")).toBe("print-tranger failed");
335
+ expect($err.textContent).toBe("the tranger could not be read");
336
+ });
337
+
338
+ test("a refusal of the transport that says nothing draws the fallback KEY too", () => {
339
+ const {viewer} = build(`${gclass}_j`, gclass);
340
+ refuse_silently = true;
341
+ click_stub(viewer);
342
+ expect(drills()).toEqual(["topics"]);
343
+ expect(viewer.priv.pending.has("topics")).toBe(false);
344
+ expect(viewer.priv.errors.get("topics")).toEqual(
345
+ {error: "print-tranger failed", is_key: true});
346
+ });
347
+
308
348
  test("a drill that failed is not answered again by a drop", () => {
309
349
  const {remote, host, viewer} = build(`${gclass}_f`, gclass);
310
350
  click_stub(viewer);
@@ -2,15 +2,26 @@
2
2
  * window_layer.wiring.test.js
3
3
  *
4
4
  * Where a C_YUI_WINDOW lives when its host names no `$parent`:
5
- * the OVERLAY layer of the shell, below the layers of the
6
- * dropdowns, the dialogs and the toasts -- not document.body,
7
- * where it covered the whole shell (a stacking context of its
8
- * own) and the dialog of its own close opened under it.
5
+ * the WINDOW layer of the shell, a stacking context inside the
6
+ * overlay layer, below the drawer, the dropdowns, the dialogs and
7
+ * the toasts -- not document.body, where it covered the whole
8
+ * shell (a stacking context of its own) and the dialog of its own
9
+ * close opened under it.
10
+ *
11
+ * And that the place FOLLOWS the shell (REV-CLOUD V23.3): a window
12
+ * parented to the app outlives a logout that destroys the shell.
13
+ * From 7.26.31 to 7.26.34 it stayed in the destroyed shell's layer,
14
+ * alive and detached, and the next shell never placed it again: the
15
+ * developer window of gui_agent, gui_treedb and wattyzer vanished
16
+ * after a logout and a login. The dock, the same.
9
17
  *
10
18
  * Copyright (c) 2026, ArtGins.
11
19
  * All Rights Reserved.
12
20
  ***********************************************************************/
13
- import {describe, test, expect, beforeAll} from "vitest";
21
+ import {describe, test, expect, beforeAll, beforeEach} from "vitest";
22
+ import {readFileSync} from "node:fs";
23
+ import {dirname, join} from "node:path";
24
+ import {fileURLToPath} from "node:url";
14
25
  import {install_dom_double} from "../test/dom_double.js";
15
26
 
16
27
  install_dom_double();
@@ -22,18 +33,27 @@ if(typeof globalThis.matchMedia === "undefined") {
22
33
  const {
23
34
  SDATA, SDATA_END, data_type_t, gclass_create, gobj_write_attr,
24
35
  gobj_start_up, gobj_create_yuno, gobj_create, gobj_create_service,
25
- gobj_start, gobj_stop, gobj_destroy, gobj_read_attr,
36
+ gobj_start, gobj_stop, gobj_destroy, gobj_read_attr, gobj_is_destroying,
37
+ set_log_callback,
26
38
  } = await import("@yuneta/gobj-js");
27
- const {register_c_yui_shell} = await import("./c_yui_shell.js");
39
+ const {register_c_yui_shell, yui_shell_window_layer} = await import("./c_yui_shell.js");
28
40
  const {register_c_yui_nav} = await import("./c_yui_nav.js");
29
41
  const {register_c_yui_window} = await import("./c_yui_window.js");
42
+ const {register_c_yui_window_manager} = await import("./c_yui_window_manager.js");
43
+
44
+ const SRC = dirname(fileURLToPath(import.meta.url));
30
45
 
46
+ const logged = [];
31
47
  let yuno = null;
48
+ let seq = 0;
32
49
 
33
50
  beforeAll(async () => {
34
51
  const i18next = (await import("i18next")).default;
35
52
  await i18next.init({lng: "en", resources: {}});
36
53
  gobj_start_up(null, null, null, null, null, null, null);
54
+ set_log_callback((level, msg) => {
55
+ logged.push({level: String(level), msg: String(msg)});
56
+ });
37
57
  gclass_create("C_TEST_LAYER_HOST", [], [["ST_IDLE", []]], {}, 0, [SDATA_END()], {}, 0, 0, 0, 0);
38
58
  gclass_create(
39
59
  "C_TEST_LAYER_VIEW", [], [["ST_IDLE", []]], {
@@ -48,59 +68,304 @@ beforeAll(async () => {
48
68
  register_c_yui_shell();
49
69
  register_c_yui_nav();
50
70
  register_c_yui_window();
71
+ register_c_yui_window_manager();
51
72
  yuno = gobj_create_yuno("layer_yuno", "C_TEST_LAYER_HOST", {});
52
73
  gobj_start(yuno);
53
74
  });
54
75
 
76
+ beforeEach(() => {
77
+ logged.length = 0;
78
+ });
79
+
80
+ function errors()
81
+ {
82
+ return logged.filter((l) => l.level === "error").map((l) => l.msg);
83
+ }
84
+
85
+ /* A shell with one routed view, as an app builds it at login. */
86
+ function build_shell(opts)
87
+ {
88
+ const shell = gobj_create(`layer_shell_${++seq}`, "C_YUI_SHELL", Object.assign({
89
+ config: {
90
+ shell: {
91
+ zones: {center: {host: "stage.main"}},
92
+ stages: {main: {zone: "center", default_route: "/a"}}
93
+ },
94
+ menu: {primary: {items: [
95
+ {id: "a", name: "a", route: "/a",
96
+ target: {stage: "main", gclass: "C_TEST_LAYER_VIEW", kw: {}}}
97
+ ]}}
98
+ },
99
+ use_hash: false
100
+ }, opts || {}), yuno);
101
+ gobj_start(shell);
102
+ return shell;
103
+ }
104
+
105
+ /* What an app does at logout (gui_agent's destroy_shell()). */
106
+ function logout(shell)
107
+ {
108
+ gobj_stop(shell);
109
+ gobj_destroy(shell);
110
+ }
111
+
112
+ function open_window(name, kw, parent)
113
+ {
114
+ const w = gobj_create_service(name, "C_YUI_WINDOW",
115
+ Object.assign({header: name, body: ["div", {}, name]}, kw || {}), parent || yuno);
116
+ gobj_start(w);
117
+ return w;
118
+ }
119
+
120
+ function close(w)
121
+ {
122
+ gobj_stop(w);
123
+ gobj_destroy(w);
124
+ }
125
+
126
+ /* Identity, asked as a boolean: a failing toBe() over a DOM double
127
+ * makes vitest pretty-print it, and the double has no attribute list. */
128
+ function lives_in(w, $place)
129
+ {
130
+ return gobj_read_attr(w, "$container").parentNode === $place;
131
+ }
132
+
133
+ function on_page(w)
134
+ {
135
+ return document.documentElement.contains(gobj_read_attr(w, "$container"));
136
+ }
137
+
55
138
  describe("the default parent of a window", () => {
56
139
  test("with no shell: document.body", () => {
57
- const w = gobj_create_service("layer_w0", "C_YUI_WINDOW", {header: "w", body: ["div", {}, "w"]}, yuno);
58
- gobj_start(w);
59
- expect(gobj_read_attr(w, "$container").parentNode).toBe(document.body);
60
- gobj_stop(w);
61
- gobj_destroy(w);
140
+ const w = open_window("layer_w0");
141
+ expect(lives_in(w, document.body)).toBe(true);
142
+ close(w);
62
143
  });
63
144
 
64
- test("with a shell: its overlay layer, also for a window that is not its child", () => {
65
- const shell = gobj_create("layer_shell", "C_YUI_SHELL", {
66
- config: {
67
- shell: {
68
- zones: {center: {host: "stage.main"}},
69
- stages: {main: {zone: "center", default_route: "/a"}}
70
- },
71
- menu: {primary: {items: [
72
- {id: "a", name: "a", route: "/a",
73
- target: {stage: "main", gclass: "C_TEST_LAYER_VIEW", kw: {}}}
74
- ]}}
75
- },
76
- use_hash: false
77
- }, yuno);
78
- gobj_start(shell);
145
+ test("with a shell: its window layer, inside the overlay layer, also for a window that is not its child", () => {
146
+ const shell = build_shell();
79
147
 
80
148
  /* A child of the shell, and one parented to the app (the dev
81
- * window, a device window of a view): both in the overlay. */
82
- const inside = gobj_create_service("layer_w1", "C_YUI_WINDOW", {header: "a", body: ["div", {}, "a"]}, shell);
83
- gobj_start(inside);
84
- const outside = gobj_create_service("layer_w2", "C_YUI_WINDOW", {header: "b", body: ["div", {}, "b"]}, yuno);
85
- gobj_start(outside);
149
+ * window, a device window of a view): both in the window layer. */
150
+ const inside = open_window("layer_w1", {}, shell);
151
+ const outside = open_window("layer_w2");
86
152
 
87
153
  const overlay = shell.priv.layers.overlay;
88
- expect(overlay).toBeTruthy();
89
- expect(gobj_read_attr(inside, "$container").parentNode).toBe(overlay);
90
- expect(gobj_read_attr(outside, "$container").parentNode).toBe(overlay);
154
+ const windows = yui_shell_window_layer(shell);
155
+ expect(!!overlay).toBe(true);
156
+ expect(windows.parentNode === overlay).toBe(true);
157
+ expect(windows.classList.contains("SHELL_WINDOW_LAYER")).toBe(true);
158
+ expect(lives_in(inside, windows)).toBe(true);
159
+ expect(lives_in(outside, windows)).toBe(true);
91
160
 
92
161
  /* And a `$parent` named by the host still wins. */
93
162
  const $mine = document.createElement("div");
94
- const named = gobj_create_service("layer_w3", "C_YUI_WINDOW",
95
- {header: "c", body: ["div", {}, "c"], $parent: $mine}, yuno);
96
- gobj_start(named);
97
- expect(gobj_read_attr(named, "$container").parentNode).toBe($mine);
163
+ const named = open_window("layer_w3", {$parent: $mine});
164
+ expect(lives_in(named, $mine)).toBe(true);
98
165
 
99
166
  for(const w of [inside, outside, named]) {
100
- gobj_stop(w);
101
- gobj_destroy(w);
167
+ close(w);
102
168
  }
103
- gobj_stop(shell);
104
- gobj_destroy(shell);
169
+ logout(shell);
170
+ expect(errors()).toEqual([]);
171
+ });
172
+ });
173
+
174
+ describe("a window outlives its shell (logout, then login)", () => {
175
+ test("it goes to the body with the shell, and the next shell takes it in", () => {
176
+ const shell1 = build_shell();
177
+ const dev = open_window("layer_dev"); /* parented to the app */
178
+ expect(lives_in(dev, yui_shell_window_layer(shell1))).toBe(true);
179
+
180
+ /* Logout: the shell and its layers go, the window does not. */
181
+ logout(shell1);
182
+ expect(gobj_is_destroying(dev)).toBe(false);
183
+ expect(lives_in(dev, document.body)).toBe(true);
184
+ expect(on_page(dev)).toBe(true);
185
+
186
+ /* Login: a new shell, and the window in it, on the page. */
187
+ const shell2 = build_shell();
188
+ expect(lives_in(dev, yui_shell_window_layer(shell2))).toBe(true);
189
+ expect(on_page(dev)).toBe(true);
190
+
191
+ close(dev);
192
+ logout(shell2);
193
+ expect(errors()).toEqual([]);
194
+ });
195
+
196
+ test("a window created before any shell is taken in by the first one", () => {
197
+ const early = open_window("layer_early");
198
+ expect(lives_in(early, document.body)).toBe(true);
199
+
200
+ const shell = build_shell();
201
+ expect(lives_in(early, yui_shell_window_layer(shell))).toBe(true);
202
+
203
+ close(early);
204
+ logout(shell);
205
+ expect(errors()).toEqual([]);
206
+ });
207
+
208
+ test("a window its host placed stays there; one whose place went with the shell takes the window place", () => {
209
+ const shell1 = build_shell();
210
+
211
+ /* Placed by its host in an element of the page: never moved. */
212
+ const $mine = document.createElement("div");
213
+ document.body.appendChild($mine);
214
+ const placed = open_window("layer_placed", {$parent: $mine});
215
+
216
+ /* Placed by its host INSIDE the shell (a layer of it), but not
217
+ * its child: the place goes with the shell, the window does not. */
218
+ const $popup = shell1.priv.layers.popup;
219
+ const stranded = open_window("layer_stranded", {$parent: $popup});
220
+
221
+ logout(shell1);
222
+ expect(lives_in(placed, $mine)).toBe(true);
223
+ expect(lives_in(stranded, document.body)).toBe(true);
224
+
225
+ const shell2 = build_shell();
226
+ expect(lives_in(placed, $mine)).toBe(true);
227
+ expect(lives_in(stranded, yui_shell_window_layer(shell2))).toBe(true);
228
+
229
+ close(placed);
230
+ close(stranded);
231
+ $mine.remove();
232
+ logout(shell2);
233
+ expect(errors()).toEqual([]);
234
+ });
235
+
236
+ test("a child of the shell goes with it, and nobody is told about it later", () => {
237
+ const shell1 = build_shell();
238
+ const child = open_window("layer_child", {}, shell1);
239
+ logout(shell1);
240
+ expect(gobj_is_destroying(child)).toBe(true);
241
+
242
+ /* The next shell tells only the live ones: no event to a dead
243
+ * window, no "destroyed gobj still watches". */
244
+ const shell2 = build_shell();
245
+ logout(shell2);
246
+ expect(errors()).toEqual([]);
247
+ });
248
+
249
+ test("its Back entry goes with the shell that held it", () => {
250
+ const shell1 = build_shell({use_hash: true});
251
+ const floating = open_window("layer_back");
252
+ expect(!!gobj_read_attr(floating, "back_overlay")).toBe(true);
253
+
254
+ logout(shell1);
255
+ expect(gobj_read_attr(floating, "back_overlay")).toBe(null);
256
+
257
+ /* The next shell holds no entry of it, and closing it retires
258
+ * nothing there. */
259
+ const shell2 = build_shell({use_hash: true});
260
+ expect(shell2.priv.overlay_stack.length).toBe(0);
261
+ close(floating);
262
+ expect(shell2.priv.overlay_stack.length).toBe(0);
263
+ logout(shell2);
264
+ expect(errors()).toEqual([]);
265
+ });
266
+ });
267
+
268
+ describe("the dock follows the shell", () => {
269
+ test("a floating dock lives in the shell's popup layer, under the dialogs; in the body while there is none", () => {
270
+ const dock = gobj_create_service("layer_dock1", "C_YUI_WINDOW_MANAGER", {}, yuno);
271
+ gobj_start(dock);
272
+ const $dock = gobj_read_attr(dock, "$container");
273
+ expect($dock.parentNode === document.body).toBe(true);
274
+
275
+ const shell1 = build_shell();
276
+ expect($dock.parentNode === shell1.priv.layers.popup).toBe(true);
277
+
278
+ logout(shell1);
279
+ expect($dock.parentNode === document.body).toBe(true);
280
+
281
+ const shell2 = build_shell();
282
+ expect($dock.parentNode === shell2.priv.layers.popup).toBe(true);
283
+
284
+ gobj_stop(dock);
285
+ gobj_destroy(dock);
286
+ logout(shell2);
287
+ expect(errors()).toEqual([]);
288
+ });
289
+
290
+ test("a docked window survives a logout, and its chip still brings it back", () => {
291
+ const dock = gobj_create_service("layer_dock2", "C_YUI_WINDOW_MANAGER", {}, yuno);
292
+ gobj_start(dock);
293
+ const shell1 = build_shell();
294
+ const dev = open_window("layer_docked", {manager: dock});
295
+ const $win = gobj_read_attr(dev, "$container");
296
+ expect(lives_in(dev, yui_shell_window_layer(shell1))).toBe(true);
297
+
298
+ logout(shell1);
299
+ const shell2 = build_shell();
300
+ expect(lives_in(dev, yui_shell_window_layer(shell2))).toBe(true);
301
+ expect(on_page(dev)).toBe(true);
302
+
303
+ /* Its chip is still in the dock, and the dock on the page: a
304
+ * press of its toggle minimises it, another one brings it back
305
+ * on top. */
306
+ const $toggle = gobj_read_attr(dock, "$container").querySelector(".DOCK_CHIP_TOGGLE");
307
+ expect(!!$toggle).toBe(true);
308
+ expect(document.documentElement.contains($toggle)).toBe(true);
309
+ $toggle.click();
310
+ expect($win.style.display).toBe("none");
311
+ $toggle.click();
312
+ expect($win.style.display).toBe(undefined);
313
+ expect(Number($win.style.zIndex) > 10).toBe(true);
314
+
315
+ close(dev);
316
+ gobj_stop(dock);
317
+ gobj_destroy(dock);
318
+ logout(shell2);
319
+ expect(errors()).toEqual([]);
320
+ });
321
+
322
+ test("an inline dock moves to the next shell's zone, and floats in the body between them", () => {
323
+ const dock = gobj_create_service("layer_dock3", "C_YUI_WINDOW_MANAGER", {
324
+ dock_mode: "inline",
325
+ inline_selector: '[data-zone="bottom-sub"]'
326
+ }, yuno);
327
+ gobj_start(dock);
328
+ const $dock = gobj_read_attr(dock, "$container");
329
+
330
+ const shell1 = build_shell();
331
+ expect($dock.parentNode === shell1.priv.zones["bottom-sub"]).toBe(true);
332
+
333
+ logout(shell1);
334
+ expect($dock.parentNode === document.body).toBe(true);
335
+ expect($dock.classList.contains("yui-dock--floating")).toBe(true);
336
+
337
+ const shell2 = build_shell();
338
+ expect($dock.parentNode === shell2.priv.zones["bottom-sub"]).toBe(true);
339
+ expect($dock.classList.contains("yui-dock--inline")).toBe(true);
340
+
341
+ gobj_stop(dock);
342
+ gobj_destroy(dock);
343
+ logout(shell2);
344
+ expect(errors()).toEqual([]);
345
+ });
346
+ });
347
+
348
+ describe("the stacking of the windows", () => {
349
+ /* The z-index a window takes from the dock grows on every focus.
350
+ * In the overlay layer itself it competed with the drawer (18):
351
+ * after a few focus changes a new drawer opened UNDER the windows.
352
+ * The window layer is a stacking context below the drawer, so no
353
+ * number a window takes reaches it. CSS, read as text: the double
354
+ * has no layout. */
355
+ test("the window layer is a stacking context under the drawer", () => {
356
+ const css = readFileSync(join(SRC, "c_yui_shell.css"), "utf8");
357
+ const rule = (selector) => {
358
+ const m = css.match(new RegExp(selector + "\\s*\\{([^}]*)\\}"));
359
+ return m? m[1] : "";
360
+ };
361
+ const z_of = (body) => {
362
+ const m = body.match(/z-index:\s*(\d+)/);
363
+ return m? Number(m[1]) : NaN;
364
+ };
365
+ const windows = rule("\\.yui-layer > \\.yui-window-layer");
366
+ const drawer = rule("\\.yui-drawer");
367
+ expect(windows).toMatch(/position:\s*absolute/);
368
+ expect(windows).toMatch(/pointer-events:\s*none/);
369
+ expect(z_of(windows) < z_of(drawer)).toBe(true);
105
370
  });
106
371
  });
@@ -51,17 +51,21 @@ function errors()
51
51
  }
52
52
 
53
53
  describe("EV_SHOW", () => {
54
- test("without a dock: a hidden window is shown, and goes on top of its layer", () => {
54
+ test("without a dock: a hidden window is shown, and goes on top of the others", () => {
55
55
  const a = gobj_create_service("show_a", "C_YUI_WINDOW", {header: "a", body: ["div", {}, "a"]}, yuno);
56
56
  gobj_start(a);
57
57
  const b = gobj_create_service("show_b", "C_YUI_WINDOW", {header: "b", body: ["div", {}, "b"]}, yuno);
58
58
  gobj_start(b);
59
59
  const $a = gobj_read_attr(a, "$container");
60
+ const $b = gobj_read_attr(b, "$container");
60
61
  $a.style.setProperty("display", "none", "important");
61
62
 
62
63
  gobj_send_event(a, "EV_SHOW", {}, yuno);
63
64
  expect($a.style.getPropertyValue("display")).toBe("");
64
- expect($a.parentNode.lastChild).toBe($a);
65
+ /* On top by the windows' one z-order (yui_shell_raise_window),
66
+ * not by being moved in the DOM: a move takes its focus and the
67
+ * press that started on it. */
68
+ expect(Number($a.style.zIndex) > Number($b.style.zIndex)).toBe(true);
65
69
  expect(errors()).toEqual([]);
66
70
  });
67
71