- Issue created by @wim leers
- 🇺🇸United States effulgentsia
We need to allow SDCs to specify they accept HTML markup in a particular prop
From the perspective of an SDC author, what's the difference between an HTML prop and a slot? What are example use-cases for an HTML prop and why shouldn't that be a slot instead?
Introduce a new type: string, format: markup
If we decide to go with this option, I suggest
format: html
rather thanformat: markup
.this string must start and end with an angular bracket
Not all HTML fragments start and end with brackets. E.g.,
hello world
,<em>hello</em> world
, andhello <em>world</em>
are all valid HTML fragments. - 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
Fair questions. I’ve seen @lauriii state that this should be a prop, not a slot. So I considered that not a question to ask, but a given.
This is already assigned to Lauri to get clarification on how this should work, now there’s just more to explain his vision for. 👍
- 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
Important: at least the current design does include a rich text editor/CKEditor 5 instance:
- Issue was unassigned.
- 🇫🇮Finland lauriii Finland
I was imagining this would be a prop because a) it's not any HTML that the prop allows, it's HTML generated by a rich text editor that's allowed b) slots are considered separate from props which would make generating a form pretty complex.
What comes to configuring the prop, it seems that
type: string, pattern: ^\<.*\>$
is also pretty bad for developer ergonomics, and thattype: string, format: markup
or type: string, format: html would be much better from that perspective. - 🇺🇸United States effulgentsia
it's not any HTML that the prop allows, it's HTML generated by a rich text editor that's allowed
Does it make sense for the SDC creator to decide that? A rich text editor can generate almost any HTML (e.g., if you use Drupal's "Full HTML" format), so what exactly is the decision that the SDC creator is making when saying something is an HTML prop vs. a slot? I.e., imagine someone creating a component library in Storybook and not thinking about Drupal concepts: for this person, what does it mean for something to be a slot? what does it mean for something to be an HTML prop?
- Assigned to lauriii
- 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
#6:
[…]
type: string, format: markup
or type: string, format: html would be much better from that perspective.Agreed it'd be better.
But it's not part of the JSON schema spec: https://json-schema.org/understanding-json-schema/reference/string#built... — we'd have to define this ourselves. And if then define it in some particular way, that brings me to #7:
#7++ — that's the part that's fuzzy to me too: what is the intent? What constraints does an SDC developer want?
- 🇫🇮Finland lauriii Finland
#7: The SDC schema is not generic in the sense that other systems would be able to read it without a specific Drupal integration. In the process where you have pre-existing components that may exist in a component library utilizing storybook, you would still have an integrator specifying the SDC schemas for the Drupal integration, which is when these types of decisions would be made. In Twig there are some interesting aspects to this, such as that slots have their own syntax, meaning that some of these decisions may be made upfront. I believe it is possible to expose a slot as a prop within a component without modifying the original code but not vice versa.
I think using either props or slots could be right depending on the use case. Here's two examples that demonstrate this.
Example 1:
Let's imagine a frontend developer received this as a design for them to implement as a component. Because the text is using bold and italic, would they think automatically that they should be using a slot that could allow variety of inputs? I personally don't think so because this would always be a short text that could have some formatting. Because HTML is somewhat arbitrary and generally wouldn't be used for defining logic, they could potentially define this as a slot that only allows a paragraph component but that would mean that the content is authored via a separate component.
Example 2:
Let's imagine we now have this as a design for them to implement as a component. The text has formatting but based on this design, we can expect this layout to also allow content creators using elements other than the text. We can se an example of button being shown in the design. I think in this case it would most likely make sense to use slots that provide content creators with more flexibility to use the space.
- Issue was unassigned.
- 🇫🇷France pdureau Paris
Hi all,
I was imagining this would be a prop because a) it's not any HTML that the prop allows, it's HTML generated by a rich text editor that's allowed b) slots are considered separate from props which would make generating a form pretty complex.
As effulgentsia asked: "Does it make sense for the SDC creator to decide that?"
In my opinion, no, it doesn't. The component author doesn't have to care about rich text editors and other CMS stuff.
The component author is building a pure UI data model, without any applicative or business considerations, where he needs to pick the right type for each data he is expecting in the template.
This is not rocket science. If there is markup, it is a slot. I would advice to keep
string
prop for plain text and move on. - 🇫🇮Finland lauriii Finland
@pdureau I think we might be talking past each other because I think you could still define component libraries and design systems that are free of business considerations. I could imagine with the example that I provided in #10 that organizations with more experience in design systems could own two components; one that's free from business considerations and one that includes business considerations. Only the one that includes business considerations would be exposed to content creators within XB. An organization could decide to share either one or both between sites depending on your particular needs. What I'm arguing is that frontend developers should be able to define these constraints in a way that is natural to them, which would be in
*.component.yml
. - 🇫🇷France pdureau Paris
Hi Laurii,
I think we might be talking past each other
Indeed, our comments have crossed in the thread.
Thanks for those interesting examples.
Example 1: Because the text is using bold and italic, would they think automatically that they should be using a slot that could allow variety of inputs?
The prop type described in the issue will also allow users to inject any markup, like an other component markup.
Of course, you can prevent this in the Experience Builder UI, by allowing only a simple WYSIWYG for this prop type, but it doesn't change the reality of the component, and the other usages (custom PHP code, twig includes, other display builders...) it will get. It is like using a Field Widget instead of a Field Type to define and check the field's stored data.
If a designer really want to enforce this specific design (with one line of italic, and one line of italic + bold), it becomes a part of the component itself. So, 2 string props, one for each line, seems to be a food solution.
that organizations with more experience in design systems could own two components; one that's free from business considerations and one that includes business considerations. Only the one that includes business considerations would be exposed to content creators within XB.
I am not sure I am understanding this idea. The business-related UI component will call/embed the business-agnostic UI component ?
- 🇫🇮Finland lauriii Finland
I am not sure I am understanding this idea. The business-related UI component will call/embed the business-agnostic UI component ?
Yes 👍 It could look like something like this:
The business agnostic card in the design system which would work in XB, but would not result in optimal editorial experience for a content creators:
<div class="card"> <div class="card__image"> {% block image %} {% endblock %} </div> <div class="card__title"> {% block title %} {% endblock %} </div> <div class="card__body"> {% block body %} {% endblock %} </div> </div>
A site specific card component with business requirements accounted for resulting in better experience for a content creator:
{% embed "design_system:card" %} {% block image %} {{ include "experience_builder:image" with { ...image } only }} {% endblock %} {% block title %} <h3>{{ title }}</h3> {% endblock %} {% block body %} {{ body }} {% endblock %} {% endembed %}
- Assigned to lauriii
- 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
@lauriii: your response in #10 is helpful, but if both examples listed the slots + props (and their schemas) you'd expect, then it'd help guide this conversation much more. 🙏
@pdureau in #12:
The component author doesn't have to care about rich text editors and other CMS stuff.
This is counter to the vision @lauriii has expressed, and would violate many of the 64 product requirements.
A rich text editor is associated with "markup", and specifically "restricted markup". This is critical. As a SDC component author, I'd expect to be able to express that in a particular markup prop, only
<strong>
and<em>
are allowed, but in another prop only is allowed. That's what @lauriii is saying in #13:[…] What I'm arguing is that frontend developers should be able to define these constraints in a way that is natural to them, which would be in
*.component.yml
.If this is to be represented by a slot, then the "per-slot tag/category-based restrictions" part becomes critically important (see [#15637196-3] and 📌 [SPIKE] Comprehensive plan for integrating with SDC Active ).
@pdureau in #14:
Of course, you can prevent this in the Experience Builder UI, by allowing only a simple WYSIWYG for this prop type, but it doesn't change the reality of the component, and the other usages (custom PHP code, twig includes, other display builders...) it will get.
I think this is the fundamental mismatch that's causing the misunderstanding. "just a simple WYSIWYG" is not precise enough. The prop's schema should be able to optionally convey exactly which tags are allowed. At least, that's my hunch. I'm looking to product manager @lauriii to clarify his product vision & requirements 🤓
[…] So, 2 string props, one for each line, seems to be a good solution.
That would indeed be the other possible solution. I'd be fine with that too. I defer to @lauriii.
I added a third proposal to the issue summary for the syntax to describe HTML restrictions. Very curious what y'all think!
P.S.: this entire discussion has a strong connection to 📌 Document supported component modeling approaches Active .
- 🇺🇸United States effulgentsia
they could potentially define this as a slot that only allows a paragraph component but that would mean that the content is authored via a separate component.
I don't think this is a limitation. I think we can support in XB the concept of slots whose values are populated by the value of a rendered field type (such as
text_long
) or field (such asfield_body
), so that the content author doesn't see it as a separate component but rather as indistinguishable from a prop. The question then becomes where should the information live that a particular slot should be treated this way within XB.What I'm arguing is that frontend developers should be able to define these constraints in a way that is natural to them, which would be in *.component.yml.
But what is the constraint that the component.yml should specify? That the slot should be constrained to "rich text" only and leave it to the system to decide what that means in terms of allowed HTML tags? Should this basically be the same as "all inline HTML tags" or would it need to vary by site or by component or be tied to Drupal's text formats?
- 🇺🇸United States effulgentsia
#18 was a cross-post with #16/#17. If we go with the
enum
approach in Proposed resolution #3, then that addresses all of my concerns with it being a prop. Perhaps it would even make sense to name itformat: rich_text
instead offormat: markup
for even more guidance as to what would typically be appropriate for theenum
? Also, presumably we could create some default JSON schema definitions for commonenum
sets that a component can reference via$ref
? - 🇫🇷France pdureau Paris
This is counter to the vision @lauriii has expressed, and would violate many of the 64 product requirements (...) As a SDC component author, I'd expect to be able to express that in a particular markup prop, only and are allowed, but in another prop only is allowed. (...) The prop's schema should be able to optionally convey exactly which tags are allowed.
Thanks for making this explicit. However, I don't recognize here the design system philosophy and the separation of concerns I am promoting. So, I have nothing more to add than the warnings I have already shared.
I am out of this thread, but I am always happy to discuss about other SDC & XB subjects ;)
- 🇺🇸United States effulgentsia
enum: - <p> - <br> - <a hreflang href> - <ul type> - <ol start type> - <strong> - <em>
Is there a reason for the SDC to constrain the attributes? I'm not clear why that would be relevant to the component's design.
If XB then uses a
text_long
field for authoring, then that field's filter format will be what restricts the attributes, but that's a separate concern from what the component cares about.This, however, raises the question of what if there's a mismatch between the component prop's allowed tags and the filter format's allowed tags?
- If the component prop allows tags that the filter format doesn't, that's fine, it doesn't break any validation: just because the component's design allows something, doesn't mean the content author has to use it.
- But if the filter format allows tags that the component prop doesn't, then it gets trickier. We'd need to get the processed text out of the text field and then strip the tags not allowed by the component prop. Which is fine from a technical standpoint, but probably not great UX (what you see in the CKEditor wouldn't match what you'd get in the rendered output). Better would be to set CKEditor's ACF to the intersection of what's allowed by the filter format and what's allowed by the component's prop.
- 🇺🇸United States effulgentsia
I don't recognize here the design system philosophy and the separation of concerns I am promoting.
I care about design system philosophy and separation of concerns. Do you think proposed resolution #3 achieves that? As I understand it:
- If the SDC wants to specify "more or less any HTML can go here", then that's a slot.
- If the SDC wants to specify "more or less only formatted text can go here", then that's a prop, with a way to specify which formatting tags are allowed.
Or does the above not match how components for design systems are actually designed?
- 🇫🇮Finland lauriii Finland
Is there a reason for the SDC to constrain the attributes? I'm not clear why that would be relevant to the component's design.
I didn't mean this level of configurability by my comment in #13. At most, I could see there there to be a use case for two different levels of RTE for components; inline vs block (inline wouldn't wrap text in
<p>
).I personally have a slight preference to the prop solution because I believe that's closer to what other platforms are doing. Pretty much all of the ones I looked at provide RTE as a unique prop type. For example:
- Paragraphs uses RTE as a field but has the concept of slots through entity references
- Wordpress Gutenberg uses
type: 'rich-text'
- Shopify uses
type: 'richtext'
andtype: 'inline_richtext'
- Builder.io uses
type: 'richText'
- Plasmic uses
type: 'string', control: 'large'
- Framer uses
type: ControlType.RichText
That said, if we find a way to provide a decent DX by using slots, I'd be totally fine with that. I'm not sure I fully understand what's the benefit of that so maybe it would be better understand what are the differences that allow it to provide a better experience.
I'm wondering if we should ship with two pre-configured text formats that are optimized to XB by us, and are always used for the XB components. The text formats can be modified but not deleted.
Block text format would come with the following buttons and would wrap text in
<p>
:- Bold
- Italic
- Underline
- Link
- Unordered list
- Ordered list
And the inline text format would come with following buttons but would not wrap any text in
<p>
:- Bold
- Italic
- Underline
- Link
This would allow us to optimize the experience so that the block text allows input from regular RTE fields using a token. However, the inline text would only allow input from string types.
- 🇺🇸United States Kristen Pol Santa Cruz, CA, USA
Big discussions :)
Is this targeted for Barcelona?
Is the best way to see what's for Barcelona here?
https://www.drupal.org/project/experience_builder/issues/3454094 🌱 Milestone 0.1.0: Experience Builder Demo Active
Should there be tags like "Barcelona Blocker" or similar for must haves for Barcelona?
- 🇺🇸United States effulgentsia
I thought more about the prop vs slot conundrum, and unfortunately I've come back around to favoring slots for this. I say unfortunately, because it will mean that this issue will take longer to get done, but I think it's worth it.
To be fair, I think there are two quite reasonable approaches for how to model the props vs slots distinction, one that leads to modeling rich text as a prop and one that leads to modeling rich text as a slot. Although both are reasonable, I think the slot approach is better.
Approach 1:
HTML web components are about the closest actual standard that exists that's closely related to the props vs slots concept. Within web components, you have attributes (which are a lot like props) and slots, and you can only pass HTML content through slots. I think this is a strong point in favor of us modeling SDC props and slots the same way. For example, it would make it easier to in the future (e.g., in 🌱 [META] Support component types other than SDC Needs work ) support implementing SDC-like components as web components. Also, because web components are a standard, all major JS component frameworks (React, Vue, Svelte, etc.) support this way of distinguishing props and slots (even in React, where it all ends up as just props, there's docs and libraries for how to map to React props if you're coming from a web components props vs slots mindset).Approach 2:
We can define SDC slots as things that you put other SDCs (or other kinds of components once 🌱 [META] Support component types other than SDC Needs work is done) into, and since content coming from a rich text editor isn't itself a component, then it should be modeled as a prop. This is the approach / mental model implied in #10, and which according to #23 is adopted by various other content management tools.However, we don't need to adopt approach #2 in order to have the UX we want. In 📌 [SPIKE] Comprehensive plan for integrating with SDC Active we're already surfacing the need for various ways we need to express slot restrictions. Whatever approach we come up with for that, I think we can extend to also include being able to express slot restrictions as HTML tag restrictions. And if a slot restriction is expressed as HTML tag restrictions rather than as component restrictions, then we can infer that we want to give content authors a rich text editor for that instead of letting them drag other components into that slot.
Following approach 1, this would avoid the need for us to figure out how to express the concept of rich text and tag restrictions within JSON schema. Instead we can invent how to do this within the concept of slot restrictions which is something we're making up all of the syntax for anyway.
However, a downside of approach 1 is that we'll need to evolve our data model. Currently our data model consists of a clean separation between the tree (which deals with slots) and prop values (which deals with props). That would need to evolve to where the "prop values" can also include the rich text values that populate slots, at which point we might need to rename that from "prop values" to just "values".
While I don't think that extending our future implementation of slot restrictions and our current data model will add an unreasonable amount of work, I think it will likely add enough to not make it in by the 0.1.0 release that we're targeting for Barcelona. I'm not sure what that means if we want the 0.1.0 release to have the ability to populate components with rich text. Maybe there's something we can hack in as a temporary way to achieve that?
- 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
#18:
I think we can support in XB the concept of slots whose values are populated by the value of a rendered field type (such as
text_long
) or field (such asfield_body
)😨 at the SDC level? Or at the level where an SDC has been "imported" into XB? (i.e. after a
Component
config entity is created for it in the implementation as of today) Because tying to specific field types will require SDC developers to know about field types: bad! And allowing SDC developers to tie to specific field instances is even more concerning — especially if it's done by name.#19: 👍
#20:
I don't recognize here the design system philosophy and the separation of concerns I am promoting
That sounds very concerning 😳
I think you and @lauriii should meet to get to the bottom of this.
#21:
RE: constraining attributes — maybe that could be omitted, but I figured that some component may expect only
<ul>
, no<ul type>
? Or only<a href>
but not<a href target>
?then that field's filter format will be what restricts the attributes
No, we'd have to configure whichever text field to precisely match the constraints imposed by the SDC metadata. Chances of a site's pre-existing text formats nicely matching the HTML restrictions imposed by SDC authors seems slim? IOW: auto-generate a "format" in place, without it creating a
FilterFormat
config entity.#23: "ship with two pre-configured text formats" — interesting! That'd sure simplify things. I like this a lot. That removes the very granular configurability I mentioned.
#24: yes, 🌱 Milestone 0.1.0: Experience Builder Demo Active is the place to look at. This should've been on the project page eons ago. Fixed that now.
We're not using tags. We'd be tagging a LOT. We're just using priority for what's critical right now, and currently, thats #3454094.
#25:
💯
Following approach 1, this would avoid the need for us to figure out how to express the concept of rich text and tag restrictions within JSON schema. Instead we can invent how to do this within the concept of slot restrictions which is something we're making up all of the syntax for anyway.
👍
However, a downside of approach 1 is that we'll need to evolve our data model. Currently our data model consists of a clean separation between the tree (which deals with slots) and prop values (which deals with props). That would need to evolve to where the "prop values" can also include the rich text values that populate slots, at which point we might need to rename that from "prop values" to just "values".
I don't think this is necessarily true.
First, an important question. When putting rich text (HTML markup) in a slot, is that:
- a single rich text "blob"
- multiple rich text "blobs"
- rich text "blobs" and components, intermixed?
Regardless of the answer to that, I think we can create a special "semi-internal"
experience_builder:rich_text
SDC that gets special treatment. We place that intree
, and then we can continue to useprops
as-is.I think it will likely add enough to not make it in by the 0.1.0 release that we're targeting for Barcelona
+1. Less than a month prior to DrupalCon, and we don't even know for sure what it would look like technically. Whatever we'd start building now, it'd very tight, because we'd have to do a fair amount of experimenting.
We have plenty of polishing to do before then.
If we're continuing down the path @effulgentsia so eloquently paints in #25, we'll need an addition/clarification for 📌 [SPIKE] Comprehensive plan for integrating with SDC Active : we'll need SDCs to be able to express:
- if the slot accepts inline or block elements
- if it accepts block elements: what component restrictions and/or arbitrary markup restrictions apply
- if it accepts inline elements: what inline markup restrictions apply
Right?
Back to you, @lauriii!
- Assigned to effulgentsia
- 🇺🇸United States effulgentsia
@lauriii and I discussed this and he gave me some compelling arguments for props (approach 2 in #25). I'm thinking that over and also thinking whether we can do that while still accommodating future use cases of web components and JS (React/Vue/Svelte/etc.) components.
- Status changed to Needs work
3 months ago 8:20am 12 September 2024 - 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
Marking to signal that the next step is on @effulgentsia's plate (he told me that in DM, so I'm not just imposing that or anything 😜).
- 🇫🇷France pdureau Paris
- Paragraphs uses RTE as a field but has the concept of slots through entity references
- Wordpress Gutenberg uses
type: 'rich-text'
- Shopify uses
type: 'richtext'
andtype: 'inline_richtext'
- Builder.io uses
type: 'richText'
- Plasmic uses
type: 'string', control: 'large'
- Framer uses
type: ControlType.RichText
If my understanding is correct, those are examples of WYSIWYG / display builders configurations, not UI components definitions. And none of them are using JSON Schema notation anyway.
he gave me some compelling arguments for props
If we go this way anyway, can we stay compatible with JSON schema specification by not introducing drupalisms or anything related to XB in the prop definitions?
For example, this is wrong because the
enum
keyword will limit available strings to "<em>" and "<stong>" instead of acting as#allowed_tags
property of#markup
renderable:heading: title: Heading type: string format: markup enum: - <em> - <strong>
- Issue was unassigned.
- 🇺🇸United States effulgentsia
For JSON schema, I think we can represent it (in YAML) as:
type: string contentMediaType: text/html x-allowedTags: - em - strong
See contentMediaType and custom annotations.
Within web components ... you can pass HTML content only through slots.
In practice I think there's some decent options available to us for passing HTML content from a rich text editor through as props/attributes to JS framework components or web components. For JS framework components, the JS framework component can internally delegate the responsibility to a component dedicated to rendering HTML content from a CMS. For example, for React there's react-html-map and probably various other options. Or, people can roll their own using whatever the framework's approach to rendering HTML strings is (for example, in Svelte, using {@html ...}) combined with an XSS filtering library like DOMPurify.
For vanilla web components (does anyone actually write vanilla web components these days now that https://lit.dev/ exists?), someone could either use
.innerHTML
combined with an XSS filtering library like DOMPurify, or we could add an optional PHP-side rich-text prop to slot converter when rendering the web component's custom element.So from a technical standpoint, I don't have objections to rich text as a prop anymore.
@lauriii and I discussed this and he gave me some compelling arguments for props (approach 2 in #25)
The main one is being able to express order. For example, say you have an SDC with "props":
- title
- description
- link
And you want
description
to be able to contain HTML formatting like<strong>
,<em>
, etc. If we modeleddescription
as a slot, we'd need some way within the SDC's YAML to specify that within editing UIs (like XB), we want the input entry fordescription
to be aftertitle
and beforelink
. Any syntax for doing that would already necessitate the SDC developer's mental model being "it's a slot but in a way it's kind of a prop", so actually making it a prop cleans that up. - 🇫🇷France pdureau Paris
See contentMediaType and custom annotations.
Cool finding! I am happy to know about
contentMediaType
.we want the input entry for
description
to be aftertitle
and beforelink
.(In this specific example, if we consider
title
a slot (which it is IMHO), we don't need to have such elaborated solution to get description after title. First the slots in the declared order, then the props in the declared order. 😉)This looks like I am nitpicking, and I like the
contentMediaType
proposal, but my concerns are broader.We are talking here about adapting the component definition because of the XB contribution form. This is what I had in mind when I said 📌 Update XB's `image` SDC to comply with best practices, and document those best practices Needs review :
I know Experience Builder is trying to build both a display builder (a better Layout Builder) and an UI components loader/manager (like UI Patterns 2), and I understand it is not an easy task. But I am also afraid the components management may be bent in some ways we may regret later, in order to comply with the current state and needs of the display builder.
I left this thread when I read about "business-related UI", but I am an hopeless optimist.
SDC freed us by giving us the opportunity of doing front-end without caring about Drupal as a CMS, business rules and application state. Now, we can build the best UI components possible, without thinking about Views, Field API, Layout Builder or Experience builder... and then use them with all their glory in a Drupal context. Let's embrace this.
It will be a key success indicator if XB is shipped without any SDC components AND is able to leverage correctly any SDC component from the Drupal community.