Skip to content

feat(rjsf): suppress the duplicative top-level object title in RJSF forms#1601

Merged
leecalcote merged 2 commits into
masterfrom
claude/sweet-sagan-O7ABR
Jun 1, 2026
Merged

feat(rjsf): suppress the duplicative top-level object title in RJSF forms#1601
leecalcote merged 2 commits into
masterfrom
claude/sweet-sagan-O7ABR

Conversation

@hortison
Copy link
Copy Markdown
Contributor

@hortison hortison commented Jun 1, 2026

Description

The RJSF form schemas we consume from @meshery/schemas carry a human‑readable title on their root object — e.g. the import‑design schema's root title: "Import Design". When that form is rendered inside a surface that already names it (most commonly a modal whose header shows the very same title), RJSF draws the root title a second time, directly above the fields:

  • Import Design modal — the top‑level object is rendered as its own section that is collapsed by default, so users have to click to expand it before they can see (or use) the form. The heading is also duplicative of the modal title.

We want the root object's contents shown directly, with the duplicative title gone — while still consuming the canonical UI schema verbatim (no hand‑edited fork, no mutation of the JSON schema). This establishes a reusable standard for that situation across Sistent and its downstream consumers (Meshery, Meshery Cloud, Meshery Extensions).

The standard pattern

Set ui:options.label = false on the root of the UI schema. Per @rjsf/core's ObjectField, when label === false the title handed to the ObjectFieldTemplate is forced to '' (and the description to undefined), so every template — the default @rjsf/mui one and the custom collapsible templates used downstream — skips its header and renders the child fields inline. Because the lever is the UI schema and not the JSON schema, validation, required, and conditional allOf branches stay untouched.

What changed

  • hideRootObjectTitle(uiSchema) — new exported helper that merges ui:options.label = false into the root of a UI schema. Returns a shallow copy (never mutates input); preserves ui:order, per‑field overrides, and any existing ui:options.
  • RJSFFormModal — new hideRootTitle prop, defaulting to true: the modal header already names the form, so the root object title is suppressed automatically. This fixes the Import Design modal out of the box.
  • RJSFFormWrapper — same hideRootTitle prop, defaulting to false so the general‑purpose wrapper is unchanged unless a consumer opts in.
  • Docssrc/schemas/readme.md documents the pattern and the three ways to apply it.

How it was verified

  • New hideRootObjectTitle unit tests (merge correctness, preservation, no‑mutation).
  • An end‑to‑end test renders the canonical import‑design schema through @rjsf/core with a recording ObjectFieldTemplate and asserts the root title RJSF derives is "Import Design" by default and "" after hideRootObjectTitle (with the child fields still present).
  • Full suite green (jest: 12 suites / 295 tests), eslint clean on changed files, tsup build succeeds and the new symbol/prop appear in both the runtime (dist/index.mjs, dist/index.js) and type (dist/index.d.ts) outputs.

Notes for reviewers

Downstream PRs in meshery/meshery and layer5io/meshery-cloud adopt the same UI‑schema‑driven approach in their own RJSF stacks (replacing a schema‑mutating hideTitle hack). They inline a local mirror of hideRootObjectTitle until they consume a release of Sistent that exports it.

  • Signed commits (DCO)

…itle

The canonical @meshery/schemas form schemas carry a title on their root object (e.g. the import-design schema's "Import Design"). Rendered inside a titled modal the root title is drawn a second time — a duplicate heading in the default theme, or a collapsed accordion in the downstream custom RJSF templates that users must expand before seeing any fields.

Introduce the standard, UI-schema-driven fix: hideRootObjectTitle() merges `ui:options.label = false` into the root of the UI schema, so RJSF's ObjectField hands an empty title (and no description) to whatever ObjectFieldTemplate is registered and the child fields render directly. The canonical JSON schema is consumed unmodified.

RJSFFormModal applies it by default (the modal header already names the form); RJSFFormWrapper exposes it via the opt-in hideRootTitle prop. Adds unit + RJSF-derivation tests and documents the pattern in the schemas readme.

Signed-off-by: Lee Calcote <leecalcote@gmail.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a mechanism to suppress the root object title and description in React JSON Schema Form (RJSF) forms, preventing duplicate headers when forms are rendered inside titled surfaces like modals. This is achieved by adding a hideRootTitle prop to RJSFFormWrapper and RJSFFormModal, supported by a new utility function hideRootObjectTitle that sets ui:options.label = false on the UI schema. Comprehensive tests and documentation in readme.md are also added. Feedback suggests making the type check for ui:options in hideRootObjectTitle more robust by explicitly ensuring it is not an array.

Comment on lines +81 to +83
const existing = uiSchema?.['ui:options'];
const existingOptions =
typeof existing === 'object' && existing !== null ? (existing as Record<string, unknown>) : {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

In JavaScript, typeof [] is 'object'. If uiSchema['ui:options'] is somehow configured as an array, typeof existing === 'object' && existing !== null will evaluate to true, and spreading it in { ...existingOptions, label: false } might lead to unexpected behavior. Adding !Array.isArray(existing) makes this check more robust and defensive.

  const existing = uiSchema?.['ui:options'];
  const existingOptions =
    typeof existing === 'object' && existing !== null && !Array.isArray(existing)
      ? (existing as Record<string, unknown>)
      : {};

Copy link
Copy Markdown
Contributor Author

@hortison hortison Jun 1, 2026

Choose a reason for hiding this comment

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

Good call — applied in 989b483. The ui:options merge now guards with !Array.isArray(existing), so a malformed array option falls back to {} cleanly instead of being spread into index keys. Added a regression test for that case.

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

This PR standardizes how Sistent (and downstream consumers) render canonical @meshery/schemas RJSF forms inside titled surfaces (especially modals) without showing a duplicative, often-collapsed root object “header”, by applying the UI-schema lever ui:options.label = false at the root.

Changes:

  • Added hideRootObjectTitle(uiSchema) helper to non-mutatively merge ui:options.label=false at the UI schema root.
  • Added hideRootTitle prop to RJSFFormModal (default true) and RJSFFormWrapper (default false) to apply the helper automatically.
  • Added docs + unit/E2E-style tests to lock in merge behavior and verify RJSF root title suppression.

Reviewed changes

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

Show a summary per file
File Description
src/schemas/readme.md Documents the standard pattern and how to apply it via modal, wrapper, or helper.
src/custom/RJSFFormWrapper/RJSFFormWrapper.tsx Adds hideRootTitle prop and applies hideRootObjectTitle when enabled.
src/custom/RJSFFormWrapper/RJSFFormModal.tsx Adds hideRootTitle prop (default true) and forwards it to the wrapper.
src/custom/RJSFFormWrapper/index.ts Re-exports hideRootObjectTitle from the module barrel.
src/custom/RJSFFormWrapper/hideRootObjectTitle.ts Introduces the helper that merges ui:options.label=false without mutating input.
src/testing/hideRootObjectTitle.test.tsx Adds unit tests for merge/no-mutation and an RJSF render assertion for root title suppression.

Address review feedback on #1601: typeof [] === 'object', so guard the ui:options merge with !Array.isArray(...) to fall back cleanly when the option is malformed. Adds a regression test.

Signed-off-by: Lee Calcote <leecalcote@gmail.com>
@leecalcote leecalcote merged commit fb7ab03 into master Jun 1, 2026
5 checks passed
@leecalcote leecalcote deleted the claude/sweet-sagan-O7ABR branch June 1, 2026 16:59
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