fix(cms): expand recursive schema types when their nested items are opened

Merged
GU
3d ago
+188-6

What is this contribution about?

#7899 stopped resolveSchema from re-expanding a $ref that is already on the current path. That fixed the hang on recursive commerce types (Product → hasVariant: Product[]), but the cut node lost its properties/items. Recursive data types broke as a result. A site's menu Item has children?: Item[], so the child items appear in the list, but each child opens as an empty form: only the multivariate variant/rule chrome shows and there are no fields to edit.

This change keeps the cut and makes it lazy:

  • resolve-schema.ts: the cut node now carries recursiveRef (the re-entered $ref), and the new resolveSchemaRef(ref, meta) builds that definition on its own.
  • fields/resolved-schema-cache.ts: expandRecursiveSchema fills in the skipped level (properties/required/items) and keeps the field's own title and description. It caches the result per node, so re-renders get the same object back.
  • renderField expands cut array and object fields. ArrayField expands a cut item schema (e.g. Product[] items). Each drill expands one level, and the next re-entry is cut again, so the hang guard still holds.

How did you verify your code works?

  • I ran resolveSchema against the real /live/_meta of an affected site. Before the fix, navItems → desktop[] → children resolved to { type: "array" } with no items. After it, expandRecursiveSchema gives the child item and the grandchild item all their fields (label, flagNew, banner, countBadge, children, url, display). The whole header section still resolves in about 10ms.
  • New tests in resolve-schema.test.ts: the cut node carries recursiveRef for Product; resolveSchemaRef builds the skipped level and cuts the next re-entry again; a recursive array def (Item.children: Item[]) is marked and expands with titleBy.
  • New tests in fields/resolved-schema-cache.test.ts: the expansion keeps the field title, returns the same object on every call, and is a no-op without recursiveRef or meta.
  • bun run verify passes: 10865 tests, 0 failures.
  • Not run: a manual check in the Site Editor UI.

Screenshots/Demonstration

No screenshots: I didn't run the Site Editor against an affected site.

How to Test

  1. Open the Site Editor on a site whose section has a recursive array prop, e.g. a header menu whose Item has children?: Item[].
  2. Open the nav items, open a top-level item, then click one of its "children" items.
  3. Expected: the child item's fields (text, URL, flag, banner, …) render and save. Before this fix the panel was empty below the variant rule.

Review Checklist

  • PR title is clear and descriptive
  • Changes are tested and working
  • Documentation is updated (if needed)
  • No breaking changes

🤖 Generated with Claude Code


Summary by cubic

Fixes recursive data-type forms that opened empty after #7899's recursion guard. When a recursive type re-entered itself (Product → hasVariant: Product[], menu Item with children?: Item[]), the cut node kept its type but lost its properties/items, so child items appeared in the list but each one rendered no fields.

The cut node now carries a recursiveRef, and the form fills in that skipped level on demand via the cached expandRecursiveSchema helper when renderField or ArrayField reaches it—restoring editable fields while keeping the recursion guard intact, one level at a time.

Bug Fixes

  • Recursive object and array properties now render their nested fields instead of an empty panel.
  • Re-expanding the same node returns a cached object, so re-renders get stable references.