Overview

Right now, if you have a number of component properties, they all show up in the UI with no organization other than the order they show up in the yml file.

There are reasons why it could improve the UX to allow for the categorization of the properties.

For example, if there are properties for filling in the content of the component vs properties for styling the component, they could be separated to make this more clear, e.g. a button might have:

Content

- icon
- text
- url

Styling

- type (primary, secondary, tertiary)
- theme (light, dark)
- size (small, regular, large)

but there might even be other "advanced"/"additional"/ "extra" props/settings like:

Advanced

- is_new_window (opens in a new window)
- is_external (shows arrow to indicate it's linking to external site)
- is_disabled (disabled button)
- css_class (extra class that can be added)

Proposed resolution

Introduce a new "category"/"group" or similar classification for properties.

Ideally, these groupings can open/close and we can also define which of these groupings are open by default. For example, we may want to close the "Styling" and "Advanced" props by default so as to not overwhelm the user.

User interface changes

Shows each category/group with all the properties under it and some mechanism to open/close.

Issue fork canvas-3492758

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

kristen pol created an issue. See original summary.

kristen pol’s picture

I should note that Lauri asked me in Slack to create this issue :)

wim leers’s picture

"category" as a term is the worst possible choice given #3459088: Every XB `Component` config entity should have a `category` property in XB and #3474533: ComponentPluginManager must implement CategorizingPluginManagerInterface in Drupal core — if both the SDC itself has a category and every SDC prop does too, then what does "category" even mean anymore?! 😅

Also, to not violate @lauriii's product requirement 3. Real-time page preview's spirit ("no XB-specific metadata or logic in SDCs"), this will need to become part of SDC itself, so it should be added to #3462705: [META] Missing features in SDC needed for XB.


Ideally, these groupings can open/close and we can also define which of these groupings are open by default. For example, we may want to close the "Styling" and "Advanced" props by default so as to not overwhelm the user.

This sounds very similar to, but different from conditional(ly required) props (f.e.: if the {type: boolean} cta.enabled prop is true, also require the cta.text and cta.url props) in #3462705: [META] Missing features in SDC needed for XB.

wim leers’s picture

Assigned: Unassigned » kristen pol

#3459088: Every XB `Component` config entity should have a `category` property is in.

@kristen pol Any thoughts from you on #3 until @lauriii gets a chance to respond? 😊

kristen pol’s picture

Found this in my inbox :D I don't have any additional feedback right now

Note this issue was created recently that is also about organizing the props form in a more general way:

#3521388: Possibility to add HTML markup to generated form from SDC

Project: Experience Builder » Drupal Canvas
Version: 0.x-dev » 1.x-dev

Experience Builder has been renamed to Drupal Canvas in preparation for its beta release. You can now track issues on the new project page.

carlitus’s picture

+10 for this feature. ¿Maybe a better title can be "Allow component properties to be grouped"?

kristen pol’s picture

Title: Allow component properties to be categorized » Allow component properties to be categorized/grouped/organized
Assigned: kristen pol » Unassigned
Status: Needs work » Active

The AI initiative has consumed me so unassigning

dalemoore’s picture

This sort of functionality is present in Layout Builder and would be a huge benefit to organizing the props. See the "Bootstrap Layout Builder" module for an idea of how it's done there for Layout Builder sections. The props can be organized into panels with tabs at the top. It would be nice to be able to alter the presentation of the props similar to what they've done to make them more intuitive than just standard dropdowns, checkboxes, etc. too.

Screenshot of Bootstrap Layout Builder

Edited to add: Grouping of props also exists on WordPress blocks, with the open/close functionality mentioned as well.

lauriii’s picture

After looking at competitors, I'm thinking we should declare prop groups once at the top, and then let each prop opt in by name:

# button.component.yml
  propGroups:
    - { id: content,  title: Content,  weight: 0,  open: true  }
    - { id: styling,  title: Styling,  weight: 10, open: true  }
    - { id: advanced, title: Advanced, weight: 20, open: false }

  props:
    type: object
    properties:
      text:
        type: string
        x-prop-group: content
      type:
        type: string
        enum: [primary, secondary]
        x-prop-group: styling
      cssClass:
        type: string
        x-prop-group: advanced

Using propGroups / x-prop-group. Avoids category, group, and section (Layout Builder). Prop groups are UI-only metadata. The value passed to the component is unchanged. Object props are for real data hierarchy (e.g. an image with src/alt); prop groups are purely for organizing the form.

Some important considerations:

  • These would create collapsible details elements by default
  • Ungrouped props render at the top with no header. (i.e. no auto-injected "General" group)
  • Groups with validation errors auto-expand on submit
  • Explicit weight for sort, declaration order as tiebreaker

Given that this is purely additive and doesn't touch data, we could consider adding forward compatibility for this once there's alignment on the syntax.

This would support more complex use cases like tabs at the top, accordions for nested structures:

# form-input.component.yml
  propGroups:
    - { id: main, title: Main, weight: 0, open: true, as: tab }
    - { id: rules, title: Rules, weight: 10, open: true, as: tab }
    - { id: content, title: Content, weight: 0, open: true, parent: main }
    - { id: validation, title: Validation, weight: 0, open: true, parent: rules }
    - { id: a11y, title: A11y, weight: 10, open: false, parent: rules }

Prior art in UI Patterns Settings
UI Patterns Settings already solves the same problem via a type: group setting + group_type (horizontal_tabs, details, container, fieldset) + a per-prop group: <parent-id> reference, with nesting achieved by pointing one group's group at the parent. My proposal is intentionally a 1:1 conceptual mirror of this, with one intentional departure: groups are declared in a sibling top-level key rather than as entries inside the prop list. That keeps SDC props 100% JSON Schema, keeps the data shape passed to the component unchanged, and avoids a type: group entries that JSON Schema validators would otherwise have to ignore.

wim leers’s picture

#10: Let's get a +1 from the SDC maintainer, @pdureau. Your call when the right time for that would be.

lauriii’s picture

I don't think there's anything to @pdureau review in the MR since it's a vibe coded MR I haven't had the time to review myself. That said, it would be great to get a review on the schema itself I proposed in #10.

Pinged @pdureau on Slack.

finnsky’s picture

I think we're asking too much from a simple SDC :)
Let's look at UI Schema
https://jsonforms.io/docs/uischema/

This is quite a popular approach!
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/uiS...

It might even be necessary to remove the variants from SDC because they were obviously added for UI improvements and are redundant.

lauriii’s picture

JSON Forms / RJSF do model this cleanly with a separate UI schema. But the relevant peer set for SDC is component authoring systems (Sanity, Storybook, Optimizely, WordPress, Builder.io, Storyblok), not form-rendering libraries. All of them chose co-location, because a separate UI schema means two files to keep in sync. When building complex forms, the trade-off is different because form-rendering libraries are designed to handle much more complexity than what's required by component authoring.

x-prop-group isn't really mixing UI logic into SDC: it's the same pattern as title, description, examples, advisory metadata that validators ignore and SDC itself renders nothing for.

Variants seems like separate discussion which deserves its own issue. They're entangled with data shape (variant is a prop) and UI semantics in a way grouping is not, so I'd like to refrain from conflating them here.

finnsky’s picture

It's convenient to make a separate file standard and easily validate it using your own schema without affecting the data.

Besides propGroups, there are many possible uses.
Hide properties. Separate property values. Or, conversely, expand headers and values.

Custom validation and dependent fields can be added.
For example, with the horizontal card option, the user doesn't have to enter media.

All of this can be greatly expanded.

pdureau’s picture

Prior art in UI Patterns Settings
UI Patterns Settings already solves the same problem via a type: group setting + group_type (horizontal_tabs, details, container, fieldset) + a per-prop group: <parent-id> reference, with nesting achieved by pointing one group's group at the parent.

My proposal is intentionally a 1:1 conceptual mirror of this, with one intentional departure: groups are declared in a sibling top-level key rather than as entries inside the prop list. That keeps SDC props 100% JSON Schema, keeps the data shape passed to the component unchanged, and avoids a type: group entries that JSON Schema validators would otherwise have to ignore.<

A little information before going further in the discussion.

This group mechanism is from the UI Patterns 1.x ecosystem, so predating SDC (UI Patterns 1.0 has been released in 2017, UI Patterns Settings has been introduced in 2019). As far as I know, we didn't port this feature in UI Patterns 2.x, when we have adopted SDC.

I think we're asking too much from a simple SDC :)

That may be the risk here. We expect SDC authors to define a UI model, while implementing a design, not to describe the form of a visual builder. But let's see what can be done...

pdureau’s picture

All of them chose co-location, because a separate UI schema means two files to keep in sync.

Indeed, keeping 2 files can be error prone and not the best DX. But @finnsky proposal of using an already well defined and used format (2700 github stars, active development, 100+ contributors...) sounds nice:

  • it delegates the responsibility of defining such format
  • it leverages potential familiarity
  • it adds extra features besides prop groups, as said by Ivan: hide properties, separate property values, expand headers and values...

As we switch from our own format to JSON schema as soon as we enter the "prop territory" in the SDC definition file, can we switch to JsonForms UI schema in a subset of the same definition file?

For example:

name: Tabs
slots:
  body:
    title: Body
props:
  type: object
  properties:
    text:
      type: string
    type:
      type: string
      enum: [primary, secondary]
ui:
  - type: Group
    label: My Group
   elements:
    - type: Control
      scope: "#/props/properties/text"
   - type: Control
     scope: "#/props/properties/type"

Source: https://jsonforms.io/docs/uischema/layouts#group

Is it something worth to investigate further (with jsonforms or another established format)?

lauriii’s picture

You and @finnsky have convinced me. The proposal from #18 allows keeping it in a single file. The syntax is more verbose but since most of this will likely be authored by AI in future, it doesn't seem problematic. It's cleaner to also have it in a separate section instead of having it partially inside the props.

Two small clarifications on syntax for the proposal:

  1. Scope is relative to the data schema, so scope: #/properties/text (not #/props/properties/text). props should be itself the JSON Schema document the UI schema points into.
  2. If ui is omitted, Canvas keeps current behavior (flat auto-form from props in declaration order). Opt-in, no migration.

We should add a validation step at SDC discovery that asserts every scope. It should ensure that the references in UI schema resolve to an existing property, so dangling references become a hard error rather than a silent UI gap. This seems cheap to build and it eliminates the main drift concern.

If the SDC + UI Patterns folks see this as a path forward I'd be happy to move the MR to use this approach.

pdureau’s picture

That's great.

The syntax is more verbose but since most of this will likely be authored by AI in future, it doesn't seem problematic.

I agree the syntax is verbose, maybe too much, and it can be challenged because we agreed on the main stuff:

  • 👍 only one file to author for themer: the SDC YAML definition
  • 👍 in this file, everything a specific root property, without altering anything elsewhere
  • 👍 in this root property, let's call it ui, we use an already established format like we already do with JSON schema in the props
  • 👍 as you said, if ui is omitted, the visual builder is able to keep its default behavior. Opt-in, no migration. A visual builder can also decide to ignore this part of the SDC definition.

But we can still discuss about the chosen format. @finnsky was nice because popular, powerful and well maintained. But we can find another with similar qualities and a better DX.

lauriii’s picture

It seems there's three credible options to consider: JSON Forms, a flat key-shape (Storybook argTypes style), and RJSF uiSchema. It seems that JSON Forms wins:

  1. RJSF: no first-class grouping (issue open since 2017).
  2. Flat key-shape: shorter for trivial cases but no conditional grammar, no tabs. Also not an established format.
  3. JSON Forms: covers groups, tabs, conditionals using JSON Schema. UI schema shape stable since 2.x.

What we'd need to be able to use it:

  1. Meta-schema in Drupal Core / Canvas for VS Code autocomplete and AI grounding.
  2. Two opt-in shorthands in the loader, pre-expanded to canonical:
    yaml# Canonical
    elements:
      - { type: Control, scope: '#/properties/text' }
    
    # Shorthand (bare string → Control; key: → scope:)
    elements: [ text ]
    
  3. Build our own renderer. Consume the schema, not the React runtime.
finnsky’s picture

I quickly scanned the list here and apparently yes - JSON Forms looks like the favorite.

https://json-schema.org/tools?query=&sortBy=name&sortOrder=ascending&gro...

ctrladel’s picture

Been awhile since I compared json schema form libraries but just want to point out RJSF has a whole grid layout feature. https://rjsf-team.github.io/react-jsonschema-form/docs/usage/layout-grid/ you can see it in the playground under the "Layout Grid" sample https://rjsf-team.github.io/react-jsonschema-form/