Skip to content

Object-list toolbar's "New" (and its mobile FAB) does not consume createPredicates — the create half is honoured on related lists only #5153

Description

@yinlianghui

Summary

Found while implementing #5142 (the object-list toolbar's Import button consuming importPredicates).

createPredicates has exactly one consumer in objectui: RelatedRecordActionsBridge (PR for #4646). That closed the related-list toolbar. The standalone object-list page has two more create entry points, and neither reads the key:

packages/app-shell/src/views/ObjectView.tsx:2129   header "New" button      (b4089beca)
packages/app-shell/src/views/ObjectView.tsx:2194   phone-only floating "+"  (b4089beca)

Both gate on affordances.create && can(objectDef.name, 'create') — the object-level verdict only. affordances.createPredicates is right there on the same resolved object and is dropped.

Why the standalone list is in scope for a toolbar predicate

The spec does not treat "standalone list" as a surface where toolbar predicates are inapplicable — it states the binding for it explicitly (RowCrudActionOverrideSchema docblock, @objectstack/spec@17.0.0):

> Toolbar affordances (create, import) have no row to bind … They evaluate once per toolbar against the record in scope where the toolbar renders: on a record page's related list that is the host (parent) record. On a standalone object list there is no record in scope, so a predicate reading record.* has nothing to bind and — per the fail-closed rule above — hides the button.

So the surface is in scope; what is absent there is only the record. Predicates over the host scope (os.user.*, features.*) bind normally and are the meaningful shape, and a record.* one is supposed to fail closed. Today the object-list page does neither: it ignores the envelope entirely.

Impact

An author writing one userActions.create object form gets two different verdicts for the same declaration, depending on which surface renders the button: honoured on a record page's related list, ignored on the object list page. visibleWhen: false — the objectui#3492 shape — does not hide the object list's "New".

Not reported from the field. Severity judged at filing time is unreliable in both directions (objectui#4646 came from a downstream project, #5142's twin did not), so this is filed plainly for triage rather than graded here.

Deliberately NOT done in the PR for #5142

That PR is scoped to the import key: it adds the consumer for importPredicates at ObjectView's single Import entry and widens UserActionsOverride.import to match. Extending it to create would have changed a second affordance on the same surface under an issue that names only one, so the create half is recorded here instead. The consumption shape it would mirror is already in the same file after that PR (importVisible / importDisabled, useRowPredicate with fallback: false, ?? true for visibleWhen, != null for disabledWhen), and UserActionsOverride.create is already the widened union — so this is the consumer half only, no type change needed.

Note the mobile FAB is a second render point of the same affordance, so both need the gate or they disagree with each other. (Import has no mobile counterpart by design — CSV import is desktop-only there — which is why #5142 had exactly one entry to close.)

Related

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpm:queue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions