An important part of single directory components (or SDC) is the component metadata. The component metadata is the information about the component that is not directly used to render the component. This includes information about the component type, the component status, the library dependencies, etc.
This issue aims to define the required metadata and the format in which developers will need to provide it. The resulting patch will create a JSON schema document that will describe such format. This JSON Schema will be used to validate the component-name.component.yml
Originally we considered the possibility to leverage the manifest definition of the W3C custom elements. However, we discovered that this document contains a lot of implementation-specific details that do not translate to a Drupal implementation.
Required metadata
The required fields that all components should specify are:
- Machine name: this identifies a component uniquely. Multiple plugins can share the same machine name for replacement reasons.
- Name: this is a human readable name that will be used to represent the component in the various user interfaces that deal with components.
- Component status: a value from an enum. Possible values are:
WIP,alpha,beta,RC,stable,deprecated. This will comunicate the different stages of readiness and the contract between the provider, and the consumer. It aims to provide additional flexibility to component development in Drupal core. It could also be used by the automated updates bot to detect that a theme is using a deprecated component. - Component type: value from an e mum. Possible values are:
atom,molecule,organism. Taken from Pattern Labs terminology, this field aims to provide semantics about how the component is intended to be used. - Prop schemas: the JSON Schema definition for component props this will be used to validate prop input during the render process. Additionally it will describe the props in a way that other systems can interpret and provide integrations for.This is one of the key points. Examples of such integrations include: generating forms automatically for humans providing values for the components (low code tools), compatibility assessment of mapping options lean my custom field map to this component?), similarity analysis (can I replace this component with this other?), templatability (editors get a list of compatible components for a given field type, custom block, etc.). Even though all these options are possible, Drupal core will only use this information to ensure that the data passed to the component is valid. Other features belong to contrib. Prop schemas will also provide example data for their props to showcase components in isolation.
- Library dependencies: additional libraries that should be included when adding a component to the page. Technically, this is not required, unless there is a library needed for the correct functioning of the component.
Optional metadata
- Description: a description used in UIs showing components.
- Custom: an optional object with free-form metadata.This is useful for projects extending the component functionality.
Example
$schema: https://git.drupalcode.org/project/cl_components/-/raw/1.x/src/metadata.schema.json
machineName: my-banner
name: Banner
componentType: organism
description: Banner with title and a CTA link
status: READY
variants:
- tall
schemas:
props:
type: object
properties:
heading:
title: Heading
description: The title for the banner text.
examples:
- Join us at The Conference
type: string
ctaText:
title: CTA Text
type: string
examples:
- Click me!
ctaHref:
title: CTA Href
type: string
examples:
- 'https://www.example.org'
ctaTarget:
title: CTA Target
type: string
enum:
- ''
- _blank
image:
title: Media Image
description: Background image for the banner.
type: string
Considered alternatives
It has been suggested that all the metadata could be derived from the twig template file name. It is hard to accommodate all the information we want to record in a single filename.
Another suggestion is to have metadata as part of the Twig template front matter. This has the limitation of binding a component to a template, and just one.This will prevent having component variants (discussed separately), among other potentially desirable features.
Another suggestion was to include a metadata.json file. This has the drawback of not being idiomatic with other plugin definitions. Drupal developers are already used to the id-name.feature.yml files.This argument also applies to the front matter alternative.
| Comment | File | Size | Author |
|---|---|---|---|
| #3 | Screenshot from 2022-10-10 12-35-02.png | 217.04 KB | e0ipso |
Comments
Comment #2
pdureau commentedHi e0ipso,
Your first proposal looks a lot like UI Patterns definition format: https://ui-patterns.readthedocs.io/en/8.x-1.x/content/patterns-definitio...
Most of the differences are very small:
idin UI Patterns, because it is the plugin IDlabelin UI Patternsstatusis missing from UI Patternstagsproperty which can replace yourcomponentTypeproposal. In my opinion, having an enum here is a bad idea, because atom, molecule, organism... are only examples of nesting provided by Brad Frost in its book (and default values in Pattern Labs), and we don't have to expect components to always be organised like that in real life.The only "big" difference between the 2 formats is the field definition.
You chose JSON schema to express field definitions, it looks nice, however:
props/object wrapper, it looks confusingtypeproperty is only a loose indication). This may change. There is an open and complicated discussion in the UI Patterns teams since DrupalCon Prague, and I doubt JSON schema typing will be the solution.fields: icon: label: Icon type: text text: label: Text type: text examples: - text: Hello World - text: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. - icon: search text: Lorem ipsum dolor sit amet, consectetur adipiscing elit.So,the examples will be displayed for each of those 3 variants in the library.
Anyway, UI Patterns format look a lot with what you are looking form, it is well tested and widely used for more than 5 years, with thousands of use cases.
Comment #3
e0ipsoGreat! I didn't list it, but the plugin system will also add an
idkey with the plugin ID.Looks like we can use any of those. I don't really care as long as the info exists, and it's semantically the same.
There may be better approaches to this, but I do not think it's a bad idea. Having an enum allows for shared configuration forms for site-builder driven UIs. For instance form below will let you configure what are the available options shown to editors when using a component selector that stores the info as a field. The same exact code is re-used to store the pre-configured filters to decide which components are available when embedding using the WYSIYWG, etc.
The point is that an enum makes it easier for site builders to have expectations of what is available now, and also what will be available in the future.
That being said, I do not thing free-form
tagsandcomponentTypeare mutually exclusive. Do you?Yes. That is on purpose. This is for two reasons.
Even when some properties may fit under the guidelines of #2, I think we want to be careful due to #1.
Comment #4
e0ipsoYeah, that might be true. I was thinking about extensibility here. I imagine contrib modules adding their 3rd party schemas as siblings of
/props. For instance/form-ui.Again, I will not die on a hill for this, but my experience tells me that it's easier to allow for extension from the ground up than shoehorn it after a while.
I did not follow this whole section. I sense you have hesitations, but I cannot understand what are the concerns. However, I will say that I do feel strongly on this. All props should be typed, and component rendering should validate them.
Using a popular standard like JSON Schema provides us with:
Please, clarify.
The reason is that this is part of JSON Schema. You can add the examples at any level (and even in multiple levels at the same time). Examples here are only for documentation and for the developer using the component to understand the type of data they are dealing with.
I would not conflate this examples with the integration with component libraries. I think component libraries should be 100% independent from component definitions (as per the requirement to limit core's API surface mentioned in the previous comment).
I don't feel strongly about setting a recommendation to put examples at a given depth in the schema, if that is your preference. I only feel strongly about the mandatory JSON Schema definition of props.
Comment #5
pcate commentedI agree with @pdureau on this. I don't think tying the values to a particular design methodology naming convention is a good idea. I've found in practice the atom, molecule, organism, etc. naming to be confusing for content editors and site builders.
If there is a
componentTypefield I think it should be easy to customize the possible values and map/rename ones provided by other contrib modules/themes.I would probably make this field optional as well.
Comment #6
idiaz.roncero+1 to "having an enum here is a bad idea, because atom, molecule, organism... are only examples of nesting provided by Brad Frost in its book (and default values in Pattern Labs), and we don't have to expect components to always be organised like that in real life."
I feel that would be tying Drupal too tight to a specific naming/methodology. Something similar happened in the past with SMACSS structure on libraries, which I feel ended up widely misused on the Drupal community (a lot of devs just put the CSS on 'base' or 'theme').
Comment #7
e0ipsoIt seems that the feedback is overwhelming in moving away from Patter Lab's terminology as an enum.
Still I would like to keep the property. The reason for this structured metadata property is to make it easy to the extending ecosystem to create component filters. Imagine a contrib that creates blocks for components, should it create a block for your paragraph component, or the icon component? Probably not, hence the need for the component to express its Type so site builders can dictate what types will generate a block.
I only see one counter-proposal, which is to drop the property, but as said above I think it provides lots of value.
I want to argue to keep it as an enum, something that is not cognitively loaded with Pattern Lab, but something semantic that helps us understand that a component is a building block that should only be used in other components like a button, as opposed to a banner which can be used directly in the page.
The reason to favor an enum is that by standarizing then extending modules have a reliable component type to build features for, rather than a myriad of types that use potentially conflicting terminology.
I know I am bringing concepts for editorially managed components, and that those are out of scope. However, I feel that since this is just metadata we want to get it right from the start.
Comment #8
stmh commentedHi, here,
+1 to open up the type definition. I have the feeling that we need to decide what we try to build here:
- Do we want to build a new component system for drupal which makes it easy for themers to add and modify components
or
- Do we want to define how a component should be handled by drupal, and what information is necessary for drupal to support a variety of component libraries like Patternlab Miyagi, compony, and others? So contrib can integrate their component system of choice with drupal in a straightforward way.
If the latter, then it should be allowed for component systems like pattern lab to use their naming conventions (atoms, organism, ...) and for other component systems to use their naming convention.
Comment #9
mrweiner commentedIt may be worth considering whether there's a way to take inspiration from the flexibility of the entity system in handling these type definitions. Every organization is going to have different ways of thinking about their component hierarchies. Wouldn't making any specific component system a requirement -- whether pre-existing or Drupal-specific -- be against the principle of making Drupal flexible to every use case? It would be kind of like shipping with atom, molecule, and organism entity types and not allowing users to create their own.
What if we were to create some sort of configuration for a site to define its own component types that could then be autowired in a way similar to how entity reference fields are aware of all entity types? Devs would just need to ensure that the type definition matches one of the enums in their own configuration. This would decouple Drupal from any particular system while still allowing for reliable, site-specific enumerations.
In theory, this would not stop a contrib module from leveraging the atomic principles on its own. It could provide a configuration page that allows the site builder to map their site's own component types to the module's atomic component types if that's a requirement for a particular implementation.
Comment #10
ctrladeljsonschema: +1 from me for both jsonschema and for components being typed. This is a particularly painful and opaque part of frontend development in Drupal right now where a template has no way of telling you what data structure it expects and if you pass a variable with the wrong type to a template the entire page just stops working.
componentType: Agreed that an enum with a set list of values is to restrictive. I'm having a hard time placing when I would use this value because it requires that the system I'm building infer something about a component based on a value instead of the component explicitly stating what it should be used for. In my own component system implementations I've started to introduce an
availableAsproperty which is just an array of strings likeavailableAs: [block, field]this allows my integrations/derivers to check for their key in availableAs to determine component availability. My availableAs use case could work just as easily with a loosely definedtagsproperty as well.status: +1 makes sense to be a predefined list to me.
props: +1 from me. I think @pdureau's question was more do we need both `schema` and `props` or could just `schema` or `props` being the top level container for component properties.
variants: The use case isn't clear to me here? What does this control?
Library dependencies: I'm a bit unsure of this one. For most use cases we'll be able to automatically define/create a library based on the files in a component directory. The question then becomes for advanced use cases is it better to allow embedding the structure of *.libraries.yml file in the component definition or is it better to require a my_component.libraries.yml be provided? Also general question how will components with a library dependency have it injected when they are embedded directly via twig include into another template?
Questions:
Comment #11
ctrladelAlso, https://blockprotocol.org/ has been around for awhile and while they are calling them blocks it is essentially the same as what we are calling components. Could be worth digging into their data structure https://blockprotocol.org/docs/spec/core to see if there are any edge cases or gotchas they've accounted for that we've missed so far.
Comment #12
e0ipsoThis is for things that don't really share that much markup, but are semantically the same component. It is a way to avoid cumbersome
if / elseblocks in a component template.Comment #13
e0ipsoI looked at them when working on progressive decoupling solutions. Like the custom element manifest, they approach a similar problem but the details are really different from our problem. From my memory, block protocol is really focused towards editorial interfaces similar to Notion and that shows in their spec.
Comment #14
e0ipsoI think that more often than not we'll want to add dependencies to
core/once, orcore/drupalSettings, etc. I agree that many are skipping declaring dependencies nowadays, but that does not mean they aren't needed.However, you are spot on that there will be advanced use cases (like wanting to set the defer attribute on some JS). There will be a separate mechanism for that to happen. But dependencies are considered in the basic use case.
Comment #15
e0ipsoThis should be possible in contrib. For now, I don't think it's necessary. What do you think?
Comment #16
e0ipsoWe favor composition over inheritance, so IMO we should not support inheritance. We do support composite components (components within components).
Comment #17
e0ipsoI may have introduced it as a dev dependency via jsonapi. I am :90: sure it made it into core. In any case justinrainbow's library is the one I particularly like.
Comment #18
e0ipsoI agree, this is important. We'll be using plugins, you might be thinking about configuration entities. In any case, I think it's a good idea to keep the customary plugin definition alter hook for this matter.
Comment #19
mherchelI personally think this should be an optional property for this. It could be extremely useful in enabling advanced site-building functionality.
Comment #20
e0ipsoMost of the feedback here made it to https://www.drupal.org/project/sdc in #3317121: Refine metadata proposal. I am closing this, since the discussion has happened and it was implemented.