Skip to content

finding(react,fields): SchemaRendererContext has no data and no formValues member, so the tail of dependentValues ?? ctx.formValues ?? ctx.data is unconditionally empty in production — and three comments describe it as live #7206

Description

@os-warren

What was measured (on dc7a48d02, branch claude/issue-7190-inline-field-input-dependson)

Two widgets in @object-ui/fields resolve the record a cascade gates on through the same three-step chain:

dependentValues ?? ctx.formValues ?? ctx.data ?? {}
  • packages/fields/src/widgets/LookupField.tsx:352 — dependent lookups (dependsOn).
  • packages/fields/src/widgets/useCascadingOptions.ts:38 — the option cascades behind SelectField, RadioField, CheckboxesField and MultiSelectField.

ctx cannot ever carry either of the last two.

  • SchemaRendererContextType (packages/react/src/context/SchemaRendererContext.tsx) declares exactly four members: dataSource, debug, debugFlags, apiFetch. There is no data and no formValues, and SchemaRendererProvider accepts no such prop.

  • The repository contains exactly one non-test provider of that context — the definition itself, at line 46 of that same file, whose value is { dataSource, debug, debugFlags, apiFetch }.

    Census command, and its control, because a "one result" reading is worthless without one:

    grep -rn "SchemaRendererContext.Provider" packages/ examples/ | grep -v "__tests__\|\.test\."   # 2 lines, both the definition's open+close tag
    grep -rn "SchemaRendererContext.Provider" packages/ examples/ | wc -l                            # 96 — the instrument is live
    

⇒ In production, the tail of that chain is unconditionally the empty object. Every surface that does not pass an explicit dependentValues prop gets {} — which for a dependsOn lookup means a permanently gated picker, and for a cascading option list means an unscoped one.

Why this is worth a card rather than a comment: three places describe the tail as live

The dead tail is not merely unused, it is documented as a working channel, which is what makes it mislead readers into treating a host as the supplier:

  1. LookupField.tsx — its own docblock calls ctx.data the "record scope" channel.
  2. packages/fields/src/widgets/toHostProps.ts:68 — "the explicit prop wins, then ctx.formValues, then …".
  3. packages/app-shell/src/views/ActionParamDialog.tsx:330 — the strongest one: "the dialog passed nothing, so useCascadingOptions fell through to SchemaRendererContext's formValues / data — the OUTER page's record". There is no outer page's record in that context; the fall-through reached {}.

This is the class objectui#7167 corrected for the grid's relational copy set: comments asserting a route that the code does not have.

Measured reach

Both known instances of "a dependsOn lookup gates forever" are instances of this root, not of two independent host bugs:

  • objectui#7165 — the grid's inline lookup column. renderCellEditor's context is { column, row, value, stage, commit, cancel }; nothing forwards row, so the chain falls to the empty tail.
  • objectui#7190 — the detail page. Measured in the real app-shell record page on this branch: gated, disabled, "Select region first", with a live control lookup enabled in the same render, and both of InlineFieldInput's call sites gating.

The fork — this is a design decision, not a mechanical repair

Three readings, and they lead to different architectures. ⛔ Not chosen here.

  • A — keep prop drilling. Every record-scoped host passes dependentValues at each call site (the grid's editor seam, the detail body, the highlights strip, and any future one). Cheapest per site; the defect class stays re-openable by the next host that forgets, exactly as it has twice.
  • B — make the context channel real. Add data to SchemaRendererContextType and have record-scoped hosts provide it, so the documented fallback becomes true and every surface is served at once. Addresses the class rather than the instances; costs a public contract change on @object-ui/react, and it must be settled whether "record scope" means the saved record or the in-flight staged one (objectui#7188 is the same question for the grid).
  • C — retire the dead tail under ADR-0049 enforce-or-remove, making dependentValues the only channel, and correct the three comments. Honest and smallest; it also removes the option of ever addressing this centrally, so it should only be chosen if B is being rejected on purpose.

A and C are not mutually exclusive; B excludes C.

Not filed as a duplicate

Searched before filing. The two dependent-lookup cards (objectui#7165, objectui#7190) are about their hosts; objectui#7188 is the grid's saved-versus-staged seam. None of them is about the context contract itself, and none proposes B or C.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:blockedpriority:p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions