fix(ui): unify heading help, table sizing and navigation contracts
Verified with the coordinated workspace changes by devkit full run 2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed). This shared UI pass does not mark the individual module reviews complete.
This commit is contained in:
Executable
+112
@@ -0,0 +1,112 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import test from "node:test";
|
||||
import vm from "node:vm";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { transformSync } = require("esbuild");
|
||||
const React = require("react");
|
||||
const { renderToStaticMarkup } = require("react-dom/server");
|
||||
const { MemoryRouter } = require("react-router");
|
||||
|
||||
function loadSource(path, bindings = {}) {
|
||||
const context = vm.createContext({ module: { exports: {} }, require, ...bindings });
|
||||
context.exports = context.module.exports;
|
||||
vm.runInContext(transformSync(readFileSync(new URL(path, import.meta.url), "utf8"), {
|
||||
loader: path.endsWith(".tsx") ? "tsx" : "ts", format: "cjs", jsx: "automatic"
|
||||
}).code, context);
|
||||
return { exports: context.module.exports, context };
|
||||
}
|
||||
|
||||
const { generatedTranslations } = loadSource("../src/i18n/generatedTranslations.ts").exports;
|
||||
const launch = loadSource("../src/platform/launchContext.ts").exports;
|
||||
|
||||
function harness(locale = "en") {
|
||||
const navigation = [];
|
||||
const translateText = (value) => {
|
||||
if (typeof value === "string") return generatedTranslations[locale][value] ?? value;
|
||||
let text = generatedTranslations[locale][value.key] ?? value.key;
|
||||
for (const [key, replacement] of Object.entries(value.values)) {
|
||||
text = text.replaceAll(`{${key}}`, replacement);
|
||||
}
|
||||
return text;
|
||||
};
|
||||
const loaded = loadSource("../src/layout/BreadcrumbBar.tsx", {
|
||||
window: { history: { state: { idx: 7 } } },
|
||||
require: (name) => {
|
||||
if (name === "../i18n/LanguageContext") return {
|
||||
usePlatformLanguage: () => ({ translateText }),
|
||||
i18nMessage: (key, values) => ({ key, values })
|
||||
};
|
||||
if (name === "../components/UnsavedChangesGuard") return {
|
||||
useGuardedNavigate: () => (...args) => navigation.push(args)
|
||||
};
|
||||
if (name === "../platform/launchContext") return launch;
|
||||
return require(name);
|
||||
}
|
||||
});
|
||||
const BreadcrumbBar = loaded.exports.default;
|
||||
return {
|
||||
...loaded, navigation, BreadcrumbBar,
|
||||
links(pathname, search = "") {
|
||||
const html = renderToStaticMarkup(React.createElement(
|
||||
MemoryRouter, { initialEntries: [`${pathname}${search}`] },
|
||||
React.createElement(BreadcrumbBar, { pathname })
|
||||
));
|
||||
return [...html.matchAll(/<a\b([^>]*)>(.*?)<\/a>/g)].map(([, attributes, label]) => ({
|
||||
href: /href="([^"]*)"/.exec(attributes)?.[1], label: label.replaceAll("&", "&")
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test("module-global reports and operator queue have their own bilingual labels and exact links", () => {
|
||||
for (const locale of ["en", "de"]) {
|
||||
const h = harness(locale);
|
||||
const root = generatedTranslations[locale]["i18n:govoplan-core.campaigns.01a23a28"];
|
||||
for (const [route, key] of [
|
||||
["reports", "i18n:govoplan-core.reports.88bc3fe3"],
|
||||
["queue", "i18n:govoplan-core.operator_queue.72492fb5"]
|
||||
]) {
|
||||
for (const trailingSlash of ["", "/"]) {
|
||||
assert.deepEqual(h.links(`/campaigns/${route}${trailingSlash}`, "?campaign=selected-campaign"), [
|
||||
{ href: "/campaigns", label: root },
|
||||
{ href: `/campaigns/${route}`, label: generatedTranslations[locale][key] }
|
||||
]);
|
||||
}
|
||||
}
|
||||
assert.equal(h.links("/operator")[0].label, generatedTranslations[locale]["i18n:govoplan-core.operator_queue.72492fb5"]);
|
||||
}
|
||||
});
|
||||
|
||||
test("campaign editor report aliases and attachment deep links retain their singular campaign context", () => {
|
||||
const h = harness();
|
||||
const campaignId = "8ef65230-94d1-49a5-93a9-c5ef7c87ad45";
|
||||
for (const suffix of ["report", "reports", "attachments"]) {
|
||||
const links = h.links(`/campaigns/${campaignId}/${suffix}`);
|
||||
assert.deepEqual(links.map((item) => item.href), ["/campaigns", `/campaigns/${campaignId}`, `/campaigns/${campaignId}/${suffix}`]);
|
||||
assert.equal(links[1].label, generatedTranslations.en["i18n:govoplan-core.campaign.69390e16"]);
|
||||
assert.equal(links[2].label, generatedTranslations.en[suffix === "attachments"
|
||||
? "i18n:govoplan-core.attachments.6771ade6" : "i18n:govoplan-core.report.ee45c303"]);
|
||||
}
|
||||
});
|
||||
|
||||
test("quick-access return preserves guarded history back or the exact origin deep link", () => {
|
||||
const origin = launch.createQuickAccessLaunchContext({
|
||||
pathname: "/cases/case-fixture", search: "?tab=work", hash: "#campaigns", historyIndex: 6,
|
||||
auth: { user: { account_id: "account-fixture" }, tenant: { id: "tenant-fixture" } },
|
||||
temporalContext: { validityMode: "current" }
|
||||
});
|
||||
const h = harness();
|
||||
for (const historyIndex of [7, 20]) {
|
||||
h.context.window.history.state.idx = historyIndex;
|
||||
const tree = h.BreadcrumbBar({ pathname: "/campaigns/reports", locationState: launch.quickAccessLaunchState(origin) });
|
||||
const button = React.Children.toArray(tree.props.children).find((item) => item.type === "button");
|
||||
assert.ok(button, "the launch return action remains visible");
|
||||
button.props.onClick();
|
||||
}
|
||||
assert.deepEqual(h.navigation[0], [-1]);
|
||||
assert.equal(h.navigation[1][0], "/cases/case-fixture?tab=work#campaigns");
|
||||
assert.equal(h.navigation[1][1].replace, true);
|
||||
});
|
||||
Reference in New Issue
Block a user