Skip to content

feat: render broken-link placeholder per element in linksToMany#4983

Merged
habdelra merged 18 commits into
mainfrom
cs-11226-render-broken-link-placeholder-per-element-in-linkstomany
May 27, 2026
Merged

feat: render broken-link placeholder per element in linksToMany#4983
habdelra merged 18 commits into
mainfrom
cs-11226-render-broken-link-placeholder-per-element-in-linkstomany

Conversation

@habdelra
Copy link
Copy Markdown
Contributor

@habdelra habdelra commented May 27, 2026

Preview

Broken-link placeholder per element in linksToMany

A linksToMany of three pets where the middle reference is broken (synthetic 404). Each view format (fitted / embedded / atom) and the edit editor render the broken element as a per-element placeholder while the present siblings render normally. Synthetic URLs only.

Summary

The plural linksToMany view path and both link editors (standard + compact) now read getRelationship once per render and dispatch BrokenLinkTemplate per slot whose relationship is error or not-found. Sibling states keep their existing render paths:

  • error / not-found → broken-link placeholder in that slot, carrying the broken URL and error doc.
  • present → the linked card renders normally.
  • not-loaded / not-set → unchanged.

Each placeholder occupies its own .linksToMany-itemContainer, so a single broken element no longer breaks the surrounding list. The view-path placeholder adopts the list's effective format (mapped through brokenLinkFormat); the editors render it at the item footprint (fitted standard / atom compact) alongside the existing remove affordance, so a dead reference is visible and clearable rather than rendering as a broken card.

Stability

This field is sensitive to component stability — an unnecessary re-render in edit format can steal focus from an input as the user types. The change is built to avoid that:

  • The {{#each}} keying is left untouched: the view path still keys on the stable per-child component identity, the editors on their index / box identity. The broken-link state only feeds an inner {{#if}} branch.
  • getRelationship is a pure read (it never retriggers lazilyLoadLink) and is read once per render via {{#let}}; its result is a fresh array that is never used as an each key.

Net effect: per-slot identity stays stable when state hasn't changed, so a broken neighbor never tears down a focused sibling.

BrokenLinkTemplate now accepts splattributes so callers can attach per-element hooks/attributes to the placeholder root.

Stacked on #4979 (singular linksTo placeholder), reusing its BrokenLinkTemplate dispatch + brokenLinkFormat plumbing. Base retargets to main once the parent merges.

Ticket: CS-11226

Test plan

New tests/integration/components/linksto-many-broken-link-placeholder-test.gts (7/7 passing locally):

  • a mixed list (present / not-found / present) renders each element correctly — the broken slot shows the placeholder in place while both healthy siblings render their cards
  • a link-error sentinel element renders the error placeholder (fitted footprint) with its broken URL
  • a list of only present links renders no placeholder
  • the placeholder occupies the same per-element item container (fitted footprint) as a card would
  • a list with one broken element converges in ≤2 renders (initial + post-lazy-load)
  • in edit format a broken element shows the placeholder + remove affordance, and removing it leaves the healthy sibling untouched
  • editing a sibling field while a broken element is present does not tear down the present element (DOM identity preserved across the re-render) — the focus-stability guard

Regression: the full links-filtered host suite is green locally (221/221), covering operator-mode | links, the linksTo/linksToMany card-basics and serialization paths, and the singular broken-link placeholder suite. Host lint:types (glint) and base hbs lint are clean.

The plural `linksToMany` view path and both link editors (standard +
compact) now read `getRelationship` once per render and dispatch
`BrokenLinkTemplate` for each slot whose relationship is `error` or
`not-found`; present, not-loaded, and not-set slots keep their existing
per-item render. Each placeholder occupies its own item container, so one
broken element no longer breaks the surrounding list.

Stability is preserved by leaving the iteration keying untouched — the
view path still keys on the stable per-child component identity and the
editors on their index / box identity. The broken-link state only feeds an
inner branch, and the relationship read is pure (it never retriggers the
lazy loader) and returns a fresh array that is never used as an each key.
This keeps per-slot identity stable when state hasn't changed, so a sibling
field staying focused mid-edit is never torn down by a broken neighbor.

In edit format a broken element shows the placeholder plus the existing
remove affordance, so a dead reference is visible and clearable rather than
rendering as a broken card.

`BrokenLinkTemplate` now accepts splattributes so callers can attach
per-element hooks/attributes to the placeholder root.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 27, 2026

Preview deployments

Host Test Results

    1 files      1 suites   1h 46m 15s ⏱️
2 813 tests 2 798 ✅ 15 💤 0 ❌
2 832 runs  2 817 ✅ 15 💤 0 ❌

Results for commit 23004c5.

Realm Server Test Results

    1 files      1 suites   9m 50s ⏱️
1 502 tests 1 502 ✅ 0 💤 0 ❌
1 593 runs  1 593 ✅ 0 💤 0 ❌

Results for commit 23004c5.

habdelra and others added 2 commits May 27, 2026 11:01
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the per-element broken-link placeholder pattern (previously added for singular linksTo in #4979) to plural linksToMany fields. Each broken slot in a linksToMany list now renders BrokenLinkTemplate in its own item container while present siblings render normally, in both view and edit (standard + compact) paths.

Changes:

  • Add brokenSlotsFor helper and per-slot broken-link rendering to the linksToMany view path, standard editor, and compact editor — keying {{#each}} on existing stable identities to preserve focus stability.
  • Export brokenLinkFormat from card-api and add splattributes (Element: HTMLDivElement, ...attributes) to BrokenLinkTemplate.
  • Add integration test coverage for mixed lists, link-error sentinel, fitted footprint, render-count convergence, edit removal, and DOM identity stability across unrelated re-renders.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
packages/base/links-to-many-component.gts Per-slot broken-link dispatch in view path + standard/compact editors, plus brokenSlotsFor helper.
packages/base/default-templates/broken-link-template.gts Accept splattributes on the root div for per-element hooks.
packages/base/card-api.gts Export brokenLinkFormat so the plural component can reuse the format mapping.
packages/host/tests/integration/components/linksto-many-broken-link-placeholder-test.gts New integration tests covering plural broken-link rendering, removal, and render stability.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

habdelra and others added 13 commits May 27, 2026 11:10
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@habdelra habdelra changed the base branch from cs-11225-render-broken-link-placeholder-for-singular-linksto-fields to main May 27, 2026 16:58
habdelra and others added 2 commits May 27, 2026 13:05
…n-link-placeholder-per-element-in-linkstomany
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@habdelra habdelra merged commit 4704cbd into main May 27, 2026
73 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants