Rename the admin example plugin to auth-admin, and let config/menu.ts hide or rename a profile-menu entry
CI / full-gate (push) Successful in 3m32s
CI / full-gate (push) Successful in 3m32s
This commit is contained in:
@@ -113,3 +113,18 @@ test("profile menu entries are gated like nav nodes, translated by their plugin
|
||||
assert.deepEqual(entries({ permissions: ["billing:read"] }).map((e) => e.label), ["Inställningar", "account.billing", "Help"]);
|
||||
assert.deepEqual(entries({ signedIn: false }).map((e) => e.label), []); // an anonymous visitor has no profile menu
|
||||
});
|
||||
|
||||
test("the central override hides and renames a profile-menu entry by its id, which is not rendered", () => {
|
||||
const links = buildPluginChrome({
|
||||
menu: { ...DEFAULT_MENU, override: { hide: ["help"], rename: { settings: "Preferences" } } },
|
||||
plugins: [{
|
||||
apiVersion: "1.0.0", id: "account",
|
||||
profileMenu: [
|
||||
{ href: "/account/settings", id: "settings", label: "account.settings", session: true },
|
||||
{ href: "/account/help", id: "help", label: "Help", public: true },
|
||||
],
|
||||
}],
|
||||
user: { email: "ada@x.io", id: "u1", permissions: [] },
|
||||
}).user.profileLinks;
|
||||
assert.deepEqual(links, [{ href: "/account/settings", label: "Preferences" }]);
|
||||
});
|
||||
|
||||
+13
-6
@@ -20,7 +20,9 @@ export interface ProfileLink {
|
||||
}
|
||||
|
||||
// What a plugin's `profileMenu` declares: a link plus the one gate that decides who sees it.
|
||||
export interface ProfileMenuEntry extends ProfileLink, Gate {}
|
||||
export interface ProfileMenuEntry extends ProfileLink, Gate {
|
||||
id?: string; // targets the entry from config/menu.ts's `hide` and `rename`, as on a nav node; not rendered
|
||||
}
|
||||
|
||||
// `profileLinks` rides on the user, which every view already hands to the shell, so no view needs a new local.
|
||||
export interface ShellUser {
|
||||
@@ -86,13 +88,18 @@ export function buildPluginChrome(opts: ChromeOptions): PageChrome {
|
||||
function profileLinks(opts: ChromeOptions, t: Translate, carryLocale: (href: string) => string): ProfileLink[] {
|
||||
const user = opts.user ?? null;
|
||||
if (!user) return [];
|
||||
const { hide = [], rename = {} } = opts.menu.override;
|
||||
return (opts.plugins ?? []).flatMap((plugin) => {
|
||||
const pluginT = opts.translatorFor?.(plugin.id) ?? t;
|
||||
return (plugin.profileMenu ?? []).filter((entry) => allows(entry, user)).map((entry) => ({
|
||||
href: carryLocale(entry.href),
|
||||
...(entry.icon != null ? { icon: entry.icon } : {}),
|
||||
label: pluginT(entry.label),
|
||||
}));
|
||||
const shown = (plugin.profileMenu ?? []).filter((entry) => allows(entry, user) && !(entry.id != null && hide.includes(entry.id)));
|
||||
return shown.map((entry) => {
|
||||
const renamed = entry.id != null ? rename[entry.id] : undefined;
|
||||
return {
|
||||
href: carryLocale(entry.href),
|
||||
...(entry.icon != null ? { icon: entry.icon } : {}),
|
||||
label: renamed != null ? t(renamed) : pluginT(entry.label),
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -23,9 +23,9 @@ export interface NavNode extends Gate {
|
||||
// order → hide, then the per-user gate filter runs last.
|
||||
export interface NavOverride {
|
||||
groups?: NavGroupSpec[]; // wrap top-level nodes (by id) under a new header
|
||||
hide?: string[]; // remove nodes by id, at any depth (incl. a group's id)
|
||||
hide?: string[]; // remove nodes by id, at any depth (incl. a group's id), and profile-menu entries
|
||||
order?: string[]; // reorder top-level nodes by id; unlisted keep their order, after
|
||||
rename?: Record<string, string>; // id → replacement label
|
||||
rename?: Record<string, string>; // id → replacement label, for a node or a profile-menu entry
|
||||
}
|
||||
|
||||
export interface NavGroupSpec {
|
||||
|
||||
Reference in New Issue
Block a user