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 #3311480: Reduce preprocess hooks usage by adding add_class() & set_attributes() filters) 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
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | Capture d’écran de 2023-03-20 15-05-14.png | 124.86 KB | grimreaper |
Comments
Comment #2
pdureau commentedHello Duael,
Thanks for this interesting and detailed proposal.
1. previews variations
100% agree with you:
Some feedbacks:
2. previews declarations
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: #3319247: Improve DX with render alias
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.
Is it related to #3342390: Make the Twig loops safer ?
3. previews usage
I love that. 100% agreeing.
What will be the render array equivalent of this Twig function?
We have this on ui_patterns today:
So, i guess it will look like that:
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/
We can ask Christian about feedbacks.
Comment #3
grimreaperHi,
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?
-
: 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?
Comment #4
grimreaperIn my previous comment I forgot the screenshot.
Comment #5
duaelfrAs 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?
Comment #6
pdureau commentedIndeed, 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.
Comment #7
duaelfrComment #8
duaelfrComment #9
pdureau commentedComment #13
pdureau commentedOngoing work in other issues.