[later phase] Support `{type: object, …}` prop shapes that require *multiple* field types — also: nested components/component reuse

Created on 13 August 2024, 5 months ago
Updated 26 August 2024, 4 months ago

Overview

So far, XB has only had example SDCs and has hence only supported {type: object, …} SDC prop shapes that have an equivalent field type. For example:

      "title": "image",
      "type":  "object",
      "required": ["src"],
      "properties": {
        "src": {
          "title": "Image URL",
          "$ref": "json-schema-definitions://experience_builder.module/image-uri"
        },
        "alt": {
          "title": "Alternative text",
          "type": "string"
        },
        "width": {
          "title": "Image width",
          "type": "integer"
        },
        "height": {
          "title": "Image height",
          "type": "integer"
        }
      }

maps perfectly onto the image field type (and can also be perfectly represented by a media reference to an @MediaSource=image-based media type/bundle — see 📌 Introduce `hook_storable_prop_shape_alter()`, use it to prefer the Media Library widget for "image" PropShape if Media Library is installed Fixed ).

But what about {type: object, …} shapes that do not happen to have an equivalent in Drupal? How do we map those?

📌 Introduce an example set of representative SDC components; transition from "component list" to "component tree" Fixed is introducing the first example SDCs that show that very problem, because it includes for example the following SDC prop JSON schema:

      "title": "Heading",
      "type": "object",
      "required": ["text", "element"],
      "properties": {
        "text": {
          "type": "string",
          "title": "Text",
          "description": "The heading text.",
          "examples": ["A heading element"]
        },
        "style": {
          "type": "string",
          "title": "Style",
          "description": "The heading style to use.",
          "enum": ["primary", "secondary"],
          "examples": ["primary", "secondary"]
        },
        "element": {
          "type": "string",
          "title": "Element",
          "description": "The HTML element to use.",
          "enum": ["div", "h1", "h2", "h3", "h4", "h5", "h6"],
          "examples": ["h1", "h2", "h3", "h4", "h5", "h6", "div"]
        }
      }

And perhaps the clearest example: the shoe_button SDC's icon prop:

    icon:
      title: Icon
      $ref: json-schema-definitions://experience_builder.module/shoe-icon
      type: object

with that schema being defined as:

…
    "shoe-icon": {
      "title": "Icon",
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "title": "Name",
          "description": "The badge’s theme variant.",
          "default": "primary",
          "enum": [
            "moon-stars-fill",
            "moon-stars",
            "star-fill",
            "star",
            "stars",
            "rocket-fill",
            "rocket-takeoff-fill",
            "rocket-takeoff",
            "rocket"
          ],
          "examples": [
            "moon-stars-fill",
            "moon-stars",
            "star-fill",
            "star",
            "stars",
            "rocket-fill",
            "rocket-takeoff-fill",
            "rocket-takeoff",
            "rocket"
          ]
        },
        "label": {
          "type": "string",
          "title": "Label",
          "description": "An alternate description to use for assistive devices. If omitted, the icon will be considered presentational and ignored by assistive devices.",
          "default": "This is an icon",
          "examples": ["This is an icon"]
        },
        "size": {
          "type": "string",
          "title": "Size",
          "description": "The icon size.",
          "default": "",
          "enum": ["", "base", "l", "s", "xs", "xxs"],
          "examples": ["", "base", "l", "s", "xs", "xxs"]
        },
        "color": {
          "type": "string",
          "title": "Color",
          "description": "The icon color",
          "default": "",
          "enum": [
            "",
            "gray",
            "primary",
            "neutral-soft",
            "neutral-medium",
            "neutral-loud",
            "primary-medium",
            "primary-loud",
            "black",
            "white",
            "red",
            "gold",
            "green"
          ],
          "examples": [
            "",
            "gray",
            "primary",
            "neutral-soft",
            "neutral-medium",
            "neutral-loud",
            "primary-medium",
            "primary-loud",
            "black",
            "white",
            "red",
            "gold",
            "green"
          ]
        },
        "slot": {
          "type": "string",
          "title": "Slot",
          "description": "The slot the icon should appear in",
          "examples": []
        }
      }
    },
…

Proposed resolution

Options:

  1. StaticPropSource currently can only have a single field type (see \Drupal\experience_builder\PropSource\StaticPropSource::__construct(fieldItem)). As long as that remains the same, the only choice would be to generate a composite field type containing all the necessary field types — basically: https://www.drupal.org/project/field_union
  2. Introduce a new CompositeStaticPropSource, that contains multiple StaticPropSources — very similar to the current \Drupal\experience_builder\PropSource\AdaptedPropSource
  3. Either of the above is going to result in rather complex (read: lengthy) JSON blobs. Rather than storing a serialized StaticPropSource, for any component prop that does not rely on any computed field property (e.g. the URL of an "image" field type, the processed text of a "text" field type, etc.), store only the resulting value, and "just" compute the appropriate field type on the fly, at edit time, using the infrastructure that 📌 Support complex SDC prop shapes: introduce (Storable)PropShape to compute field type storage settings Fixed introduced.

⚠️ Not even considered yet: deep nesting. 📌 Follow-up for #3446722: `side_by_side` + `accordion` (+ maybe `text`) components Active contains some SDCs that reuse other SDCs's Twig templates (not, the full SDC, just the Twig template — because SDC does not have the concept of nesting) and hence nest another component's props into a single prop — e.g. the side_by_side components reusescomponents/simple/heading/heading.component.yml component. Hence the text prop for "heading" becomes heading.text. Another component might even use the "heading" component twice and hence have first_heading.text and second_heading.text.
But what if another component reuses side_by_side? Et cetera? Then it would become side_by_side.first_heading.text. Do we represent that as nested collapsible fieldsets? That seems bad for UX.

🚨 IOW: I fear that we have far more work to do to properly support every component that 📌 Introduce an example set of representative SDC components; transition from "component list" to "component tree" Fixed added, let alone to support arbitrarily complex SDCs.

💡 📌 Auto-create/update Component config entities for all discovered SDCs that meet XB's minimum criteria Fixed is adding a mechanism that allows us to define minimum criteria for an SDC to become available in XB. So we could choose to grow those criteria, to only allow SDCs to be used in XB that result in a reasonable UX.

User interface changes

TBD

📌 Task
Status

Postponed

Component

Page builder

Created by

🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

Live updates comments and jobs are added and updated live.
  • Needs product manager review

    It is used to alert the product manager core committer(s) that an issue represents a significant new feature, UI change, or change to the "user experience" of Drupal, and their signoff is needed. If an issue significantly affects the usability of Drupal, use Needs usability review instead (see the governance policy draft for more information).

Sign in to follow issues

Comments & Activities

Production build 0.71.5 2024