Skip to content

feat: add experience-toolbar starter example [EXT-7365]#11009

Merged
Jared Jolton (jjolton-contentful) merged 11 commits into
masterfrom
feat/experience-toolbar-example
Jul 9, 2026
Merged

feat: add experience-toolbar starter example [EXT-7365]#11009
Jared Jolton (jjolton-contentful) merged 11 commits into
masterfrom
feat/experience-toolbar-example

Conversation

@jjolton-contentful

@jjolton-contentful Jared Jolton (jjolton-contentful) commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Note

This example targets an App SDK location that's currently in limited/internal availability. The Experience Editor toolbar location isn't generally available yet, so an app built from this example won't be installable in most spaces for the time being. The example is published now so the code and patterns are here to explore and build against ahead of a wider rollout.

Especially curious? If you'd like to try the toolbar location early, reach out to Contentful support — we're happy to talk through early-access options.

EXT-7365

Summary

  • Adds a Create Contentful App example at examples/experience-toolbar for the new Experience Editor toolbar App SDK location (LOCATION_EXPERIENCE_TOOLBAR), scaffoldable via npx create-contentful-app --example experience-toolbar.
  • Typed with ExperienceEditorToolbarAppSDK from @contentful/app-sdk@^4.63.1. Demonstrates every pattern in the ticket AC:
    • location detection — sdk.location.is(locations.LOCATION_EXPERIENCE_TOOLBAR) in App.tsx
    • reading sdk.experiences.context to tell experience vs. fragment apart (+ onContextChanged)
    • subscribing to sdk.experiences.experience.selection.onChange()
    • resolving the selected node via sdk.experiences.experience.getNode(nodeId) and reading its properties
    • driving the canvas from the toolbar via sdk.experiences.experience.selection.highlight()
  • React-only; no Data Assembly or save()/publish() (per ticket excluded scope). Mirrors examples/typescript conventions (Vite + Vitest + Forma 36).

Two deviations from the AC (intentional — flagging for review)

  1. --example, not --template. The AC says --template experience-toolbar, but the CLI scaffolds repo examples via --example <name> (resolved from apps/examples/ via the GitHub API on master). --template is a fixed allowlist of framework starters (IGNORED_EXAMPLE_FOLDERS). Delivered as an example, matching how editor-assignment, page-location, etc. ship. The ticket hedges with "(or equivalent)".
  2. No app-definition.json. No example in the repo ships a manifest, and the toolbar location is not an EditorInterface target state — visibility is determined solely by whether the location is registered on the app definition. Documented registration via create-app-definition / add-locations in the README and a ConfigScreen note instead of inventing a manifest the CLI would strip.

Verification status

  • ✅ Type-verified: tsc --noEmit clean against the published app-sdk types.
  • vite build clean.
  • ✅ 7/7 Vitest specs pass (mocked SDK drives selection, properties, and canvas-highlight paths).
  • ⚠️ Not runtime-verified inside a live Experience Editor — the host renderer serving sdk.experiences is still rolling out (EXT-7363/EXT-7364). The README states this explicitly. API shapes match the published types exactly.

Test plan

  • npm install && npm run build in examples/experience-toolbar succeeds
  • npm run test:ci passes
  • npx create-contentful-app --example experience-toolbar scaffolds a working project (once merged to master, since the CLI reads examples from the GitHub API)
  • (When host renderer lands) install into a space, register the experience-toolbar location, open an experience, and confirm context/selection/properties render live

Generated with Claude Code

@lewisjcs

Josh Lewis (lewisjcs) commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

⚔️ The Gauntlet — ✅ 0 Blockers · 2 advisory

🤖 AI-powered · your code, through the lanes

gauntlet verdict

🛑 Blockers 0
⚠️ Concerns 0
💡 Nits 2
🔒 Security clean ✓
🧪 Lanes code-quality-audit · adversarial · doc-review (PR body) · security

Re-review (2026-07-09) covering the six commits since the last pass — canvas-highlight button, the sdk.exosdk.experiences rename, the UiMode surface removal, and two app-sdk bumps up to ^4.63.1. Both prior concerns are resolved with real tests, not just taken on faith. Two small items surfaced in the delta itself; no blockers.

Update (2026-07-09): the README staleness concern is resolved — the stale form-mode gating sentence was removed. Two nits remain open.

Findings

Lens Finding Location Status
⚠️ code-quality-audit / Staleness The README still says the "Highlight on canvas" button is disabled in form mode, but that gating was fully removed from the component in this same delta. README.md:88 ✅ Resolved
💡 doc-review / Memory rules — no local paths Orphaned [EXT-7365]: …?atlOrigin=… reference-link still carries an Atlassian smartlink token into a public repo. PR description footer 🔴 Still open
💡 code-quality-audit / Gaps formatPropertyValue's ternary has a dead false-branch — app-sdk 4.63.1's EntryBinding.type is a single literal, so the non-'entry' arm can never execute. src/locations/ExperienceToolbar.tsx:233 🔴 Still open
⚠️ adversarial-review / Hidden Assumptions getProperties never rejected in the mock, so the loading-spinner and error paths were untested. test/mocks/mockSdk.ts:16 ✅ Resolved
⚠️ code-quality-audit / Gap The binding branch of formatPropertyValue was never exercised. src/locations/ExperienceToolbar.tsx:222 ✅ Resolved
💡 code-quality-standards / Rule 5 Initial getProperties().catch() reset state silently with no log. src/locations/ExperienceToolbar.tsx:101 ✅ Resolved
🤖 Machine-readable findings (for agents)
{
  "tool": "gauntlet",
  "schema": "v1",
  "reviewed_ref": "5e27e878f",
  "verdict": { "blockers": 0, "concerns": 0, "nits": 2, "security": "clean" },
  "findings": [
    { "severity": "nit", "location": "PR description footer", "lens": "doc-review / Memory rules — no local paths", "confidence": 88, "claim": "Orphaned reference-link definition still carries an Atlassian smartlink tracking token into a public repo.", "recommendation": "Delete the trailing reference-style definition; the inline top link suffices." },
    { "severity": "nit", "location": "src/locations/ExperienceToolbar.tsx:233", "lens": "code-quality-audit / Gaps", "confidence": 85, "claim": "formatPropertyValue's binding-type ternary has an unreachable false branch under app-sdk 4.63.1's EntryBinding shape.", "recommendation": "Simplify to a direct entry -> entryId template string; drop the ternary." }
  ]
}

🎮 The Gauntlet · an AI review harness built by Josh C.S. Lewis · reviewed at 5e27e878f. Everything but the 🛑 is advisory.

@lewisjcs Josh Lewis (lewisjcs) force-pushed the feat/experience-toolbar-example branch from eba492e to 45989e8 Compare June 5, 2026 01:58
@lewisjcs Josh Lewis (lewisjcs) force-pushed the feat/experience-toolbar-example branch from 165d7b4 to 473df75 Compare June 24, 2026 18:56
@wiz-inc-38d59fb8d7

wiz-inc-38d59fb8d7 Bot commented Jul 6, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings -
Software Management Finding Software Management Findings -
Total -

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

Add a Create Contentful App example demonstrating the new ExO toolbar
App SDK location (LOCATION_EXPERIENCE_TOOLBAR), scaffoldable via
`npx create-contentful-app --example experience-toolbar`.

Typed with ExperienceEditorToolbarAppSDK (@contentful/app-sdk@4.58.0),
it demonstrates location detection, reading sdk.exo.context
(experience vs. fragment), subscribing to selection changes, reading
the selected node's properties via getNode, and reacting to UI mode
changes. React-only; no Data Assembly or save/publish (per ticket scope).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-7365]

Address code review on the experience-toolbar starter:
- add a deferred-promise spec asserting the loading spinner
- add a spec for the getProperties rejection path (spinner clears, no table)
- cover the formatPropertyValue binding branch ("entry -> entryId")
- comment the previously-silent getProperties().catch() state reset

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…on [EXT-7365]

4.58.1 reshaped ComponentPropertyDescriptor.binding to the typed `Binding`
union (EntryBinding | ManualBinding), replacing `{ sourceType, entryId }`.

- formatPropertyValue narrows on `binding.type === 'entry'` to read entryId
  off the EntryBinding arm (was destructuring the removed `sourceType`).
- Spec uses the EntryBinding fixture shape `{ type, entryId, fieldId }`.
- `area` rendering is unchanged — the discriminator is retained in the SDK.
- Bump @contentful/app-sdk 4.58.0 -> 4.58.1.

Verified: tsc --noEmit clean, 12 tests passing against 4.58.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Track the latest published ExO surface (DA definition-only trim landed in
ui-extensions-sdk#2592, widget-renderer synced in experience-packages#3544).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…le [EXT-7497]

Bump @contentful/app-sdk floor ^4.59.0 → ^4.60.0 and apply the
Exo*→Experience* / sdk.exo→sdk.experiences rename across all example
source, test, mock, and documentation files.
…lbar [EXT-7365]

README referenced @contentful/app-sdk@4.58.0 (which shipped the old sdk.exo
surface) while package.json pins ^4.60.0 (where sdk.experiences first exists),
so a reader checking the version would hit a surface mismatch. Bumps both
references to 4.60.0 and drops customer-facing "ExO" phrasing per the approved
naming (internal-only abbreviation).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
app-sdk 4.63.1 removes the experience getUiMode()/onUiModeChanged()
surface, which this starter demonstrated. Remove the mode badge, the
form-mode notice, and the mode-gated highlight button; the highlight
action now keys off whether a node is selected. Bump the app-sdk pin to
^4.63.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…XT-7365]

UI mode tracking was dropped from the example in 35eb56c; the README
still claimed the highlight button was disabled in form mode.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jjolton-contentful Jared Jolton (jjolton-contentful) merged commit 1b794c1 into master Jul 9, 2026
16 checks passed
@jjolton-contentful Jared Jolton (jjolton-contentful) deleted the feat/experience-toolbar-example branch July 9, 2026 20:44
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.

2 participants