Repository navigation
Commit 1ccb5ba
fix(layout): a present navigation label renders verbatim, with no translate-if-equal-to-name exception, and an inline locale-map label renders its text (objectui#11201) (#11297)
Fixes #11201
Clause-②: no
Stage 2 of ruling B: the director's ruling `5921167691`, on the ruling
record `5910404448` and its execution note `5910537969`. Claim
`5921933038`, branch `claude/issue-11201-nav-label-verbatim`. Stage 1
landed as PR #11225 (`cb2f6fb5b`).
## What changes
`resolveNavItemLabel` in `packages/layout/src/NavigationRenderer.tsx`
now follows the spec's one order and keeps no rule of its own:
1. The id-keyed bundle entry `apps.APP.navigation.ID.label`.
`translateApp` applies it upstream, at `/meta`. Untouched here.
2. A present label: an inline locale map reads its locale's entry, and a
string renders verbatim.
3. An absent label inherits the target's current, localized label
(objectui#9868, unchanged).
- **Deleted:** the `isCustomized` arm and its comment. The arm looked a
present plain-string label up through the object, view or dashboard
resolver when its text equalled the target's machine name (trimmed,
case-insensitive). Nothing on `label` matches text against a name now.
- **Added:** a present map-valued entry label (`{ en: 'Accounts',
'zh-CN': '客户' }`, which the spec accepts) now reads through the spec's
`resolveI18nLabel`. Areas and the designer canvas already use that
resolver. The label used to reach the keyed resolver and render empty.
- **Kept, but not read:** `resolveObjectLabel`, `resolveDashboardLabel`
and `resolveViewLabel` (the props, and the matching positional
arguments). They are marked `@deprecated`, so callers compile and get
the ruled behaviour. Retiring them is open question 2 in the report.
- `packages/i18n/src/useObjectLabel.ts`: the one comment that named the
guard now says the guard is gone. Comment only.
- Changeset: `@object-ui/layout` patch. It states the visible effect and
the one-line fix: clear the entry's label so it inherits, or set the
wanted text. No conversion, no notice run.
## Mechanism finding: which locale a map label reads
The ruling's map pin says "renders the locale's value". This layer is
not given a locale. `NavigationRenderer` has no locale prop, and
`@object-ui/layout` has no i18n dependency by design (see the docblock
of `resolveAreaLabel` in `AppSchemaRenderer.tsx`). Within the ruled
parameters (patch, `Clause-②: no`, no public prop change), the map
resolves at the spec resolver's documented no-locale default: its `en`
entry, else `default`, else any entry. The area switcher in this package
does the same. So in the console, a zh-CN viewer sees the `en` entry of
a map-valued nav label.
Reaching the viewer's locale needs a new optional prop, with the locale
threaded from the console (`UnifiedSidebar` already holds `language`).
That widens the public surface, so it is not built here. It is open
question 1 in the report.
Measured pull: no app navigation in readable sources writes a map-valued
entry label. This is the objectstack pointer `5913605784`, re-read at
objectstack `5f6b63a6f`: in the files that declare `navigation`, the
only object-valued labels are in translation bundles.
## Pins
New file
`packages/layout/src/__tests__/NavigationRenderer.labelVerbatim-11201.test.tsx`:
- **VERBATIM, 4 rows.** A present label equal to its target's machine
name (object `account`, view `board`, dashboard `sales_overview`)
renders verbatim under `en` and under `zh-CN`. The console's convention
resolvers are wired as `UnifiedSidebar` wires them, and none is called.
Case and padding variants (`ACCOUNT`, `SALES_OVERVIEW`) stay as written.
Control in the same tree: an absent label inherits the target's
localized label in each locale, which shows the wiring is live.
- **MAP, 2 rows.** Map-valued object, dashboard, group and action labels
render the map's text. They never render `[object Object]`, empty text
or the target's label. Search finds them by that text.
Restated rows in `NavigationRenderer.navLabelOwnership.test.tsx` (they
pinned the retired convention):
- `still resolves an un-customized object label through
resolveObjectLabel` is now `renders a present object label equal to the
machine name verbatim, resolveObjectLabel unasked (objectui#11201)`.
- `keeps the same guard on the dashboard branch` is now `renders both
present dashboard labels verbatim, resolveDashboardLabel unasked
(objectui#11201)`.
- `still lets an authored object label win over resolveObjectLabel`: the
assertion is unchanged. Its comment no longer names the guard.
Unchanged and green:
`NavigationRenderer.labelInheritsTarget-9868.test.tsx` (absent labels
inherit, and an explicit label equal to the machine name stays verbatim
with echo resolvers), and the stage-1 pin
`useNavigationSync.standardEntryLocalizes-11201.test.tsx`.
## Ablation
Run from committed `54656bd7d` with objectstack's
`scripts/ablation-replace.mjs`. The layout tests import the renderer by
relative path, and the root vitest config aliases `@object-ui/layout` to
`src`, so no `dist/` is in the path.
- **Leg A: restore the `isCustomized` arm.** Over
`packages/layout/src/`: 6 failed, 287 passed. The 6 are exactly the 4
new VERBATIM rows and the 2 restated rows. The MAP rows stay green.
- **Leg B: drop the map resolution,** so every object label goes back
through the keyed resolver. Over `packages/layout/src/`: 2 failed, 291
passed. The 2 are exactly the MAP rows.
- Each leg: the anchor hit once and then zero times, and the blob
changed. The restore was proven: blob equals `HEAD`, and `git diff HEAD`
is empty.
## Verification
All gates ran from the worktree root, on head `54656bd7d`:
- `pnpm exec vitest run --reporter=verbose packages/layout/src/`: 30
files, 293 passed.
- Consumers outside layout (every test file that imports
`NavigationRenderer` or `resolveNavItemLabel`, plus
`packages/app-shell/src/layout/`, `packages/app-shell/src/chrome/`, the
`nav:menu` render tests, the Studio and designer nav-label pins,
`packages/plugin-designer/` and `packages/i18n/src/`): 173 files, 1966
passed, 13 skipped.
- Closure build `turbo run build --filter=@object-ui/layout^...
--filter=@object-ui/i18n^... --concurrency=2`: 8 of 8 successful. Then
`type-check` for `@object-ui/layout` and `@object-ui/i18n`: exit 0. `tsc
-p tsconfig.test.json --listFilesOnly` lists both touched layout test
files.
- `eslint` on the 4 changed source and test files: 0 errors and 28
warnings, all pre-existing. Linting the base blobs gives
`NavigationRenderer.tsx` 25 warnings against 23 at head (two `as any`
casts went with the arm), and the other files the same counts.
- `pnpm exec vitest run scripts/__tests__/` (the whole root suite,
because a changeset `.md` is added): 177 files passed and 2 skipped;
5371 tests passed and 2 skipped; lock VERDICT command-exit 0.
- `check-changeset-presence` exit 0. `check:control-bytes` exit 0.
`check:new-line-citations` reports 0 new citations, exit 0.
`markdown-test-inputs --audit` exit 0. `check-changeset-no-major` exit
0. `check:changeset-claims` is report-only: it names 3 pending
changesets that cite a touched file, and I read each paragraph; none is
falsified by this diff.
## Acceptance notes
- **Fork clause: not triggered.** No objectui producer on `main` writes
a machine-name label. The console sync effect passes no label. The
Studio skeleton and inspector bind and the wizard's
`generatedEntryLabel` were fixed by stage 1. Remaining `label:
d.name`-shaped writes are the Studio rail's object list and a dashboard
document seed, not nav entries.
- **Declared-type gap, measured, reported (not filed):**
`@object-ui/types` declares a nav entry's `label` as `string` (the type
and the zod mirror), narrower than the spec's `I18nLabel`.
`safeValidateSchema`, which `objectui validate` calls, refuses a
map-valued nav label (`navigation[0].label`: expected string, received
object). The spec's `NavigationItemSchema` accepts the same entry. The
new map pin casts its fixtures for that reason, and says so.
- The header comment of
`packages/app-shell/src/views/nav-menu-renderer.tsx` still says its
labels get "the same convention-based object / view / dashboard i18n
resolution the sidebar gets". That is no longer true for any label.
`UnifiedSidebar` and `nav-menu-renderer` still build the three unread
resolvers. Both files are outside this claim's file surface; they belong
with open question 2.
- `AppSchemaRenderer.tsx`'s `resolveAreaLabel` docblock quotes the old
`resolveObjectLabel` prop doc ("enables convention-based i18n
auto-resolution …"), which this PR rewrote. The docblock's point,
injection over an i18n dependency, still stands.
- `resolveNavItemLabel` still resolves objectui's keyed `{ key,
defaultValue }` reference through `t`, unchanged. The spec's nav label
refuses that shape and objectui's type does not declare it. The new code
tells it apart from a locale map by the two member names a map can never
carry.
---
_Generated by [Claude
Code](https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent d79f525 commit 1ccb5ba
5 files changed
Lines changed: 352 additions & 111 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
376 | 376 | | |
377 | 377 | | |
378 | 378 | | |
379 | | - | |
380 | | - | |
381 | | - | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
382 | 384 | | |
383 | 385 | | |
384 | 386 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
69 | 74 | | |
70 | 75 | | |
71 | 76 | | |
| |||
219 | 224 | | |
220 | 225 | | |
221 | 226 | | |
222 | | - | |
223 | | - | |
224 | | - | |
225 | | - | |
226 | | - | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
227 | 233 | | |
228 | 234 | | |
229 | 235 | | |
230 | 236 | | |
231 | | - | |
232 | | - | |
233 | | - | |
234 | | - | |
235 | | - | |
| 237 | + | |
| 238 | + | |
236 | 239 | | |
237 | 240 | | |
238 | 241 | | |
239 | 242 | | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
250 | 247 | | |
251 | 248 | | |
252 | 249 | | |
| |||
261 | 258 | | |
262 | 259 | | |
263 | 260 | | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
268 | 267 | | |
269 | 268 | | |
270 | | - | |
271 | | - | |
272 | | - | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
273 | 274 | | |
274 | 275 | | |
275 | 276 | | |
| |||
329 | 330 | | |
330 | 331 | | |
331 | 332 | | |
332 | | - | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
337 | | - | |
338 | | - | |
339 | | - | |
340 | | - | |
| 333 | + | |
| 334 | + | |
341 | 335 | | |
342 | | - | |
343 | | - | |
344 | | - | |
345 | | - | |
346 | | - | |
347 | | - | |
348 | | - | |
349 | | - | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
350 | 350 | | |
351 | | - | |
352 | | - | |
353 | | - | |
354 | | - | |
355 | | - | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
356 | 359 | | |
357 | 360 | | |
358 | 361 | | |
| |||
363 | 366 | | |
364 | 367 | | |
365 | 368 | | |
366 | | - | |
367 | | - | |
368 | | - | |
369 | | - | |
370 | | - | |
371 | | - | |
372 | | - | |
373 | 369 | | |
374 | 370 | | |
375 | 371 | | |
376 | | - | |
| 372 | + | |
377 | 373 | | |
378 | | - | |
379 | | - | |
| 374 | + | |
| 375 | + | |
380 | 376 | | |
381 | 377 | | |
382 | 378 | | |
383 | 379 | | |
384 | | - | |
385 | | - | |
| 380 | + | |
386 | 381 | | |
387 | | - | |
388 | | - | |
389 | | - | |
390 | | - | |
391 | | - | |
392 | | - | |
393 | | - | |
394 | | - | |
395 | | - | |
396 | | - | |
397 | | - | |
398 | | - | |
399 | | - | |
400 | | - | |
401 | | - | |
402 | | - | |
403 | | - | |
404 | | - | |
405 | | - | |
406 | | - | |
407 | | - | |
408 | | - | |
409 | | - | |
410 | | - | |
411 | | - | |
412 | | - | |
413 | | - | |
414 | | - | |
415 | | - | |
416 | | - | |
417 | | - | |
418 | | - | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
419 | 416 | | |
420 | 417 | | |
421 | 418 | | |
| |||
1360 | 1357 | | |
1361 | 1358 | | |
1362 | 1359 | | |
1363 | | - | |
| 1360 | + | |
| 1361 | + | |
1364 | 1362 | | |
1365 | 1363 | | |
1366 | 1364 | | |
| |||
0 commit comments