Overview

Currently, the Experience Builder code editor does not offer a way to add image fields.

Proposed resolution

  1. ✅ Back end: allowing a prop to be created using this definition: {"type": "object", "$ref": "json-schema-definitions://experience_builder.module/image"}. Requires:
    1. Updating config schema: #2
    2. Expanding validation: #6
    3. Explicit test coverage: #7.
  2. Front end: Introduce a new image property type that can be used in the code editor.

User interface changes

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

lauriii created an issue. See original summary.

wim leers’s picture

This is incomplete because it still allows type: object with arbitrary other values to be specified. IOW: more validation logic is needed.

But … this should allow the FE work to begin right now:

diff --git a/config/schema/experience_builder.schema.yml b/config/schema/experience_builder.schema.yml
index b3c76f77..8f86dd25 100644
--- a/config/schema/experience_builder.schema.yml
+++ b/config/schema/experience_builder.schema.yml
@@ -102,9 +102,20 @@ experience_builder.js_component.*:
                 - 'number'
                 - 'integer'
                 - 'boolean'
-                # @todo Consider adding `array` after https://www.drupal.org/i/3467870 is fixed.
-                # @todo Consider adding `object`, but defining _arbitrary_ object shapes is not yet supported.
+                # ⚠️ Defining _arbitrary_ object shapes is not yet supported. Currently only one such shape is
+                # supported: images, which requires `type: object` plus
+                # `$ref: json-schema-definitions://experience_builder.module/image`.
                 # @see \Drupal\experience_builder\JsonSchemaInterpreter\SdcPropJsonSchemaType::computeStorablePropShape()
+                - 'object'
+                # @todo Consider adding `array` after https://www.drupal.org/i/3467870 is fixed.
+          '$ref':
+            requiredKey: false
+            type: string
+            constraints:
+              # All choices listed here must be supported: they must have a corresponding storable prop shape.
+              # @see \Drupal\experience_builder\JsonSchemaInterpreter\SdcPropJsonSchemaType::computeStorablePropShape()
+              Choice:
+                - 'json-schema-definitions://experience_builder.module/image'
           description:
             requiredKey: false
             type: label
wim leers’s picture

(#3499927: Config entity for storing code components heavily restricted what props you're allowed to use. My initial thinking: add a validation constraint that checks that the user-specified prop for the code component results in a non-NULL return value from \Drupal\experience_builder\JsonSchemaInterpreter\SdcPropJsonSchemaType::computeStorablePropShape().)

lauriii’s picture

Cross referencing an issue to add support for textarea in the code editor which is likely very similar to this.

wim leers’s picture

wim leers’s picture

Assigned: Unassigned » f.mazeikis

What's not yet explained in #2, is the explicit validation.

We'll need to add a new validation constraint:

diff --git a/config/schema/experience_builder.schema.yml b/config/schema/experience_builder.schema.yml
index e3352d5b..26e26d27 100644
--- a/config/schema/experience_builder.schema.yml
+++ b/config/schema/experience_builder.schema.yml
@@ -96,6 +96,8 @@ experience_builder.js_component.*:
       type: sequence
       sequence:
         type: mapping
+        constraints:
+          IsStorablePropShape: ~
         mapping:
           title:
             type: required_label

This then receives every key-value pair under props, so something like

$value = [
  'type' => 'string',
  'title' => 'Title',
  'examples' => ['Press', 'Submit now'],
];

(this is the example in \Drupal\Tests\experience_builder\Kernel\Config\JavaScriptComponentValidationTest::setUp()).

The validation constraint's validator then should do something like:

class IsStorablePropShapeConstraintValidator extends ConstraintValidator {

  /**
   * {@inheritdoc}
   */
  public function validate(mixed $value, Constraint $constraint): void {
    if (!is_array($value)) {
      // If the value is NULL, then the `NotNull` constraint validator will
      // set the appropriate validation error message.
      // @see \Drupal\Core\Validation\Plugin\Validation\Constraint\NotNullConstraintValidator
      if ($value === NULL) {
        return;
      }
      throw new UnexpectedTypeException($value, 'array');
    }

    // Verify an absolute minimum of an JSON Schema definition is present. If
    // not, do not perform further validation; it's up to the other validation
    // constraints to consider this overall value invalid.
    // @todo Ideally, we'd only validate this if and only if all key-value pairs in this mapping entry are valid. That requires conditional/sequential execution of validation constraints, which Drupal does not currently support.
    // @see https://www.drupal.org/project/drupal/issues/2820364
    if (!array_key_exists('type', $value)) {
      return;
    }

    if (PropShape::normalize($value)->getStorage() == NULL) {
      $this->context->buildViolation($constraint->message)
        // @todo look at \Drupal\Core\Validation\Plugin\Validation\Constraint\ValidKeysConstraintValidator for how to surface the key ("prop name") for which this occurred so you can include it in the error message
        …
        ->addViolation();
    }
  }

}
wim leers’s picture

Title: Allow adding image properties in the code editor » Allow adding "image" props in the code component editor
Issue summary: View changes
Issue tags: +Needs tests, +blocker, +Needs screenshots

Needs explicit test coverage, probably just a new case in \Drupal\Tests\experience_builder\Kernel\Config\JavaScriptComponentValidationTest::providerTestEntityShapes()?

Blocks #3507928, see #3507928-3: [Front-end] Allow adding "textarea" props in the code component editor.

Once the BE MR is in, the FE work can begin, after which a screenshot (or better yet: a screencast) is expected.

wim leers’s picture

Issue summary: View changes
wim leers’s picture

Title: Allow adding "image" props in the code component editor » [Back-end] Allow adding "image" props in the code component editor
wim leers’s picture

Issue tags: -sprint-candidate +sprint

f.mazeikis made their first commit to this issue’s fork.

wim leers’s picture

Status: Active » Needs work
Issue tags: -Needs tests

Tests are present 👍 CI results look promising. MR looks great, only nits! 😄

f.mazeikis’s picture

Status: Needs work » Needs review

Addressed nits, updated one failing test, waiting on CI.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

Just one last suggestion on the MR, but this is good to go!

f.mazeikis’s picture

Status: Reviewed & tested by the community » Fixed

Implemented the last suggestion, addressed failing CI on ESlint and merged.

wim leers’s picture

Title: [Back-end] Allow adding "image" props in the code component editor » [Front-end] Allow adding "image" props in the code component editor
Assigned: f.mazeikis » Unassigned
Status: Fixed » Active

Now ready for the front-end part!

wim leers’s picture

Issue summary: View changes
wim leers’s picture

Title: [Front-end] Allow adding "image" props in the code component editor » [Back-end] Allow adding "image" props in the code component editor
Assigned: Unassigned » f.mazeikis
Issue tags: +openapi

@f.mazeikis We forgot to update openapi.yml and expand \Drupal\Tests\experience_builder\Functional\XbConfigEntityHttpApiTest::testJavaScriptComponent() to test either what this MR landed, or what #3507928: [Front-end] Allow adding "textarea" props in the code component editor's back-end MR landed.

I vote explicitly testing type: object, $ref: json-schema-definitions://experience_builder.module/image in the internal HTTP API integration test.

wim leers credited tedbow.

wim leers’s picture

Looks like the components.schemas.CodeComponent in openapi.yml is not very precise currently (and it can't be due to #3503377: CI: introduce a nightly-only `slow-motion cypress E2E` CI job to auto-detect response latency-sensitive tests) and thanks to that, it looks like this wouldn't fail tests.

P.S.: it's thanks to @tedbow's work on #3509026: JavaScript code components slots' examples and description should be optional that I even realized #19, so crediting him for that 😄

wim leers’s picture

Title: [Back-end] Allow adding "image" props in the code component editor » [Front-end] Allow adding "image" props in the code component editor
Assigned: f.mazeikis » balintbrews
Issue tags: -openapi

balintbrews made their first commit to this issue’s fork.

wim leers’s picture

Assigned: balintbrews » wim leers
Status: Active » Needs work

@balintbrews was hitting

AssertionError: assert($componentSource instanceof UrlRewriteInterface) in assert()

Analyzed it. Conclusion:

  1. $default_resolved_value is computed correctly, and is set to the absolute image URL that was specified as the default
  2. … but ::getClientSideInfo() doesn’t yet pass that to the client, that’s up to #3493943: SDC+JS Component Sources: split default values into `resolved` and `source` to do! 😬
  3. so the client cannot send the resolved value to the server, because it simply doesn’t exist on the client
  4. … which then hits the UrlPreviewPropSource fallback case in GeneratedFieldExplicitInputUxComponentSourceBase::clientModelToInput() … but that is designed to only be created when there’s a need for actual rewriting
  5. I’ll work around it by implementing adding a no-op JsComponent::rewriteExampleUrl() with a @todo pointing to #3493943

wim leers’s picture

Assigned: wim leers » balintbrews
Status: Needs work » Needs review
Related issues: +#3493943: SDC+JS Component Sources: split default values into `resolved` and `source`
StatusFileSize
new291.29 KB

Done. This is the very first code component containing an image ever rendered — forgive the ugliness of my test content 🤣

I'll let @balintbrews record a magnificent screencast instead 😇😜

P.S.: all of this is only possible thanks to the work @longwave and I did on #3501902: Adding the Image component results in a state considered invalid 🥳
P.P.S.: @longwave +1'd #25's approach — crediting him.

balintbrews’s picture

lauriii’s picture

#27 😱😱😱🎉🎉🎉👏👏👏

wim leers’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs screenshots

wim leers’s picture

Assigned: balintbrews » Unassigned
Status: Reviewed & tested by the community » Fixed
wim leers’s picture

Title: [Front-end] Allow adding "image" props in the code component editor » Allow adding "image" props in the code component editor

Both pieces are in :)

balintbrews’s picture

Issue tags: -blocker
StatusFileSize
new25.47 MB

Code component image props

nagwani’s picture

Issue tags: -sprint
nagwani’s picture

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.