Rename ShellUser.menu to profileLinks, so menu keeps one meaning in the shell
This commit was merged in pull request #220.
This commit is contained in:
@@ -608,7 +608,7 @@ interface RequestContext {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**`ctx.chrome`** — `{ brand, csrfToken, nav, signInHref, theme, user }` (`user.menu` holds the profile-dropdown links). Hand each field to the
|
**`ctx.chrome`** — `{ brand, csrfToken, nav, signInHref, theme, user }` (`user.profileLinks` holds the profile-dropdown links). Hand each field to the
|
||||||
matching `partials/shell` local and a `view` result renders the **native app shell**, exactly as
|
matching `partials/shell` local and a `view` result renders the **native app shell**, exactly as
|
||||||
`examples/plugins/scheduling/views/overview.ejs` does; a field you omit falls back to its shell
|
`examples/plugins/scheduling/views/overview.ejs` does; a field you omit falls back to its shell
|
||||||
default rather than erroring. `chrome.nav` is the whole global menu — every installed plugin's
|
default rather than erroring. `chrome.nav` is the whole global menu — every installed plugin's
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ const entries = (opts: { permissions?: string[]; signedIn?: boolean }): { href:
|
|||||||
plugins: [account],
|
plugins: [account],
|
||||||
translatorFor: () => (key) => (key === "account.settings" ? "Inställningar" : key),
|
translatorFor: () => (key) => (key === "account.settings" ? "Inställningar" : key),
|
||||||
...(opts.signedIn === false ? {} : { user: { email: "ada@x.io", id: "u1", permissions: opts.permissions ?? [] } }),
|
...(opts.signedIn === false ? {} : { user: { email: "ada@x.io", id: "u1", permissions: opts.permissions ?? [] } }),
|
||||||
}).user.menu!;
|
}).user.profileLinks!;
|
||||||
|
|
||||||
test("profile menu entries are gated like nav nodes, translated by their plugin and carry the locale", () => {
|
test("profile menu entries are gated like nav nodes, translated by their plugin and carry the locale", () => {
|
||||||
assert.deepEqual(entries({}), [
|
assert.deepEqual(entries({}), [
|
||||||
|
|||||||
+3
-3
@@ -22,11 +22,11 @@ export interface ProfileLink {
|
|||||||
// What a plugin's `profileMenu` declares: a link plus the one gate that decides who sees it.
|
// 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 {}
|
||||||
|
|
||||||
// `menu` rides on the user, which every view already hands to the shell, so no view needs a new local.
|
// `profileLinks` rides on the user, which every view already hands to the shell, so no view needs a new local.
|
||||||
export interface ShellUser {
|
export interface ShellUser {
|
||||||
email: string;
|
email: string;
|
||||||
initials: string;
|
initials: string;
|
||||||
menu?: ProfileLink[]; // gate-filtered, translated, locale-carried. The host always sets it; a hand-built fixture may omit it
|
profileLinks?: ProfileLink[]; // gate-filtered, translated, locale-carried. The host always sets it; a hand-built fixture may omit it
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ export function buildPluginChrome(opts: ChromeOptions): PageChrome {
|
|||||||
nav: carryLocaleInto(nav, carryLocale),
|
nav: carryLocaleInto(nav, carryLocale),
|
||||||
signInHref: carryLocale(returnTo),
|
signInHref: carryLocale(returnTo),
|
||||||
...(theme != null ? { theme } : {}),
|
...(theme != null ? { theme } : {}),
|
||||||
user: { ...shellUser(opts.user, t), menu: profileLinks(opts, t, carryLocale) },
|
user: { ...shellUser(opts.user, t), profileLinks: profileLinks(opts, t, carryLocale) },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ test("profile menu entries render as links above Sign out, with their icon", asy
|
|||||||
csrfToken: "tok.sig",
|
csrfToken: "tok.sig",
|
||||||
user: {
|
user: {
|
||||||
email: "ada@acme.io", initials: "AD", name: "ada",
|
email: "ada@acme.io", initials: "AD", name: "ada",
|
||||||
menu: [{ href: "/account/settings", icon: "i-user", label: "My <settings>" }, { href: "/account/help", label: "Help" }],
|
profileLinks: [{ href: "/account/settings", icon: "i-user", label: "My <settings>" }, { href: "/account/help", label: "Help" }],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const panel = html.slice(html.indexOf('id="profile-menu"'), html.indexOf("</aside>"));
|
const panel = html.slice(html.indexOf('id="profile-menu"'), html.indexOf("</aside>"));
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div id="profile-menu" class="menu-pop left up" popover style="min-width:220px">
|
<div id="profile-menu" class="menu-pop left up" popover style="min-width:220px">
|
||||||
<div class="menu-head"><%= t("shell.signedInAs", { name: user.name }) %></div>
|
<div class="menu-head"><%= t("shell.signedInAs", { name: user.name }) %></div>
|
||||||
<% (user.menu || []).forEach((link) => { %>
|
<% (user.profileLinks || []).forEach((link) => { %>
|
||||||
<a class="menu-item" href="<%= link.href %>"><% if (link.icon) { %><svg class="ico"><use href="#<%= link.icon %>" /></svg><% } %><%= link.label %></a>
|
<a class="menu-item" href="<%= link.href %>"><% if (link.icon) { %><svg class="ico"><use href="#<%= link.icon %>" /></svg><% } %><%= link.label %></a>
|
||||||
<% }) %>
|
<% }) %>
|
||||||
<%# Sign out is a state change → a POST form (not a GET link), CSRF-guarded by app.ts %>
|
<%# Sign out is a state change → a POST form (not a GET link), CSRF-guarded by app.ts %>
|
||||||
|
|||||||
Reference in New Issue
Block a user