[2.0.x] Update ui_patterns_library and rethink patterns previews

Created on 26 January 2023, almost 2 years ago
Updated 18 June 2024, 5 months ago

Problem/Motivation

When we define a pattern we can set a preview value for each field (and setting) which is going to be used by the \Drupal\ui_patterns\Element\PatternPreview render element to generate the markup show in the /patterns library. One version of each patterns is displayed for each of their variants.

This is a good start but it does not allow to illustrate the complexity of some patterns and rise a few issues that we could try to tackle.

1. previews variations

In the real life usage all the fields and settings are not mandatory so the current way of generating preview does not cover all use cases. If a themer needs to design all cases of a component they'll have to change the preview settings of the pattern multiple times, clearing cache between every change, to be able to see how their changes are rendered.

2. previews declarations

Another issue is that, the preview values to define are not easily usable for themers that doesn't know the render array system (why would they?). The current system also leads to issues that are often solved by using preprocess hooks (see Reduce preprocess hooks usage by adding add_class() & set_attributes() filters Fixed ) and that could be simplified by rethinking how these values are generated.

3. previews usage

Given the nature of our patterns, we often need to nest patterns inside other patterns to cover our design cases. For this purpose, we can use the pattern_preview render element. One issue is that this element has no settings so override the previewed fields for a specific case. Plus, if we allow themers to define more previews variations as told above, we'll need to also allow them to select which variation they need in their preview.

Pattern example

Let's use this pattern as an example.

captioned_image:
  label: Captioned image
  variants:
    full_width:
      label: Full width
    hero:
      label: Hero picture
  settings:
    collapsed_legend:
      label: Collapsed legend
      type: checkbox
      default_value: false
      preview: true
  fields:
    title:
      label: Title
      type: string
      preview: 'My picture'
    image:
      label: Image
      type: string
      preview:
        - '#theme': 'image'
          '#uri': 'templates/patterns/molecules/captioned_image/demo_img.jpg'
    credits:
      label: Credits
      type: string
      preview: 'by John Doe'
    legend:
      label: Legend
      type: string
      preview: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sodales libero metus.'
    transcript:
      label: Transcript text
      type: string
      preview:
        - '#markup': '<h2>Lorem ipsum dolor sit amet</h2><p>Suspendisse molestie placerat nulla non bibendum. Sed scelerisque ac eros sed porttitor. Aliquam luctus pulvinar pellentesque. Proin congue purus augue, non fringilla erat rutrum dapibus. Curabitur in vulputate sapien. Donec pellentesque sagittis justo sed congue. Integer sodales finibus ante, sit amet hendrerit quam aliquet sed. Integer arcu orci, lobortis id vulputate vitae, scelerisque non dui. Integer quis ligula non tortor iaculis eleifend. Morbi convallis diam ac mi pellentesque, sed commodo nunc porttitor.</p>'

Proposed resolution

We want to expand the options given to themers in their work process but we need to maintain Backward Compatibility in a context where no hook_update could build an upgrade path.

1. previews variations

Without removing the existing way of defining the preview values of the fields, we could add a new section in the YML definition called previews which would allow to set as many previews as we need. Each of these previews should be named to ease their usage afterwards. They might also have a label and a description in case themers need to explain a bit which case is covered by this specific preview.

For example:

captioned_image:
  label: Captioned image
  variants:
    [...]
  settings:
    [...]
  fields:
    [...]
  previews:
    image_only:
      label: Image only
      variant: full_width
      settings:
        collapsed_legend: false
      fields:
        image:
          - '#theme': 'image'
            '#uri': 'templates/patterns/molecules/captioned_image/demo_img.jpg'
    full_display_collapsed:
      label: Full display collapsed
      variant: hero
      description: 'Image with all fields (title, credits, legend and transcript) collapsed'
      settings:
        collapsed_legend: true
      fields:
        title: 'Beautiful image'
        image:
          - '#theme': 'image'
            '#uri': 'templates/patterns/molecules/captioned_image/demo_img2.jpg'
        credits: '© 2023'
        legend: 'Wonderful sunset over the mountains'
        transcript:
          - '#markup': '<h2>Lorem ipsum dolor sit amet</h2><p>Suspendisse molestie placerat nulla non bibendum. Sed scelerisque ac eros sed porttitor. Aliquam luctus pulvinar pellentesque. Proin congue purus augue, non fringilla erat rutrum dapibus. Curabitur in vulputate sapien. Donec pellentesque sagittis justo sed congue. Integer sodales finibus ante, sit amet hendrerit quam aliquet sed. Integer arcu orci, lobortis id vulputate vitae, scelerisque non dui. Integer quis ligula non tortor iaculis eleifend. Morbi convallis diam ac mi pellentesque, sed commodo nunc porttitor.</p>'

2. previews declarations

We should assume that themers don't know the Render API and its render arrays. We need to provide them an easy way to declare the preview values. We could take advantage of the unused types of the fields to define what's expected and react accordingly.

For example:

captioned_image:
  label: Captioned image
  variants:
    [...]
  settings:
    [...]
  fields:
    title:
      label: Title
      type: string
      preview: 'My picture'
    image:
      label: Image
      type: image
      preview: 'demo_img.jpg'
    credits:
      label: Credits
      type: string
      preview: 'by John Doe'
    legend:
      label: Legend
      type: string
      preview: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sodales libero metus.'
    transcript:
      label: Transcript text
      type: markup
      preview: '<h2>Lorem ipsum dolor sit amet</h2><p>Suspendisse molestie placerat nulla non bibendum. Sed scelerisque ac eros sed porttitor. Aliquam luctus pulvinar pellentesque. Proin congue purus augue, non fringilla erat rutrum dapibus. Curabitur in vulputate sapien. Donec pellentesque sagittis justo sed congue. Integer sodales finibus ante, sit amet hendrerit quam aliquet sed. Integer arcu orci, lobortis id vulputate vitae, scelerisque non dui. Integer quis ligula non tortor iaculis eleifend. Morbi convallis diam ac mi pellentesque, sed commodo nunc porttitor.</p>'

From my little experience we have three very common types:

  • string: only text content is expected and no HTML should be rendered.
    Nothing to change for this one.
  • markup: any HTML should be rendered as is (we might give it a little round of Xss::filter though).
    We would just need to get the string from the YML file, filter it and feed it into a Markup object before rendering it.
  • image: we might be able to easily declare which image to use.
    This one is probably the trickiest. I'd say that the given input is considered as a path or an URL. If the path is an URL just use it. If it's an absolute path, start from Drupal's root. If it's a relative path, find the file in a set of directories (first, pattern directory, then theme one, then Drupal root).

There might be other cases to cover if more usage come so I'd suggest to implement these processors as plugins that could be extending by anyone.

For Backward Compatibility:

  • any render array should be kept as declared
  • the "string" plugin should be the fallback one if none or an incorrect one are declared

Obviously, what works in the fields declaration should also work in the standalone preview declarations as suggested in the previous point.

Note/warning: each fields could contain multiple elements so we'll need to find a way to handle differently a render array that a render of values that needs to be processed.

3. previews usage

The pattern_preview render element is used either by a direct call from a pattern preview definition or, less likely, withing a Twig template using the {{ pattern_preview(...) }} function (which is a wrapper for the render element anyway).

Current usage example:
{{ pattern_preview('captioned_image', 'hero') }}

First of all, we should allow someone to override preview fields in this call so they can have the more appropriate values for their preview case.

For example:
{{ pattern_preview('captioned_image', {'transcript': ''}, 'hero') }}

Then, we should allow to use the same syntax as seen in the previous point to override these fields.

For example:
{{ pattern_preview('captioned_image', {'image': 'other_picture.png'}, 'full_width') }}

Finally, we might add some extra parameter to allow selecting a specific preview in all available previews declared according to the first point of this plan. As a bonus, it would be really great to have a way to ask for a random preview in the available ones (think about the grid/slideshow pattern in which you'd like to have a few variety).

For example:

{{ pattern_preview('captioned_image:image_only') }}
or
{{ pattern_preview('captioned_image:_any_') }}

Obviously, all these should be usable at the same time:
{{ pattern_preview('captioned_image:_any_', {'credits': '© Me'}, 'full_width') }}

Remaining tasks

Discuss, split in subtasks, implement

User interface changes

/patterns library should evolve to display the previews as defined in the first point.

API changes

Fields could be added to {{ pattern_preview() }} and "#type" => 'pattern_preview' calls.
Preview "id" could be specified in the two call above.

Data model changes

None

🌱 Plan
Status

Closed: duplicate

Version

2.0

Component

UI Patterns Library

Created by

🇫🇷France duaelfr Montpellier, France

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

Comments & Activities

  • Issue created by @duaelfr
  • 🇫🇷France pdureau Paris

    Hello Duael,

    Thanks for this detailed proposal.

    1. previews variations

    100% agree with this:

    We need many previews of a pattern

    • The current situation of adding the previews inside the field definition is limiting us to a single preview per pattern
    • So, we need to add a new section in the pattern definition with as many named previews as we need
    • We are not removing the existing preview property from the field definition

    Some feedbacks:

    • What are we doing with the existing field definition preview properties? Maybe we should reserve the keyword "default" for one of the previews which would consist of the agglomeration of the previews of each fields & settings
    • It would be nice to create a ComponentPreviewDefinition class like ComponentFieldDefinition and ComponentVariantDefinition, and make it compatible with Drupal\ui_examples\Definition\ExampleDefinition for consistency in the UI Suite ecosystem.

    2. previews declarations

    We could take advantage of the unused types of the fields to define what's expected and react accordingly.

    This is the part I am the least comfortable of, using the field types. Because, fields are slots and slots are not typed. I would prefer to remove the type property than doing magic on it. And are we sure "markup" or "image" keyword are not conflicting with existing or future usage? how many types will become keywords like that in the future?

    I understand the big issue is the developer experience related to the Drupal Render API. It drives me nut too. I was so desperate that i worked on this issue: 🌱 Improve DX with render alias Closed: works as designed

    Because the existing render elements are not friendly, it may be time to create new render elements, which will be easy to add in UI Patterns previews and be proper render element usable elsewhere. Examples:

    I know it is a bold proposal, but at least we are using the existing render API and we can still mix with teh existing render arrays.

    Note/warning: each fields could contain multiple elements so we'll need to find a way to handle differently a render array that a render of values that needs to be processed.

    Is it related to Make the Twig loops safer Postponed ?

    3. previews usage

    First of all, we should allow someone to override preview fields in this call so they can have the more appropriate values for their preview case. Then, we should allow to use the same syntax as seen in the previous point to override these fields. Finally, we might add some extra parameter to allow selecting a specific preview in all available previews declared according to the first point of this plan. Obviously, all these should be usable at the same time

    I love that. 100% agreeing I would not change a word :)

    As a bonus, it would be really great to have a way to ask for a random preview in the available ones (think about the grid/slideshow pattern in which you'd like to have a few variety).

    Indeed. I haven't tried but it seems Wingsuit (which is extending UI Patterns definitions) is doing something this using Faker JS library: https://wingsuit-designsystem.github.io/components/wingsuit/

    preview:
          faker:
            token: "{{ name.lastName }} {{ name.firstName }}
    

    We can ask Christian about feedbacks.

  • 🇫🇷France Grimreaper France 🇫🇷

    Hi,

    Thanks both for your thoughts about that.

    1. previews variations

    Ok

    Why not introduce the previews rework in UI Patterns 2.0 so no backward compatibility to maintain?

    About Pierre's feedback:
    - ComponentPreviewDefinition: Not sure about the need to make it compatible with ui_examples, would this make ui_examples a dependency of UI Patterns or the opposite?
    -

    those preview must be variant agnostic, in order to loop, in the pattern library, first on each variant ; then, for each variant, on each preview. In order to have a full rendering.

    : I see the will for patterns with a lot of variants to have to avoid to declare a lot of previews but there are sometimes patterns with variants where options are useful for some variants in particular. So I would say if a preview do not have a "variant" declared we loop on all, but if the variant is specified no loop on all variants.

    2. previews declarations

    This should be a dedicated issue and improved in a second time.

    Also this will go against the will to use the fields type as documentation. See screenshot

    About Pierre's feedback:
    10000% agree, I prepared my feedback before reading Pierre's one! :)

    3. previews usage

    Ok.

    A little bit skeptical about the random preview

    Why not introduce the previews rework in UI Patterns 2.0 so no backward compatibility to maintain?

  • 🇫🇷France Grimreaper France 🇫🇷

    In my previous comment I forgot the screenshot.

  • 🇫🇷France duaelfr Montpellier, France

    As far as I remember there was no wide agreement on using fields types like this. That slide was quickly presented to everyone but I don't recall we had the time to think about it and argue for or against. Is there an issue where the typing is discussed?

  • 🇫🇷France pdureau Paris

    As far as I remember there was no wide agreement on using fields types like this. That slide was quickly presented to everyone but I don't recall we had the time to think about it and argue for or against. Is there an issue where the typing is discussed?

    Indeed, this slide was just an open question during a monthly meeting. Nothing was deeply discussed, nothing was decided.

    This is an other subject, not directly related to the current issue. Today, the field typings of our UI Patterns implementations are a mess. Just random keywords put here by the developers, without a consistent nomenclature. I wanted to set a meeting to decide a set of keywords, to share as good practice, for documentation purpose.

  • 🇫🇷France duaelfr Montpellier, France
  • 🇫🇷France duaelfr Montpellier, France
  • Assigned to pdureau
    • pdureau committed 2d703620 on 2.0.x
      Issue #3336654 by pdureau: [2.0.x] Update ui_patterns_library and...
    • pdureau committed 83dcb7af on 2.0.x
      Issue #3336654 by pdureau: loop on stories instead of variant to allow...
    • pdureau committed b5b9e5a6 on 2.0.x
      Issue #3336654 by pdureau: Add link to replaced component in library...
  • Issue was unassigned.
  • Status changed to Closed: duplicate 5 months ago
  • 🇫🇷France pdureau Paris

    Ongoing work in other issues.

Production build 0.71.5 2024