@nanobpm/urban 0.61.0 → 0.63.0
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/dist/runtime/core/host.d.ts +15 -0
- package/dist/runtime/core/host.js +28 -0
- package/dist/runtime/core/modules/runtime.gen.js +1 -1
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +4 -0
- package/package.json +1 -1
- package/src/runtime/browser/runtime.browser.js +113 -49
- package/src/runtime/browser/runtime.detailform.test.ts +268 -0
- package/src/runtime/core/host.ts +36 -0
- package/src/runtime/core/modules/pages.test.ts +43 -16
- package/src/runtime/core/modules/runtime.gen.ts +1 -1
- package/src/runtime/index.ts +4 -0
|
@@ -1350,18 +1350,23 @@ test("child grids support per-row detail expansion, collapsed by default (#332)"
|
|
|
1350
1350
|
// affordance instead of dumping the whole history flat.
|
|
1351
1351
|
assert.match(js, /const cdetail = cg\.detail \|\| cg\.expandable \|\| null;/);
|
|
1352
1352
|
assert.match(js, /const chasExpand = cdetail != null;/);
|
|
1353
|
-
// The expander shares the top-level detail look
|
|
1354
|
-
// pc-row-actions cell, revealing
|
|
1355
|
-
|
|
1353
|
+
// The expander shares the top-level detail look and the shared chevronToggle()
|
|
1354
|
+
// factory: a pc-chevron disclosure button in a pc-row-actions cell, revealing
|
|
1355
|
+
// detailPanel(cr, cdetail) in a following row. setChevronOpen seeds the glyph +
|
|
1356
|
+
// aria-expanded from the persisted collapse state so they can't drift.
|
|
1357
|
+
// The panel's escalation form (if any) is threaded a child-scoped onSuccess so
|
|
1358
|
+
// answering it re-fetches only this child grid (load()), not the whole page (#333).
|
|
1359
|
+
assert.match(js, /const ctoggle = chevronToggle\("Toggle row details"\);\s*setChevronOpen\(ctoggle, !collapsed\);/);
|
|
1356
1360
|
assert.match(js, /cells\.push\(el\("td", \{ class: "pc-row-actions" \}, ctoggle\)\);/);
|
|
1357
|
-
assert.match(js, /cdtr\.firstChild\.append\(detailPanel\(cr, cdetail\)\)/);
|
|
1361
|
+
assert.match(js, /cdtr\.firstChild\.append\(detailPanel\(cr, cdetail, \(\) => load\(\)\)\)/);
|
|
1358
1362
|
// Rows are collapsed by default; detail.defaultCollapsed/detail.collapsed (both
|
|
1359
1363
|
// defaulting to true) seed the initial state.
|
|
1360
1364
|
assert.match(js, /cdetail\.defaultCollapsed != null/);
|
|
1361
1365
|
assert.match(js, /cdetail\.collapsed != null/);
|
|
1362
1366
|
// detailPanel is now config-parametrised so the top-level grid and child grids
|
|
1363
|
-
// share one implementation (link + fields + nested children + form).
|
|
1364
|
-
|
|
1367
|
+
// share one implementation (link + fields + nested children + form). Its optional
|
|
1368
|
+
// onSuccess lets each caller scope the form's post-answer refresh (#333).
|
|
1369
|
+
assert.match(js, /function detailPanel\(row, cfg, onSuccess\)/);
|
|
1365
1370
|
assert.match(js, /const d = cfg \|\| detail;/);
|
|
1366
1371
|
});
|
|
1367
1372
|
|
|
@@ -1401,22 +1406,44 @@ test("child-grid detail panel never falls back to the top-level grid's form (#33
|
|
|
1401
1406
|
const res = await dispatch("GET", "/app/runtime.js");
|
|
1402
1407
|
const js = res.body ?? "";
|
|
1403
1408
|
// detailPanel(cr, cdetail) renders a child grid's own detail config. Its form is
|
|
1404
|
-
//
|
|
1405
|
-
// must have NO form — it must NOT implicitly reuse the top-level
|
|
1406
|
-
// detail.form. So
|
|
1407
|
-
//
|
|
1408
|
-
assert.match(js, /
|
|
1409
|
-
assert.
|
|
1409
|
+
// built from d.form (the panel's own config): when the child config declares no
|
|
1410
|
+
// `form` the panel must have NO form — it must NOT implicitly reuse the top-level
|
|
1411
|
+
// grid's detail.form. So the shared buildDetailForm uses its `f` argument verbatim
|
|
1412
|
+
// (returning null when it is missing) rather than falling back to detail.form.
|
|
1413
|
+
assert.match(js, /const form = buildDetailForm\(d\.form, row,/);
|
|
1414
|
+
assert.match(js, /function buildDetailForm\(f, row, onSuccess\) \{\s*if \(!f \|\| !row\[f\.showWhenField\]\) return null;/);
|
|
1415
|
+
assert.doesNotMatch(js, /d\.form \|\| \(?detail/);
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
test("child-grid and top-level detail forms thread different post-answer refreshes (#333)", async () => {
|
|
1419
|
+
const res = await dispatch("GET", "/app/runtime.js");
|
|
1420
|
+
const js = res.body ?? "";
|
|
1421
|
+
// The #333 vs #332 reconciliation (#336): both grids share one detailPanel /
|
|
1422
|
+
// buildDetailForm, but they thread DIFFERENT onSuccess callbacks. A child-grid
|
|
1423
|
+
// row's inline answer must refresh ONLY that child grid — via its local load()
|
|
1424
|
+
// re-fetch — so the answered row drops without a disruptive whole-page re-poll.
|
|
1425
|
+
// The child callback returns the load() promise (not swallowed) so a failed
|
|
1426
|
+
// re-fetch surfaces in buildDetailForm and re-enables its submit button.
|
|
1427
|
+
assert.match(js, /detailPanel\(cr, cdetail, \(\) => load\(\)\)/);
|
|
1428
|
+
// load() is the child-scoped re-fetch: it repaints just this child grid's tbody.
|
|
1429
|
+
assert.match(js, /async function load\(\) \{[\s\S]*?cbody\.replaceChildren\(\);/);
|
|
1430
|
+
// The top-level grid keeps its whole-page re-poll: detailPanel's default onSuccess
|
|
1431
|
+
// (used when no child-scoped callback is passed) dispatches pc:refresh.
|
|
1432
|
+
assert.match(js, /onSuccess \|\| \(\(\) => \{ document\.dispatchEvent\(new CustomEvent\("pc:refresh"\)\); \}\)/);
|
|
1433
|
+
// The single shared expander (cdetail) hosts the form — there is no duplicate,
|
|
1434
|
+
// parallel form-only child expander (the pre-reconciliation cdetailForm surface).
|
|
1435
|
+
assert.doesNotMatch(js, /const cdetailForm = /);
|
|
1410
1436
|
});
|
|
1411
1437
|
|
|
1412
1438
|
test("child-grid fetch failure renders a full-width error row (#332)", async () => {
|
|
1413
1439
|
const res = await dispatch("GET", "/app/runtime.js");
|
|
1414
1440
|
const js = res.body ?? "";
|
|
1415
|
-
// The child grid's colspan is computed eagerly, BEFORE the getJSON() fetch
|
|
1416
|
-
//
|
|
1441
|
+
// The child grid's colspan is computed eagerly, BEFORE the getJSON() fetch (now
|
|
1442
|
+
// wrapped in load() so the grid can re-fetch itself after an inline answer, #333),
|
|
1443
|
+
// so the catch path's error row (colspan: cspan) spans the whole table even when
|
|
1417
1444
|
// the fetch throws. A lazily-assigned cspan would be undefined in the catch.
|
|
1418
|
-
assert.match(js, /const cspan = String\(\(ccols\.length \|\| 1\) \+ cextra\)
|
|
1419
|
-
assert.match(js, /\} catch \(e\) \{\s*cbody\.
|
|
1445
|
+
assert.match(js, /const cspan = String\(\(ccols\.length \|\| 1\) \+ cextra\);[\s\S]*?async function load\(\) \{[\s\S]*?await getJSON\(dataUrl\(cg\.source/);
|
|
1446
|
+
assert.match(js, /\} catch \(e\) \{\s*cbody\.replaceChildren\(el\("tr", \{\}, el\("td", \{ colspan: cspan \}/);
|
|
1420
1447
|
});
|
|
1421
1448
|
|
|
1422
1449
|
test("the renderer honours an optional Tier-2 page-level mobile layout variant (#268)", async () => {
|