feat(app-shell): wire navigation action items to the console action runtime (framework#4509) - #3180
Merged
Merged
Conversation
…untime (framework#4509)
A `type: 'action'` nav item rendered, gated like any other item, and did nothing
when clicked. NavigationRenderer dispatches such a click to an `onAction` prop it
expects the host to supply — it deliberately never reads `item.actionDef` itself
— and no shipped sidebar supplied it. So `actionDef.actionName` reached no
dispatcher: an author could put an action in the menu, watch it render with icon
and label, and never learn that clicking it was a no-op. The framework's liveness
ledger recorded this as the single gap in the AppSchema navigation surface.
`useNavActionDispatch` resolves the nav item's actionName against `action`
metadata at click time — the same source DeclaredActionsBar reads for a record
toolbar — and dispatches the resolved def through useAction(). UnifiedSidebar
passes it. No new provider: the sidebar already renders inside ConsoleShell's
GlobalActionRuntimeProvider, so nav actions get the fully-wired console runner
with its confirm/param/result/navigate dialogs. A declared `params` array becomes
the runner's param-dialog input; the nav item's own `actionDef.params` rides as
the value bag, so a menu entry can pre-fill the action it launches.
Nav actions are inherently global — ActionNavItemSchema is strict with exactly
{ actionName, params? } and carries no objectName — so resolution is by name and
no record context rides along.
Behaviour change: a shell passing no `onAction` no longer renders action items at
all, rather than rendering them dead. This mirrors the existing capability guards
and makes the omission diagnosable — a missing prop now reads as "my item is
gone", which leads to the prop, instead of "clicking does nothing", which for
three releases led nowhere. Every dispatch-time failure warns and toasts instead
of returning silently, since silence is the bug being fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5CYr5SDwe85gH2Jr5KSgu
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
…nment (#4509) `let def = null` is never read — both the try and the catch either assign or return — so ESLint's no-useless-assignment failed the Lint job. Declare without the initializer. Also removes an unused React import from the new hook test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5CYr5SDwe85gH2Jr5KSgu
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes item 4 of objectstack-ai/objectstack#4509.
Problem
A
type: 'action'navigation item rendered, gated like any other item (permissions, capabilities, visibility) — and did nothing when clicked.NavigationRendererdispatches an action click to anonActionprop it expects the host shell to supply; it deliberately never readsitem.actionDefitself. No shipped sidebar supplied that prop. SoactionDef.actionNamereached no dispatcher: an author could put an action in the menu, watch it render with its icon and label, and never find out that clicking it was a no-op. The framework liveness ledger recorded this as the single gap in the AppSchema navigation surface (app.jsontype note, seeded by the #4488 audit).What changed
useNavActionDispatch(@object-ui/app-shell) — resolves the nav item'sactionNameagainstactionmetadata at click time (the same sourceDeclaredActionsBarreads for a record toolbar) and dispatches the resolved definition throughuseAction().UnifiedSidebarpasses it. No new provider: the sidebar already renders insideConsoleShell'sGlobalActionRuntimeProvider, so nav actions get the fully-wired console runner — api/flow/script handlers plus the confirm, param-collection, result and navigate dialogs.paramsarray becomes the runner's param-dialog input (actionParams); the nav item's ownactionDef.paramsrides as the value bag, so a menu entry can pre-fill the action it launches.NavigationRendererhides action items when noonActionis passed, rather than rendering them dead.Nav actions are inherently global:
ActionNavItemSchemais strict with exactly{ actionName, params? }and carries noobjectName, so resolution is by name alone and no record context rides along.AppSidebaris left untouched — it has no JSX call site anywhere in the repo (superseded byUnifiedSidebarviaConsoleLayout).Behaviour change worth a reviewer's attention
Hiding rather than disabling was a deliberate call. It mirrors the existing capability guards (an item the host cannot serve is hidden), and it makes the omission diagnosable: a missing prop now shows up as "my action item is gone", which leads to the prop, instead of "clicking does nothing", which for three releases led nowhere. Every dispatch-time failure (unnamed item, unresolvable action, throwing action) warns and toasts instead of returning silently — silence is the bug being fixed.
Verification
NavigationRenderer.actions.test.tsx(3): the whole item reachesonActionso the host can readactionDef; action items are not rendered without a handler; the rest of the tree still renders.useNavActionDispatch.test.tsx(7): resolution by name,paramsarray →actionParams, navactionDef.params→ value bag, and the four loud-failure paths.Generated by Claude Code