Allow adding image properties in the code editor

Created on 20 February 2025, 3 months ago

Overview

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

Proposed resolution

Introduce a new image property type that can be used in the code editor.

User interface changes

โœจ Feature request
Status

Active

Version

0.0

Component

Theme builder

Created by

๐Ÿ‡ซ๐Ÿ‡ฎFinland lauriii Finland

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @lauriii
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    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
    
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    ( โœจ Config entity for storing code components Active heavily restricted what props you're allowed to use.)

  • ๐Ÿ‡ซ๐Ÿ‡ฎFinland lauriii Finland

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

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    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();
        }
      }
    
    }
    

    Look at

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

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

    Blocks #3507928, see #3507928-3: Allow adding textarea in the code editor โ†’ .

    Once the BE MR is in

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom f.mazeikis Brighton

    f.mazeikis โ†’ made their first commit to this issueโ€™s fork.

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Tests are present ๐Ÿ‘ CI results look promising. MR looks great, only nits! ๐Ÿ˜„

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom f.mazeikis Brighton

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

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

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

  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom f.mazeikis Brighton

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

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Now ready for the front-end part!

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    @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 โœจ Allow adding textarea in the code editor Active '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.

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States tedbow Ithaca, NY, USA
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Looks like the components.schemas.CodeComponent in openapi.yml is not very precise currently (and it can't be due to ๐Ÿ“Œ CI: temporary intervention to avoid response latency-sensitive E2E tests from failing Active ) and thanks to that, it looks like this wouldn't fail tests.

    P.S.: it's thanks to @tedbow's work on ๐Ÿ› Javascript code components props, examples and description should be optional Active that I even realized #19, so crediting him for that ๐Ÿ˜„

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • First commit to issue fork.
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    @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 ๐Ÿ“Œ Split model values into resolved and raw Active 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
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom longwave UK
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    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 ๐Ÿ› Adding the Image component results in a state considered invalid Active ๐Ÿฅณ
    P.P.S.: @longwave +1'd #25's approach โ€” crediting him.

  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands balintbrews Amsterdam, NL
  • ๐Ÿ‡ซ๐Ÿ‡ฎFinland lauriii Finland

    #27 ๐Ÿ˜ฑ๐Ÿ˜ฑ๐Ÿ˜ฑ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰๐Ÿ‘๐Ÿ‘๐Ÿ‘

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • Pipeline finished with Skipped
    3 months ago
    #436061
  • Pipeline finished with Skipped
    3 months ago
    #436063
  • Pipeline finished with Skipped
    3 months ago
    #436064
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Both pieces are in :)

  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands balintbrews Amsterdam, NL

  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia nagwani
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia nagwani
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024