Add field configuration option to provide editor#5106
Conversation
A field declaration can supply its own edit component via the `edit:` option, overriding the FieldDef's `static edit` for that specific usage. * Atomic fields (contains / linksTo): the override resolves at the field-component layer and replaces the standard edit component. The dispatch is gated on fieldType so the field descriptor inherited by iterated items inside a containsMany doesn't accidentally hijack each item's template. * containsMany: the override receives `@model` (containing card), `@values` (the array), and `@defaultEditor` — a pre-bound ContainsManyEditor — so the override can wrap (rather than replace) the standard iteration / add / remove / sortable UI.
The override branch in field-component sees the same field descriptor for both the outer collection render and each iterated item inside containsMany. Without a gate it would replace each item's edit template with the collection-level override component, blanking every row. Restrict it to atomic field types; collection fields go through their own wrap-style handler. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* linksTo: when `linksToField.edit` is set, render that component in place of the default LinksToEditor at the atomic site. * linksToMany: mirror the containsMany wrap contract — the override receives `@model` (containing card), `@values` (the linksToMany array), and a pre-bound default LinksToManyEditor as `@defaultEditor` so it can wrap (rather than replace) the standard iteration / add / remove UI.
One test per field type, asserting: * contains / linksTo (atomic): the override replaces the default edit component for the specific usage; the default editor is suppressed. * containsMany / linksToMany (wrap): the override is invoked with a pre-bound `@defaultEditor`, and rendering it produces the standard editor so the wrap doesn't have to reimplement iteration / add / remove UI.
Preview deploymentsHost Test Results 1 files ±0 1 suites ±0 1h 51m 15s ⏱️ - 2m 1s Results for commit 0a8201a. ± Comparison against earlier commit ffe5cc0. Realm Server Test Results 1 files ± 0 1 suites ±0 8m 20s ⏱️ - 4m 53s Results for commit 0a8201a. ± Comparison against earlier commit ffe5cc0. |
`ember/no-empty-glimmer-component-classes` flags GlimmerComponent subclasses whose only member is a `<template>`; switching the test helpers to `TemplateOnlyComponent` removes the empty backing class and collapses a couple of prettier-flagged call sites onto single lines.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f3855b0b6d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The view-slot dedup picks a shared component reference when a
FieldDef intentionally aliases its embedded / isolated slot to its
`static edit`, so toggling between formats keeps the same component
mounted. Gating the per-usage override on `!viewSlot` made the
override silently lose to that dedup, contradicting the documented
contract that `{ edit: ... }` replaces the FieldDef's `static edit`
for that usage. Drop the gate so the override always wins in edit
format; toggling embedded↔edit will remount as a result, which is
correct — the consumer opted into a different component.
The standard LinksToManyEditor branch already receives `...attributes`, so a caller writing `<@fields.foo class='whatever' data-x='y' />` had those forwarded to the editor. The override branch dropped them, regressing the contract for that field type. Splat them onto the override too (consumers can choose whether to forward them through to `@defaultEditor`).
…age-edit-option-to-field-declarations
There was a problem hiding this comment.
Pull request overview
Adds a per-field-declaration edit option that lets card authors override a field’s edit component at the usage site (without subclassing the FieldDef), including a “wrap the default editor” contract for collection fields to support richer realm-config editing UIs.
Changes:
- Extends field options/descriptor shape (
card-api.gts) to carry an optionaleditoverride component. - Updates field rendering to prefer the per-usage
editoverride in edit format, with special handling forcontainsMany/linksToManyoverrides via@defaultEditor. - Adds integration tests covering atomic and collection override behavior (including
@defaultEditorwrapping and attribute passthrough forlinksToMany).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/base/card-api.gts | Adds edit to field options/descriptor and wires it into field factory helpers + linksTo edit rendering. |
| packages/base/field-component.gts | Ensures per-usage edit wins for atomic fields in edit format, without breaking collection item rendering. |
| packages/base/contains-many-component.gts | Adds per-usage edit override support for containsMany via a wrap-style @defaultEditor contract. |
| packages/base/links-to-many-component.gts | Adds per-usage edit override support for linksToMany via a wrap-style @defaultEditor contract and forwards attributes. |
| packages/host/tests/integration/components/per-usage-edit-override-test.gts | New integration coverage for atomic + collection per-usage edit overrides, including @defaultEditor wrapping. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Mirrors the linksToMany fix: the override is the contract a
consumer reaches when supplying `{ edit: ... }`, so it should pass
through `<@fields.foo class='x' data-y='z' />`-style attributes.
The non-edit branch already splats them onto the container, and
the linksToMany override now does too; making containsMany behave
the same way keeps the override contract consistent across field
types. The standard ContainsManyEditor branch is unchanged
(broader cross-format contract change, out of scope here).
This is an outgrowth of #4973, where we want to provide a custom editor for the collection of host routing rules to support a rich realm config editor:
In that context it’s used like this: