@nanobpm/urban 0.19.0 → 0.20.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.
|
@@ -392,6 +392,27 @@ function renderText(node) {
|
|
|
392
392
|
return el(v === "heading" ? "h1" : "p", { class: cls }, node.props.text || "");
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
+
// A grid td cell. When the column declares linkField, the cell text (the
|
|
396
|
+
// column's own value) becomes a link to the URL held in that other field,
|
|
397
|
+
// opened in a new tab. Only http(s) hrefs are linked — anything else (e.g. a
|
|
398
|
+
// javascript: URL smuggled through row data) falls back to plain text — and
|
|
399
|
+
// external links get rel=noopener noreferrer so the opened page can't reach
|
|
400
|
+
// window.opener. Shared by the top-level grid and child grids.
|
|
401
|
+
function gridCell(col, row) {
|
|
402
|
+
const text = row[col.field] == null ? "" : String(row[col.field]);
|
|
403
|
+
if (col.linkField) {
|
|
404
|
+
const href = row[col.linkField] == null ? "" : String(row[col.linkField]);
|
|
405
|
+
if (text !== "" && /^https?:\/\//i.test(href)) {
|
|
406
|
+
return el(
|
|
407
|
+
"td",
|
|
408
|
+
{},
|
|
409
|
+
el("a", { class: "pc-link", href, target: "_blank", rel: "noopener noreferrer" }, text),
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return el("td", {}, text);
|
|
414
|
+
}
|
|
415
|
+
|
|
395
416
|
function renderActionForm(node) {
|
|
396
417
|
const p = node.props;
|
|
397
418
|
const card = el("section", { class: "pc-card" });
|
|
@@ -552,7 +573,7 @@ function renderDataGrid(node) {
|
|
|
552
573
|
cbody.append(el("tr", {}, el("td", { colspan: cspan }, "None")));
|
|
553
574
|
}
|
|
554
575
|
for (const cr of rows) {
|
|
555
|
-
const cells = ccols.map((c) =>
|
|
576
|
+
const cells = ccols.map((c) => gridCell(c, cr));
|
|
556
577
|
if (cg.lazyField) {
|
|
557
578
|
const lf = cg.lazyField;
|
|
558
579
|
const has = cr[lf.field] != null && String(cr[lf.field]).trim() !== "";
|
|
@@ -606,7 +627,7 @@ function renderDataGrid(node) {
|
|
|
606
627
|
}
|
|
607
628
|
|
|
608
629
|
function renderRow(row) {
|
|
609
|
-
const cells = cols.map((c) =>
|
|
630
|
+
const cells = cols.map((c) => gridCell(c, row));
|
|
610
631
|
let toggle = null;
|
|
611
632
|
if (hasExtra) {
|
|
612
633
|
const actionCell = el("td", { class: "pc-row-actions" });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanobpm/urban",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Urban: build and run code-first apps on Nano — runtime, derivation toolkit, and CLI in one. Author durable processes with @nanobpm/workflow (defineFlow), a typed datasource, forms, triggers and surfaces from a nano.app.json manifest, on Node or Deno.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -89,6 +89,19 @@ test("GET /app/runtime.js serves the renderer module", async () => {
|
|
|
89
89
|
assert.match(res.body ?? "", /pc:refresh/);
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
test("renderer wires a column's linkField to a new-tab anchor", async () => {
|
|
93
|
+
const res = await dispatch("GET", "/app/runtime.js");
|
|
94
|
+
const js = res.body ?? "";
|
|
95
|
+
// A column declaring `linkField` renders its text as a link to that field's
|
|
96
|
+
// URL, opened safely in a new tab. Guard the whole shape so the feature can't
|
|
97
|
+
// silently regress: the field is read, http(s) is required, and the anchor
|
|
98
|
+
// opens in a new tab with a hardened rel.
|
|
99
|
+
assert.match(js, /col\.linkField/);
|
|
100
|
+
assert.match(js, /\^https\?:/);
|
|
101
|
+
assert.match(js, /target: "_blank"/);
|
|
102
|
+
assert.match(js, /rel: "noopener noreferrer"/);
|
|
103
|
+
});
|
|
104
|
+
|
|
92
105
|
test("GET /app/pages/<id> returns the page json, 404 for unknown", async () => {
|
|
93
106
|
const ok = await dispatch("GET", "/app/pages/home");
|
|
94
107
|
assert.equal(ok.status, 200);
|
|
@@ -427,6 +427,27 @@ function renderText(node) {
|
|
|
427
427
|
return el(v === "heading" ? "h1" : "p", { class: cls }, node.props.text || "");
|
|
428
428
|
}
|
|
429
429
|
|
|
430
|
+
// A grid td cell. When the column declares linkField, the cell text (the
|
|
431
|
+
// column's own value) becomes a link to the URL held in that other field,
|
|
432
|
+
// opened in a new tab. Only http(s) hrefs are linked — anything else (e.g. a
|
|
433
|
+
// javascript: URL smuggled through row data) falls back to plain text — and
|
|
434
|
+
// external links get rel=noopener noreferrer so the opened page can't reach
|
|
435
|
+
// window.opener. Shared by the top-level grid and child grids.
|
|
436
|
+
function gridCell(col, row) {
|
|
437
|
+
const text = row[col.field] == null ? "" : String(row[col.field]);
|
|
438
|
+
if (col.linkField) {
|
|
439
|
+
const href = row[col.linkField] == null ? "" : String(row[col.linkField]);
|
|
440
|
+
if (text !== "" && /^https?:\/\//i.test(href)) {
|
|
441
|
+
return el(
|
|
442
|
+
"td",
|
|
443
|
+
{},
|
|
444
|
+
el("a", { class: "pc-link", href, target: "_blank", rel: "noopener noreferrer" }, text),
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return el("td", {}, text);
|
|
449
|
+
}
|
|
450
|
+
|
|
430
451
|
function renderActionForm(node) {
|
|
431
452
|
const p = node.props;
|
|
432
453
|
const card = el("section", { class: "pc-card" });
|
|
@@ -587,7 +608,7 @@ function renderDataGrid(node) {
|
|
|
587
608
|
cbody.append(el("tr", {}, el("td", { colspan: cspan }, "None")));
|
|
588
609
|
}
|
|
589
610
|
for (const cr of rows) {
|
|
590
|
-
const cells = ccols.map((c) =>
|
|
611
|
+
const cells = ccols.map((c) => gridCell(c, cr));
|
|
591
612
|
if (cg.lazyField) {
|
|
592
613
|
const lf = cg.lazyField;
|
|
593
614
|
const has = cr[lf.field] != null && String(cr[lf.field]).trim() !== "";
|
|
@@ -641,7 +662,7 @@ function renderDataGrid(node) {
|
|
|
641
662
|
}
|
|
642
663
|
|
|
643
664
|
function renderRow(row) {
|
|
644
|
-
const cells = cols.map((c) =>
|
|
665
|
+
const cells = cols.map((c) => gridCell(c, row));
|
|
645
666
|
let toggle = null;
|
|
646
667
|
if (hasExtra) {
|
|
647
668
|
const actionCell = el("td", { class: "pc-row-actions" });
|