@yuneta/gobj-ui 7.26.27 → 7.26.29
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/README.md +590 -25
- package/dist/gobj-ui.cjs.js +1080 -229
- package/dist/gobj-ui.es.js +1081 -230
- package/package.json +4 -2
- package/scripts/validate-locales.mjs +2656 -315
- package/src/c_g6_nodes_tree.js +1086 -253
- package/src/c_yui_form.js +16 -4
- package/src/c_yui_fsm_graph.js +5 -0
- package/src/c_yui_gclass.js +11 -0
- package/src/c_yui_gobj_tree_js.js +18 -1
- package/src/c_yui_json_graph.js +5 -0
- package/src/c_yui_nav.js +21 -16
- package/src/c_yui_pager.js +1 -1
- package/src/c_yui_period.js +10 -3
- package/src/c_yui_schema_editor.js +34 -18
- package/src/c_yui_shell.js +11 -10
- package/src/c_yui_treedb_graph.js +17 -9
- package/src/c_yui_treedb_schema.js +5 -0
- package/src/c_yui_treedb_topic_with_form.js +9 -9
- package/src/c_yui_treedb_topics.js +13 -13
- package/src/c_yui_window.js +2 -2
- package/src/c_yui_window_manager.js +5 -2
- package/src/c_yui_wizard.js +8 -1
- package/src/form_json_field.js +7 -0
- package/src/g6_canvas_tab_order.js +84 -0
- package/src/g6_canvas_tab_order.wiring.test.js +232 -0
- package/src/g6_nodes_tree_controls.test.js +547 -0
- package/src/g6_nodes_tree_focus.wiring.test.js +1360 -0
- package/src/g6_nodes_tree_i18n_keys.test.js +241 -5
- package/src/i18n_key_functions.js +235 -0
- package/src/i18n_key_functions.test.js +1525 -0
- package/src/lib_graph.js +38 -1
- package/src/nav_backbar_name.wiring.test.js +82 -0
- package/src/schema_flags.js +47 -0
- package/src/schema_import.js +7 -0
- package/src/shell_modals.js +20 -16
- package/src/shell_route_map.js +13 -3
- package/src/wizard_helpers.js +8 -0
- package/src/yui_dev.js +16 -3
- package/src/yui_dev_controls.test.js +174 -0
- package/src/yui_gclass_view.js +1 -1
- package/src/yui_graph_camera.js +1 -1
- package/src/yui_install.js +10 -8
- package/src/yui_table_select.js +2 -2
- package/src/yui_time.js +17 -2
|
@@ -565,7 +565,7 @@ function show_load_error(gobj, message)
|
|
|
565
565
|
$container.insertBefore($err, $container.firstChild);
|
|
566
566
|
}
|
|
567
567
|
let treedb = gobj_read_attr(gobj, "treedb_name") || "";
|
|
568
|
-
$err.textContent = (treedb ? `${treedb}: ` : "") + (message || "cannot load treedb");
|
|
568
|
+
$err.textContent = (treedb ? `${treedb}: ` : "") + (message || t("cannot load treedb"));
|
|
569
569
|
}
|
|
570
570
|
|
|
571
571
|
/************************************************************
|
|
@@ -1368,8 +1368,9 @@ function process_treedb_descs(gobj)
|
|
|
1368
1368
|
);
|
|
1369
1369
|
|
|
1370
1370
|
// TODO get icon from remote config
|
|
1371
|
-
|
|
1372
|
-
|
|
1371
|
+
/* `key` is a topic of the backend's schema: data */
|
|
1372
|
+
add_tab(gobj, gobj_topic_form, id, /* i18n: data */ key, "yi-table");
|
|
1373
|
+
add_topic_card(gobj, id, /* i18n: data */ key, "yi-table", desc);
|
|
1373
1374
|
|
|
1374
1375
|
gobj_start(gobj_topic_form);
|
|
1375
1376
|
}
|
|
@@ -1390,7 +1391,7 @@ function process_treedb_descs(gobj)
|
|
|
1390
1391
|
if(gobj.priv._pending_info) {
|
|
1391
1392
|
let ti = gobj.priv._pending_info;
|
|
1392
1393
|
gobj.priv._pending_info = null;
|
|
1393
|
-
show_topic_info(gobj, ti);
|
|
1394
|
+
show_topic_info(gobj, /* i18n: data */ ti); // a topic name
|
|
1394
1395
|
} else if(href && href.indexOf("?") >= 0) {
|
|
1395
1396
|
gobj_send_event(gobj, "EV_SHOW", {href: href}, gobj);
|
|
1396
1397
|
} else {
|
|
@@ -2075,7 +2076,7 @@ function open_json_viewer(gobj, opts)
|
|
|
2075
2076
|
close_json_viewer(gobj);
|
|
2076
2077
|
return;
|
|
2077
2078
|
}
|
|
2078
|
-
priv.json_modal = yui_shell_show_modal(shell, $box, {
|
|
2079
|
+
priv.json_modal = yui_shell_show_modal(shell, /* i18n: data */ $box, {
|
|
2079
2080
|
dialog: true,
|
|
2080
2081
|
logical_class: "TREEDB_JSON_SHEET",
|
|
2081
2082
|
title_prefix: priv.treedb_name,
|
|
@@ -2223,8 +2224,7 @@ function answer_print_tranger_refused(gobj, path, answer)
|
|
|
2223
2224
|
gobj_send_event(jv, "EV_SUBTREE_ERROR",
|
|
2224
2225
|
Object.assign({path: path}, answer), gobj);
|
|
2225
2226
|
} else {
|
|
2226
|
-
yui_shell_show_error(yui_shell_of(gobj),
|
|
2227
|
-
answer.i18n || answer.error, {t: t});
|
|
2227
|
+
yui_shell_show_error(yui_shell_of(gobj), answer.i18n || answer.error, {t: t});
|
|
2228
2228
|
}
|
|
2229
2229
|
}
|
|
2230
2230
|
|
|
@@ -2274,7 +2274,7 @@ function request_print_tranger(gobj, path)
|
|
|
2274
2274
|
}, gobj);
|
|
2275
2275
|
if(ret) {
|
|
2276
2276
|
log_error(ret);
|
|
2277
|
-
answer_print_tranger_refused(gobj, path, {error: refusal_text(ret)});
|
|
2277
|
+
answer_print_tranger_refused(gobj, path, {error: /* i18n: data */ refusal_text(ret)});
|
|
2278
2278
|
return;
|
|
2279
2279
|
}
|
|
2280
2280
|
if(path) {
|
|
@@ -2386,7 +2386,7 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2386
2386
|
{path: path, error: comment || "print-tranger failed"}, gobj);
|
|
2387
2387
|
} else {
|
|
2388
2388
|
yui_shell_show_error(yui_shell_of(gobj),
|
|
2389
|
-
comment || "print-tranger failed", {t: t});
|
|
2389
|
+
/* i18n: data */ comment || "print-tranger failed", {t: t});
|
|
2390
2390
|
}
|
|
2391
2391
|
return 0;
|
|
2392
2392
|
}
|
|
@@ -2411,7 +2411,7 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2411
2411
|
}
|
|
2412
2412
|
if(result < 0) {
|
|
2413
2413
|
yui_shell_show_error(yui_shell_of(gobj),
|
|
2414
|
-
comment || "schema-file failed", {t: t});
|
|
2414
|
+
/* i18n: data */ comment || "schema-file failed", {t: t});
|
|
2415
2415
|
return 0;
|
|
2416
2416
|
}
|
|
2417
2417
|
gobj_send_event(jv, "EV_SET_JSON", {json: data}, gobj);
|
|
@@ -2459,9 +2459,9 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2459
2459
|
/* The schema couldn't load (not a treedb, no authz for it, backend
|
|
2460
2460
|
* down…). Show an inline banner in the view rather than a blocking
|
|
2461
2461
|
* app-modal that wedges the whole SPA behind an empty tab. */
|
|
2462
|
-
show_load_error(gobj, t(comment));
|
|
2462
|
+
show_load_error(gobj, t(/* i18n: data */ comment));
|
|
2463
2463
|
} else {
|
|
2464
|
-
yui_shell_show_error(yui_shell_of(gobj), comment, {t: t});
|
|
2464
|
+
yui_shell_show_error(yui_shell_of(gobj), /* i18n: data */ comment, {t: t});
|
|
2465
2465
|
/* A refused write leaves the TABLE showing what the operator
|
|
2466
2466
|
* typed and the store holding what it had. That is tolerable
|
|
2467
2467
|
* for a form (it stays open on the values it failed with) and
|
|
@@ -2737,7 +2737,7 @@ function ac_show(gobj, event, kw, src)
|
|
|
2737
2737
|
************************************************************/
|
|
2738
2738
|
function ac_show_topic_info(gobj, event, kw, src)
|
|
2739
2739
|
{
|
|
2740
|
-
show_topic_info(gobj, kw && kw.topic);
|
|
2740
|
+
show_topic_info(gobj, /* i18n: data */ kw && kw.topic); // a topic name
|
|
2741
2741
|
return 0;
|
|
2742
2742
|
}
|
|
2743
2743
|
|
package/src/c_yui_window.js
CHANGED
|
@@ -836,7 +836,7 @@ function close_window(gobj)
|
|
|
836
836
|
return {state: "vetoed"};
|
|
837
837
|
}
|
|
838
838
|
const answer = yui_shell_confirm_yesnocancel(
|
|
839
|
-
yui_shell_of(gobj), kw_close.warning,
|
|
839
|
+
yui_shell_of(gobj), /* i18n: data */ kw_close.warning,
|
|
840
840
|
{t: t, yes_label: "yes", no_label: "no", cancel_label: "cancel"}
|
|
841
841
|
).then(function(answer) {
|
|
842
842
|
if(answer !== "yes") {
|
|
@@ -909,7 +909,7 @@ function set_max_icon(gobj, maximized)
|
|
|
909
909
|
}
|
|
910
910
|
let $btn = $container.querySelector('.wc-max');
|
|
911
911
|
if($btn) {
|
|
912
|
-
|
|
912
|
+
const key = maximized ? 'restore' : 'maximize';
|
|
913
913
|
$btn.innerHTML = maximized ? WC_RESTORE : WC_MAX;
|
|
914
914
|
$btn.setAttribute('title', t(key));
|
|
915
915
|
$btn.setAttribute('data-i18n-title', key);
|
|
@@ -510,9 +510,12 @@ function ac_register_window(gobj, event, kw, src)
|
|
|
510
510
|
label_items.push(['span', {class: 'yui-dock-label-prefix'},
|
|
511
511
|
text_node(prefix)]); // data, not markup
|
|
512
512
|
}
|
|
513
|
+
/* An FSM action: what it is given is the kw of EV_REGISTER_WINDOW,
|
|
514
|
+
* which no reader can follow. `title_key` is the window's `title`,
|
|
515
|
+
* a key its HOST chose when it created the window. */
|
|
513
516
|
if(title_key) {
|
|
514
517
|
label_items.push(
|
|
515
|
-
['span', {class: 'yui-dock-label-kind', i18n: title_key},
|
|
518
|
+
['span', {class: 'yui-dock-label-kind', i18n: /* i18n: data */ title_key},
|
|
516
519
|
kind_text || title_key]
|
|
517
520
|
);
|
|
518
521
|
}
|
|
@@ -532,7 +535,7 @@ function ac_register_window(gobj, event, kw, src)
|
|
|
532
535
|
let $chip = createElement2(
|
|
533
536
|
['div', Object.assign(
|
|
534
537
|
{class: 'yui-dock-chip', role: 'button', tabindex: '0', title: title},
|
|
535
|
-
(!prefix && title_key) ? {'data-i18n-title': title_key} : {}
|
|
538
|
+
(!prefix && title_key) ? {'data-i18n-title': /* i18n: data */ title_key} : {}
|
|
536
539
|
), [
|
|
537
540
|
chip_lead(kw.icon),
|
|
538
541
|
['span', {class: 'yui-dock-label'}, label_items],
|
package/src/c_yui_wizard.js
CHANGED
|
@@ -75,6 +75,12 @@ SDATA(data_type_t.DTP_POINTER, "$container", 0, null, "HTMLElement roo
|
|
|
75
75
|
SDATA_END()
|
|
76
76
|
];
|
|
77
77
|
|
|
78
|
+
/* The default keys of the three buttons, which reach t() through the
|
|
79
|
+
* attrs: published for a consumer's validate-locales, which asks the
|
|
80
|
+
* app's locales for every key of an exported `*_I18N_KEYS` array. A
|
|
81
|
+
* host that names its own buttons asks for its own keys besides. */
|
|
82
|
+
export const C_YUI_WIZARD_I18N_KEYS = ["confirm", "next", "back"];
|
|
83
|
+
|
|
78
84
|
let PRIVATE_DATA = {
|
|
79
85
|
steps: null,
|
|
80
86
|
idx: 0,
|
|
@@ -267,7 +273,8 @@ function render_chrome(gobj)
|
|
|
267
273
|
|
|
268
274
|
$back.classList.toggle('is-hidden', !model.show_back);
|
|
269
275
|
|
|
270
|
-
$plabel.setAttribute('data-i18n',
|
|
276
|
+
$plabel.setAttribute('data-i18n',
|
|
277
|
+
/* i18n: WIZARD_HELPERS_I18N_KEYS, C_YUI_WIZARD_I18N_KEYS */ model.primary_label);
|
|
271
278
|
$plabel.textContent = t(model.primary_label);
|
|
272
279
|
$primary.setAttribute('title', t(model.primary_label));
|
|
273
280
|
$primary.setAttribute('data-i18n-title', model.primary_label);
|
package/src/form_json_field.js
CHANGED
|
@@ -64,6 +64,13 @@ function value_fits(value, shape)
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/* The key parse_json_field() returns as data (its `error`), which the
|
|
68
|
+
* form hands to t(): published for a consumer's validate-locales, which
|
|
69
|
+
* asks the app's locales for every key of an exported `*_I18N_KEYS`
|
|
70
|
+
* array. i18n_key_functions.test.js holds it to every `error:` this
|
|
71
|
+
* module writes. */
|
|
72
|
+
export const FORM_JSON_FIELD_I18N_KEYS = ["invalid json"];
|
|
73
|
+
|
|
67
74
|
/************************************************************
|
|
68
75
|
* Parse what a json field holds.
|
|
69
76
|
*
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* g6_canvas_tab_order.js
|
|
3
|
+
*
|
|
4
|
+
* The canvases of a G6 graph in the page's Tab order: the main
|
|
5
|
+
* one in its place, the other layers out of it.
|
|
6
|
+
*
|
|
7
|
+
* G6 stacks four canvases (background, main, label, transient)
|
|
8
|
+
* and gives each of them `tabIndex = 1` (configCanvasDom, when it
|
|
9
|
+
* builds the Canvas and again on a renderer change). A POSITIVE
|
|
10
|
+
* tabindex goes before every control of the page: Tab from a
|
|
11
|
+
* graph went to its other canvases, then to the top of the page
|
|
12
|
+
* and through every control there, and only then to the graph's
|
|
13
|
+
* own buttons. The main canvas -- the one G6 listens on, and the
|
|
14
|
+
* one a view puts the keyboard on -- takes 0, so Tab goes from it
|
|
15
|
+
* to the graph's toolbars and buttons in DOM order; the other
|
|
16
|
+
* layers receive nothing (`pointer-events: none`) and take -1.
|
|
17
|
+
*
|
|
18
|
+
* Every view that creates a G6 Graph calls
|
|
19
|
+
* keep_canvas_tab_order() right after `new Graph()`: the order is
|
|
20
|
+
* put right each time G6 writes the 1 -- once the canvas is built
|
|
21
|
+
* (on the first render(), whichever function of the view calls
|
|
22
|
+
* it), and after a renderer change. One place, so a view with
|
|
23
|
+
* several render() calls cannot miss the one that builds the
|
|
24
|
+
* canvas.
|
|
25
|
+
*
|
|
26
|
+
* Copyright (c) 2026, ArtGins.
|
|
27
|
+
* All Rights Reserved.
|
|
28
|
+
***********************************************************************/
|
|
29
|
+
import {
|
|
30
|
+
gobj_short_name,
|
|
31
|
+
log_error,
|
|
32
|
+
} from "@yuneta/gobj-js";
|
|
33
|
+
|
|
34
|
+
import {GraphEvent} from "@antv/g6";
|
|
35
|
+
|
|
36
|
+
/************************************************************
|
|
37
|
+
* Main canvas 0, every other layer -1. Returns 0, or -1 with
|
|
38
|
+
* the cause logged.
|
|
39
|
+
************************************************************/
|
|
40
|
+
function set_canvas_tab_order(gobj, graph)
|
|
41
|
+
{
|
|
42
|
+
let canvas = graph? graph.getCanvas() : null;
|
|
43
|
+
|
|
44
|
+
if(!canvas || typeof canvas.getLayers !== "function") {
|
|
45
|
+
log_error(`${gobj_short_name(gobj)}: no canvas to set the Tab order of`);
|
|
46
|
+
return -1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let ret = 0;
|
|
50
|
+
let layers = canvas.getLayers() || {};
|
|
51
|
+
for(let name of Object.keys(layers)) {
|
|
52
|
+
let $el = null;
|
|
53
|
+
try {
|
|
54
|
+
$el = layers[name].getContextService().getDomElement();
|
|
55
|
+
} catch(e) {
|
|
56
|
+
$el = null;
|
|
57
|
+
}
|
|
58
|
+
if(!$el) {
|
|
59
|
+
log_error(`${gobj_short_name(gobj)}: canvas layer '${name}' has no element`);
|
|
60
|
+
ret = -1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
$el.tabIndex = (name === "main")? 0 : -1;
|
|
64
|
+
}
|
|
65
|
+
return ret;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/************************************************************
|
|
69
|
+
* Keep the order for the life of `graph`: on every moment G6
|
|
70
|
+
* writes its own.
|
|
71
|
+
************************************************************/
|
|
72
|
+
function keep_canvas_tab_order(gobj, graph)
|
|
73
|
+
{
|
|
74
|
+
for(let ev of [GraphEvent.AFTER_CANVAS_INIT, GraphEvent.AFTER_RENDERER_CHANGE]) {
|
|
75
|
+
graph.on(ev, () => {
|
|
76
|
+
set_canvas_tab_order(gobj, graph);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
set_canvas_tab_order,
|
|
83
|
+
keep_canvas_tab_order,
|
|
84
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/***********************************************************************
|
|
2
|
+
* g6_canvas_tab_order.wiring.test.js
|
|
3
|
+
*
|
|
4
|
+
* Every view that draws a G6 graph puts its canvases in the page's
|
|
5
|
+
* Tab order the same way (g6_canvas_tab_order.js): the main canvas
|
|
6
|
+
* 0, the other layers -1 (REV-CLOUD V20.5 #2, and the four views
|
|
7
|
+
* the second pass left out).
|
|
8
|
+
*
|
|
9
|
+
* G6 stacks four canvases and gives each `tabIndex = 1`; a
|
|
10
|
+
* positive tabindex goes before every control of the page, so Tab
|
|
11
|
+
* from a graph went through its other canvases and the whole page
|
|
12
|
+
* before it reached the graph's own buttons.
|
|
13
|
+
*
|
|
14
|
+
* The real gclasses, on a G6 Graph double that does what G6 does
|
|
15
|
+
* here: the FIRST render() builds the four canvases with
|
|
16
|
+
* `tabIndex = 1` and says so with `aftercanvasinit`; a renderer
|
|
17
|
+
* change writes the 1 again and says `afterrendererchange`.
|
|
18
|
+
*
|
|
19
|
+
* Copyright (c) 2026, ArtGins.
|
|
20
|
+
* All Rights Reserved.
|
|
21
|
+
***********************************************************************/
|
|
22
|
+
import {describe, test, expect, beforeAll, beforeEach, vi} from "vitest";
|
|
23
|
+
import {install_dom_double} from "../test/dom_double.js";
|
|
24
|
+
|
|
25
|
+
install_dom_double();
|
|
26
|
+
|
|
27
|
+
/* The views watch their container; nothing resizes here. */
|
|
28
|
+
if(typeof globalThis.ResizeObserver === "undefined") {
|
|
29
|
+
globalThis.ResizeObserver = class {
|
|
30
|
+
observe() {
|
|
31
|
+
}
|
|
32
|
+
unobserve() {
|
|
33
|
+
}
|
|
34
|
+
disconnect() {
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const graphs = vi.hoisted(() => []);
|
|
40
|
+
vi.mock("@antv/g6", async (importOriginal) => {
|
|
41
|
+
const real = await importOriginal();
|
|
42
|
+
class FakeGraph {
|
|
43
|
+
constructor(options) {
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.handlers = {};
|
|
46
|
+
this.layers = null;
|
|
47
|
+
graphs.push(this);
|
|
48
|
+
return new Proxy(this, {
|
|
49
|
+
get: (t, k) => (k in t) ? t[k] : (() => undefined),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
container_el() {
|
|
53
|
+
const c = this.options.container;
|
|
54
|
+
return (typeof c === "string") ? document.getElementById(c) : c;
|
|
55
|
+
}
|
|
56
|
+
write_canvas_dom() {
|
|
57
|
+
for(const name of Object.keys(this.layers)) {
|
|
58
|
+
this.layers[name].el.tabIndex = 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
render() {
|
|
62
|
+
if(!this.layers) {
|
|
63
|
+
const $c = this.container_el();
|
|
64
|
+
this.layers = {};
|
|
65
|
+
for(const name of ["background", "main", "label", "transient"]) {
|
|
66
|
+
const el = document.createElement("canvas");
|
|
67
|
+
el.setAttribute("data-layer", name);
|
|
68
|
+
$c.appendChild(el);
|
|
69
|
+
this.layers[name] = {
|
|
70
|
+
el: el,
|
|
71
|
+
getContextService: () => ({getDomElement: () => el}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
this.write_canvas_dom();
|
|
75
|
+
this.emit(real.GraphEvent.AFTER_CANVAS_INIT, {canvas: this.getCanvas()});
|
|
76
|
+
}
|
|
77
|
+
return Promise.resolve();
|
|
78
|
+
}
|
|
79
|
+
setRenderer() {
|
|
80
|
+
this.write_canvas_dom();
|
|
81
|
+
this.emit(real.GraphEvent.AFTER_RENDERER_CHANGE, {});
|
|
82
|
+
}
|
|
83
|
+
getCanvas() {
|
|
84
|
+
return {
|
|
85
|
+
getLayers: () => this.layers,
|
|
86
|
+
getContainer: () => this.container_el(),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
on(name, fn) {
|
|
90
|
+
(this.handlers[name] = this.handlers[name] || []).push(fn);
|
|
91
|
+
}
|
|
92
|
+
emit(name, ev) {
|
|
93
|
+
for(const fn of (this.handlers[name] || [])) {
|
|
94
|
+
fn(ev);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
getZoom() {
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
getData() {
|
|
101
|
+
return {nodes: [], edges: [], combos: []};
|
|
102
|
+
}
|
|
103
|
+
getNodeData() {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
getEdgeData() {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
getPlugins() {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
getBehaviors() {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
getOptions() {
|
|
116
|
+
return {behaviors: []};
|
|
117
|
+
}
|
|
118
|
+
draw() {
|
|
119
|
+
return Promise.resolve();
|
|
120
|
+
}
|
|
121
|
+
fitView() {
|
|
122
|
+
return Promise.resolve();
|
|
123
|
+
}
|
|
124
|
+
zoomTo() {
|
|
125
|
+
return Promise.resolve();
|
|
126
|
+
}
|
|
127
|
+
destroy() {
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {...real, Graph: FakeGraph};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const {
|
|
134
|
+
SDATA_END, gclass_create, gobj_start_up, gobj_create_yuno, gobj_create_pure_child,
|
|
135
|
+
gobj_start, gobj_read_attr, set_log_callback, register_c_timer,
|
|
136
|
+
} = await import("@yuneta/gobj-js");
|
|
137
|
+
const {register_c_yui_json_graph} = await import("./c_yui_json_graph.js");
|
|
138
|
+
const {register_c_yui_gobj_tree_js} = await import("./c_yui_gobj_tree_js.js");
|
|
139
|
+
const {register_c_yui_fsm_graph} = await import("./c_yui_fsm_graph.js");
|
|
140
|
+
const {register_c_yui_treedb_schema} = await import("./c_yui_treedb_schema.js");
|
|
141
|
+
const {register_c_g6_nodes_tree} = await import("./c_g6_nodes_tree.js");
|
|
142
|
+
|
|
143
|
+
const logged = [];
|
|
144
|
+
let yuno = null;
|
|
145
|
+
|
|
146
|
+
beforeAll(async () => {
|
|
147
|
+
const i18next = (await import("i18next")).default;
|
|
148
|
+
await i18next.init({lng: "en", resources: {en: {translation: {}}}});
|
|
149
|
+
gobj_start_up(null, null, null, null, null, null, null);
|
|
150
|
+
set_log_callback((level, msg) => {
|
|
151
|
+
logged.push({level: String(level), msg: String(msg)});
|
|
152
|
+
});
|
|
153
|
+
register_c_timer();
|
|
154
|
+
register_c_yui_json_graph();
|
|
155
|
+
register_c_yui_gobj_tree_js();
|
|
156
|
+
register_c_yui_fsm_graph();
|
|
157
|
+
register_c_yui_treedb_schema();
|
|
158
|
+
register_c_g6_nodes_tree();
|
|
159
|
+
gclass_create("C_TEST_TAB_ORDER_HOST", [], [["ST_IDLE", []]], {}, 0,
|
|
160
|
+
[SDATA_END()], {}, 0, 0, 0, 0);
|
|
161
|
+
yuno = gobj_create_yuno("tab_order_yuno", "C_TEST_TAB_ORDER_HOST", {});
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
beforeEach(() => {
|
|
165
|
+
logged.length = 0;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const DESC = {topic_name: "users", cols: [
|
|
169
|
+
{id: "id", type: "string", flag: ["persistent"]},
|
|
170
|
+
]};
|
|
171
|
+
|
|
172
|
+
/* Each view, with what it needs to render a first time. */
|
|
173
|
+
const VIEWS = {
|
|
174
|
+
C_YUI_JSON_GRAPH: {json_data: {a: 1, b: [1, 2]}},
|
|
175
|
+
C_YUI_GOBJ_TREE_JS: {},
|
|
176
|
+
C_YUI_FSM_GRAPH: {fsm: {
|
|
177
|
+
entry_state: "ST_IDLE",
|
|
178
|
+
states: [{name: "ST_IDLE"}, {name: "ST_BUSY"}],
|
|
179
|
+
rows: [
|
|
180
|
+
{event: "EV_GO", cells: [{next_state: "ST_BUSY"}, null]},
|
|
181
|
+
{event: "EV_DONE", cells: [null, {next_state: "ST_IDLE"}]},
|
|
182
|
+
],
|
|
183
|
+
published: [],
|
|
184
|
+
unreachable: [],
|
|
185
|
+
}},
|
|
186
|
+
C_YUI_TREEDB_SCHEMA: {descs: {users: DESC}},
|
|
187
|
+
C_G6_NODES_TREE: {with_toolbar: false, operation_mode: "reading"},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
let seq = 0;
|
|
191
|
+
|
|
192
|
+
async function mount(gclass_name, attrs)
|
|
193
|
+
{
|
|
194
|
+
const $container = document.createElement("div");
|
|
195
|
+
document.body.appendChild($container);
|
|
196
|
+
const n0 = graphs.length;
|
|
197
|
+
const view = gobj_create_pure_child(`tab_order_${++seq}`, gclass_name,
|
|
198
|
+
Object.assign({$container: $container}, attrs), yuno);
|
|
199
|
+
/* Mounted as a host mounts it: a view that builds its own root
|
|
200
|
+
* (json, gobj tree) is in the document before it starts, and
|
|
201
|
+
* G6 finds its canvas mount there by id. */
|
|
202
|
+
document.body.appendChild(gobj_read_attr(view, "$container"));
|
|
203
|
+
gobj_start(view);
|
|
204
|
+
/* render() resolves on its own time, as G6's does. */
|
|
205
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
206
|
+
return {view, $container, graph: graphs.length > n0 ? graphs[graphs.length - 1] : null};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function tab_order(graph)
|
|
210
|
+
{
|
|
211
|
+
const order = {};
|
|
212
|
+
for(const [name, layer] of Object.entries(graph.layers || {})) {
|
|
213
|
+
order[name] = layer.el.tabIndex;
|
|
214
|
+
}
|
|
215
|
+
return order;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
describe("every G6 view: the main canvas in the Tab order, the other layers out", () => {
|
|
219
|
+
for(const [gclass_name, attrs] of Object.entries(VIEWS)) {
|
|
220
|
+
test(`${gclass_name}: after its first render, and after a renderer change`, async () => {
|
|
221
|
+
const {graph} = await mount(gclass_name, attrs);
|
|
222
|
+
expect(graph, `${gclass_name} built a graph`).toBeTruthy();
|
|
223
|
+
expect(graph.layers, `${gclass_name} rendered when it started`).toBeTruthy();
|
|
224
|
+
expect(tab_order(graph)).toEqual({background: -1, main: 0, label: -1, transient: -1});
|
|
225
|
+
|
|
226
|
+
graph.setRenderer();
|
|
227
|
+
expect(tab_order(graph)).toEqual({background: -1, main: 0, label: -1, transient: -1});
|
|
228
|
+
|
|
229
|
+
expect(logged.filter((l) => l.level === "error").map((l) => l.msg)).toEqual([]);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
});
|