Quantcast
Channel: Issues for Drupal core
Viewing all articles
Browse latest Browse all 293733

Complete and clarify SDC documentation

$
0
0

Problem/Motivation

When helping people with SDC, doing my consultancy job, on Drupal slack, or in contrib issues, I see a lot of confusion and struggle with the components slots, especially related to the embed and block mechanisms.

The current SDC documentation needs to be updated to:

  • tone down the promotion of Twig embed and Twig block
  • preparing the people of using less the templates to templates calls and more the Render API (manually or through display buidling)

Twig blocks are problematic:

  • Originally, they come from Jinja templating language, as a template inheritance mechanism. Something we never want to do in/with a component template.
  • Then, Twig introduced embed tag to use blocks for nesting. It is very Twig specific, and this is the reason why blocks may initially look interesting for SDC authors.

Slots declared as blocks are verbose and confusing.

For example, instead of {{ branding }} and {{ copyright }}:

  • Radix do that in navbar:{% block branding %}{{ branding }}{% endblock %}
  • umami do that in disclaimer : {% block copyright %}{% endblock %}

(The radix way is better because compatible with both include and embed)

Some component authors are so confused that they overlap slots and props. For example, bootstrap's card:

{% block card_header_block %}
  {{ card_header }}
{% endblock %}

Slots declared as blocks are complicated to call from another template.

For example, node--event.html.twig template which is passing some formatted fields to umami disclaimer's slots :

{% embed 'umami:disclaimer' only %}
  {% block disclaimer %}
     {{ content.field_date }}
  {% endblock %}
  {% block copyright %}
     {{ content.field_location }}
  {% endblock %}
{% endembed %}

Instead of the simpler and safer:

{{ include(
  'umami:disclaimer', {
    disclaimer: content.field_date,
    copyright: content.field_location
  },
  with_context = false,
) }}

Testing if a slot is empty seems complicated with a block.

Today, on #components Slack channel, brayn7 came with this snippet which was not working:

<div class="component">
    {% if block('area_one') is defined and block('area_one')|trim != '' %}
    <div class="wrapper-div-i-want-conditionally">
      {{ block('area_one') }}
    </div>
  {% endif %}
</div>
called by:
{% embed 'theme:component' %}
  {% block area_one %}
      <h2>Hello world</h2>
  {% endblock %}
{% endembed %}

Removing the blocks and embed was a straightforward fix:

<div class="component">
  {% if area_one %}
  <div class="wrapper-div-i-want-conditionally">
    {{ area_one }}
  </div>
  {% endif %}
</div>
called by:
{% set area_one %}
    <h2>Hello world</h2>
{% endset %}
{{ include("theme:component", {area_one: area_one}, with_context = false) }}

It is not possible to loop on a slot values when implemented as a Twig block

When we can expect multiple renderable injected in a slot (examples: grid, carousel, accordion...), we may want to loop on them:

{% set my_slot = my_slot and my_slot is not sequence ? [my_slot] : my_slot %}
{% for item in my_slot %}
  <div class="a-markup-wrapper">
    {{ item }}
  </div>
{% endfor %}

It is not possible with blocks. It may be something we realize later in the lifecycle of a component and break the compatibility.

The 2 uses of blocks (embed & extends) are not always playing well together

See: #3446933: SDC incorrectly throws an exception about embedded slots

Twig block is a "template to template" only mechanism.

It is triggered only when a component is called from a presenter template (node.html.twig, field.html.twig...), not when its is called from the Render API. This create a gap between usage of SDC components, which will grow with the development of new display builders tools (SDC Display, UI Patterns 2, Experience Builder...).

Indeed, SDC API has currently a workaround to inject missing blocks with the Render API but this may not stay forever:

$template .= "  {% block $slot_name %}" . PHP_EOL
    . "    {{ $slot_name }}" . PHP_EOL
    . "  {% endblock %}" . PHP_EOL;
}

Proper variables manipulation ({% for item in slot %}, {% if slot %}, {{ slot }}...) regardless of where the component is called, without the need of such a hack.

Proposed resolution

Update Quickstart

Add an example without block and with include() as the primary example.

Keep the example with block and embed, but add the print node:

{% block chip_content %}
  {{ chip_content }}
{% endblock %}

Update Using your new single-directory component

Keep the mentions of block and embed but move it to the page bottom and update any parts which can make readers believing that "slots == Twig blocks". For examples:

Use the Twig include() function if there are no slots or arbitrary HTML properties and the data all follows a defined structure.

Use a Twig {% embed %} tag if you need to populate slots.

Also, don't promote the use of include tag at the same level of include function:

It is recommended to use the include function instead as it provides the same features with a bit more flexibility:

  • The include function is semantically more "correct" (including a template outputs its rendered contents in the current scope; a tag should not display anything)
  • The include function is more "composable"
  • The include function does not impose any specific order for arguments thanks to named arguments.

Source: https://twig.symfony.com/doc/3.x/tags/include.html

Render element is already explained in this page, but it can be promoted a bit. It is a good opportunity to explain the difference between presenter templates:

  • unavoidable because of the lack of display building: breadcrumbs.html.twig, status-message.html.twig, menu.html.twig, pager.html.twig..
  • the ones conflicting with the display building: node.html.twig, field.html.twig, views.html.twig, block.html.twig...

Also, "Component data validation" section can be moved in "Creating a single-directory component" page.

Update What are Props and Slots in Drupal SDC Theming?

Remove any mention of block and embed. The goal of this page is to teach about slots and props, not to list all ways of calling components from templates.

A bit of rewording and an annotated component screenshot like this can also help the reader:
slide barcelona

Update API for Single-Directory Components

Replace the example with block and embed by one with include. Or simply remove the example.

Also, a schema can be a good way to show how the different parts of the API are interacting.

Conclusion

SDC in Core is now 16 months old, and the community is still struggling with component templating and usage. I believe updating the confusing parts of the documentation will help the adoption and the blooming of the ecosystem.


Viewing all articles
Browse latest Browse all 293733

Trending Articles