Figure out a solution for rendering lists

Created on 26 January 2024, about 1 year ago
Updated 12 March 2024, about 1 year ago

Problem/Motivation

In a few patterns that accept lists I've had to use something like

{% if content.field_name[0] %}
  <ul>
    {% for key, item in content.field_name if key|first != β€˜#’ %}
      <li>{{ item[β€˜#title’] }}</li>
    {% endfor %}
  </ul>
{% endif %}

This is because the list array contains a lot of info that shouldn't be printed. I can obviously do this logic in the main twig file before calling the pattern but then I feel I can't use the pattern from the UI.

@pdureau stated in slack https://github.com/twigphp/Twig/pull/3859 may help solve this problem.

Steps to reproduce

NA

Proposed resolution

TBD

Remaining tasks

Figure out a good solution

User interface changes

NA

API changes

NA

Data model changes

NA

πŸ“Œ Task
Status

Fixed

Version

3.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States smustgrave

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

Merge Requests

Comments & Activities

  • Issue created by @smustgrave
  • πŸ‡«πŸ‡·France pdureau Paris

    This kind of code must be avoided because it is inspecting the structure of a render array in a slot:

    {% for key, item in message|filter((item, key) => key|first != '#') %}

    Slots content must not be inspected. They are opaque. Slots are not always render arrays.

    This was found in two components alert and collection_item.

    Proposed resolution for alert

    This one is easy to fix because there is no need of such inspection if we follow the upstream documentation instead of managing the complexity of statys-message.html?tiwg inside the component template.

    Let's keep the component template simple:

    <div{{ attributes.addClass('usa-alert').setAttribute('role', role) }}>
      <div class="usa-alert__body">
        {% if heading %}
          <h4 class="usa-alert__heading">{{ heading }}</h4>
        {% endif %}
        {% if message %}
          <div class="usa-alert__text">
            {{ message }}
          </div>
        {% endif %}
      </div>
    </div>

    And move the loop in the presenter template:

      {% for type, messages in message_list %}
        {% for message in messages %}
          {{ pattern('alert', {
            'variant': type == 'status' ? 'info' : type,
            'message': messages,
            'attributes': attributes.addClass('margin-y-2')
          }) }}
        {% endfor %}
      {% endfor %}

    By doing that, we are more compliant with upstream USWDS and we are avoiding 2 other risks: the use of merge filter and the alteration messages variable from inside a loop.

    Proposed resolution for collection_item

    This component is only called from search-result.html.twig which is sending a list of plain strings, so why inspecting the renderable?

    Maybe because you want to be extra careful while waiting for ✨ Make the Twig loops safer Postponed , but I don't believe introducing this complexity will help.

    I can obviously do this logic in the main twig file before calling the pattern but then I feel I can't use the pattern from the UI.

    Hmmm.. Indeed we need to check that. Can you tell me more?

  • Status changed to Needs review about 1 year ago
  • Pipeline finished with Success
    about 1 year ago
    Total: 254s
    #89261
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    So removing ((meta_item, key) => key|first != '#') this kind of stuff means (least I can't find a way) that patterns can be used outside twig. So if I tried to use in layout builder, field group, or views and I assign a Drupal list field to this slot it won't work.

  • πŸ‡«πŸ‡·France pdureau Paris

    So if I tried to use in layout builder, field group, or views and I assign a Drupal list field to this slot it won't work.

    I guess you have this issue with collection_item because my proposal about alert looks compatible with site building.

    Proposal: instead of doing this stuff with the filter() filter:
    {% for key, meta_item in meta_items_list|filter((meta_item, key) => key|first != '#') -%}

    We can do something like that:

    {% set meta_items_list = (meta_items_list is iterable and (meta_items_list|keys[0] == 0)) ? meta_items_list : [meta_items_list] %} 
    {% for meta_item in meta_items_list %}

    So, when Twig will be ready, it will be easy to switch to the expected mechanism:

    {% set meta_items_list = (meta_items_list is sequence) ? meta_items_list : [meta_items_list] %} 
    {% for meta_item in meta_items_list %}
  • Status changed to Needs work about 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Tested the MR and the solution in #6 but unfortunately causes infinite loops or empty tags.

  • Assigned to pdureau
  • πŸ‡«πŸ‡·France pdureau Paris

    I will test on my side

  • Issue was unassigned.
  • Status changed to Needs review about 1 year ago
  • πŸ‡«πŸ‡·France pdureau Paris

    New commit. Is it better?

  • Pipeline finished with Success
    about 1 year ago
    Total: 147s
    #91726
  • Status changed to Needs work about 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Cleared cached and the alert message isn't appearing.

  • Assigned to pdureau
  • πŸ‡«πŸ‡·France pdureau Paris
  • Pipeline finished with Success
    about 1 year ago
    Total: 265s
    #94373
  • πŸ‡«πŸ‡·France pdureau Paris

    OOps, there was a typo in templates/base/misc/status-messages.html.twig

         {% for message in messages %}
           {{ pattern('alert', {
             'variant': type == 'status' ? 'info' : type,
    -        'message': messages,
    +        'message': message,
             'attributes': attributes.addClass('margin-y-2')
           }) }}
         {% endfor %}

    I will push the tiny fix as soon as i can.

  • Issue was unassigned.
  • Status changed to Needs review about 1 year ago
  • πŸ‡«πŸ‡·France pdureau Paris

    Pushed. Not fully tested. I hope it is OK.

  • Pipeline finished with Success
    about 1 year ago
    Total: 144s
    #98665
  • πŸ‡«πŸ‡·France pdureau Paris

    FYI, Florent is working on ✨ Make the Twig loops safer Postponed for UI Patterns 1.x

    Once done, my current proposal:

          {% set is_list = meta_items_list is iterable and meta_items_list|keys|first == 0 %}
          {% set is_children = meta_items_list is iterable and meta_items_list|keys|first|first != "#" %}
          {% set meta_items_list = is_list or is_children ? meta_items_list : [meta_items_list] %}
          {% for meta_item in meta_items_list -%}
    

    Can be replaced by:

          {% set meta_items_list = meta_items_list is sequence ? meta_items_list : [meta_items_list] %}
          {% for meta_item in meta_items_list -%}
    
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Should this postponed then?

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    So this does work with the twig block of alerts and patterns but when using with views it doesn't work due to {% if meta_items_tags %}
    always returning true.

    Pushed up a fix for that but still doesn't work great in views because of how views returns the string.

  • Status changed to Needs work about 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Really need to think about this more

    |render|striptags|trim breaks the pattern display but works with things like views.

    Removing it fixes the pattern but breaks views and shows empty divs.

    Want to make sure we aren't moving away from having this work in the UI and not being just twig based.

  • Status changed to Fixed about 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    Reverted my change and will think about that later.

    Will use the other changes.

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024