fix(client): give the portal row an icon, and assert every gated nav has one
All checks were successful
PR Checks / server-tests (pull_request) Successful in 19s
PR Checks / client-build (pull_request) Successful in 8m49s

The §7.7 smoke, running the pair together: registering the player row without an
`icon` blanked the whole portal with React error #130, because core's
PlayerPortalLayout rendered `<n.icon />` unguarded. Core is guarded now
(website), and this is the other half — the row had an icon before it moved and
should have kept one.

A second glyph rather than reusing IconShard: these two rows sit in different
navs and each matched its neighbours before the extraction. The admin sidebar's
UO rows were gems; the portal's Characters row was a person beside Appeals'
shield and Account's gear. Matching the nav a row lands in is the whole reason
`icon` is in the contract.

`registration.test.js` now asserts it for admin AND player rows, which is the
cheap place to catch the next one. The public header is text buttons and is
deliberately excluded.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 18:54:42 -05:00
parent e4af7dd9a8
commit 9d559091c5
3 changed files with 45 additions and 5 deletions

View File

@@ -18,7 +18,7 @@
// size and it is the one thing this seam cannot have.
import { registry, coreApiVersion } from './core.js'
import { IconShard } from './icons.jsx'
import { IconShard, IconUser } from './icons.jsx'
import { useShardFlags } from './lib/useShardFeatures.js'
// Public pages — the twelve that used to live at /site/*.
@@ -159,7 +159,7 @@ registry.registerNav(ID, {
area: 'player',
// Order 0: Characters is the portal's first row today, and with the module
// installed it is also what core's `/player` index resolves to.
items: [{ label: 'Characters', to: '/player/uo/characters', order: 0 }],
items: [{ label: 'Characters', to: '/player/uo/characters', icon: IconUser, order: 0 }],
})
// ── Feature provider ───────────────────────────────────────────────────────

View File

@@ -35,4 +35,32 @@ export const IconShard = () => (
</Icon>
)
/**
* A figure — the glyph core used for the portal's "Characters" row.
*
* A second icon rather than reusing IconShard, because these two rows sit in
* different navs and each matched its neighbours before the extraction: the
* admin sidebar's UO rows were all gems, and the portal's Characters row was a
* person beside Appeals' shield and Account's gear. Copied from core's
* PlayerPortalLayout, which uses a 16px frame and a heavier stroke than the
* admin one — matching the nav a row lands in is the whole reason `icon` exists.
*/
export const IconUser = () => (
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
focusable="false"
>
<circle cx="12" cy="8" r="4" />
<path d="M4 21a8 8 0 0 1 16 0" />
</svg>
)
export default IconShard