Problem

  • #type 'item' only works within a form.
  • Link module has to implement a second field formatter, because there is no way to render a compound element with a #title and #markup.

Goal

  • Eliminate Link module's 'link_separate' field formatter (merge it into 'link_default').

Proposed solution

  1. Make #type 'item' work outside of a form context. Make it produce a heading (label is only allowed within a form) and markup, like so:
    <div class="item">
    <h4>Label, as provided in #title</h4>
    Content, as provided in #markup (or rendered children).
    </div>
    

Related issues

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sun’s picture

Status: Active » Needs review
FileSize
14.71 KB

Like this.

Status: Needs review » Needs work

The last submitted patch, drupal8.theme-item.1.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
14.84 KB

Umph. Apparently, a couple of core modules are using #type 'item' in very unusual ways.

Since I don't want to rewrite half of core for this patch, I added the necessary properties and behavior to support those unusual usages. Specifically:

- Some modules do not specify a #title.
- Some specify only #markup and #description.

Status: Needs review » Needs work

The last submitted patch, drupal8.theme-item.3.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
14.81 KB

Re-rolled against HEAD.

Status: Needs review » Needs work

The last submitted patch, drupal8.theme-item.5.patch, failed testing.

sun’s picture

sun’s picture

Status: Needs work » Needs review
FileSize
8.76 KB
21.3 KB
  1. Fixed RenderTest.
  2. Fixed NodeTypeInitialLanguageTest.
  3. Fixed LinkFieldUITest.
  4. Fixed LinkFieldTest.
sun’s picture

Manual/visual testing:

The classless H4 heading causes problems, since headings usually get a top+bottom margin of 1em. Options:

1) Do not use a heading, but div.label (as in #882666: Core form descriptions shouldn't use a label when not associated with a form) — in hindsight, I don't really like that idea, since it abuses an HTML element to look like another. If something should look like a heading/label, then it should be formatted as one.

2) Apply a class to the heading, so the margin can be force-removed.

3) Use a higher/smaller heading element (e.g., H6). This probably still requires 2), not only for the margin, but also for the font-size.

I guess that would leave us with h4.label and accompanying CSS styles to remove the margins and inherit the font-size.

sun’s picture

FileSize
1.84 KB
21.77 KB
  1. Incorporated CSS from #882666: Core form descriptions shouldn't use a label when not associated with a form
  2. Added .label class to item heading.

The last submitted patch, theme.item_.10.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
Issue tags: +API clean-up, +theme system cleanup, +Theme Component Library

#10: theme.item_.10.patch queued for re-testing.

sun’s picture

I consider this patch ready. :)

sun’s picture

FileSize
21.77 KB

Merged HEAD.

sun’s picture

Status: Needs review » Needs work
Issue tags: +API clean-up, +theme system cleanup, +Theme Component Library

The last submitted patch, theme.item_.14.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
21.2 KB

Merged HEAD.

sun’s picture

Alrighty. So here's my stance on the current patch:

  1. I'm 100% confident that making #type 'item' work as a render element outside of forms is a good idea.

    I had a dozen of use-cases for that in the past already. That change looks complete to me.

  2. I'm only 60% sure of the Link field formatter change, which merges the 'separate' formatter into the 'link_default' formatter.

    The idea is that the 'separate' formatter shares most of the formatter settings, prepare and view code with the default link formatter. The actual difference between both is merely that the 'separate' formatter outputs the link title and link URL as separate HTML elements (as the formatter name suggests).

    To clarify, this is what 'link_default' outputs:

    <a href="http://example.com">My link title</a>
    

    And this is what 'separate' outputs:

    <h4>My link title</h4>
    <a href="http://example.com">http://example.com</a>
    

    Both formatters share a couple of settings that allow to control whether to output the URL as plain-text (instead of a link) and so on. Also, the code to sanitize the values as well as applying tokens is almost identical.

    I don't know what our current guidelines are with regard to "When, exactly, does one implement a standalone formatter instead of formatter settings?"

    In other words, is a formatter defined by its technical behavior? Or is it defined by its resulting markup?

    Because, if it's the markup that makes up a formatter, then the 'separate' formatter definitely produces different markup and should probably stay its own formatter... But if the markup does not play a key role - or if the marginal difference outlined above is not deemed to be sufficient - then it would be OK to merge them.

    Perhaps I should move the Link formatter change into an own issue?

sun’s picture

FileSize
8.38 KB

Created a separate issue for the Link formatter change: #1867822: Leverage new #type 'item' for Link field formatter (merge 'link_separate' formatter into 'link_default')

Removed Link formatter changes.

sun’s picture

sun’s picture

#19: theme.item_.19.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, theme.item_.19.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
Issue tags: +API clean-up, +theme system cleanup, +Theme Component Library

#19: theme.item_.19.patch queued for re-testing.

swentel’s picture

Makes a lot of sense. The example in the summary has a wrapper like <div class="item">, however, when testing, I don't see that in the markup, unless I'm doing something wrong. Or don't you want to add that wrapper anymore ?

     // Test in NodeRenderController
      $entity->content['testing'] = array(
        '#type' => 'item',
        '#title' => 'de max',
        '#markup' => 'Testing item with title',
      );

Screen Shot 2013-01-09 at 16.49.43.png

sun’s picture

Yep, that's intentional, since a wrapper is not logical in all cases:

+  // Lastly, only wrap the item in a container if there are any attributes.
+  if (!empty($element['#attributes'])) {
+    $output = '<div' . new Attribute($element['#attributes']) . '>' . $output . '</div>';
+  }

Of course, we could decide that all items should always get at least an .item class on a wrapping DIV, but in evaluating this patch across core, I wasn't able to see why that should be necessary (and it resulted in some divitis). There's a counter-argument of reliability, of course. Perhaps we need to always add a wrapper in order to allow JS and CSS to reliably target elements.

swentel’s picture

Perhaps we need to always add a wrapper in order to allow JS and CSS to reliably target elements.

Would allow to target for #ajax calls as well I guess, so my best guess would be to add it by default, although in the end I can live with none as well :)

sun’s picture

Yeah. But if it's always wrapped in a DIV, then... what's the actual difference to #type 'container'...?

Would it make sense to remove #type 'item' altogether and move the #title » h4.label processing as contained here into #type 'container'?

mgifford’s picture

Just adding a link to #1886728: Switch from details to fieldset when collapsing isn't needed where @sun @nod_ & I were discussing forms & containers.

Would be really good to have some UI patterns that are semantically correct to help us group form elements. In many cases I do think that a styled <div> is going to be the right choice.

sun’s picture

For #ajax and #states, we'd actually need a HTML ID in #attributes, which in turn means that we need a #pre_render to call element_set_attributes() (e.g., like all form elements).

Second, when appearing inside forms, we could automatically translate #name into an HTML class on the wrapping container; e.g., '[#name]-wrapper'. That should be sufficient for theming.

But when appearing outside of forms, and if there are no #attributes, then I think we do not need a wrapping container, since that would be a DIV without any attributes, which doesn't make sense.

I think we should definitely try to get this in for D8. Perhaps first as #type 'item', to make sure it lands? But with the possible clean-up of merging 'item' into 'container'.

What do you think?

jibran’s picture

Status: Needs review » Needs work

The last submitted patch, theme.item_.19.patch, failed testing.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.

sun’s picture

Note that #2002336: Introduce a CSS class to hide borders of fieldset elements introduced a new #type 'fieldgroup' that essentially resolves part of this use-case here, but only within a form context, because it is a <fieldset>.

The original use-case here was targeting output outside of a form context; e.g., Link field formatters just want a "label" followed by custom #markup/#children.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

andypost’s picture

Version: 8.1.x-dev » 8.3.x-dev
Assigned: sun » Unassigned
Issue tags: +Needs reroll
vprocessor’s picture

Assigned: Unassigned » vprocessor
Manuel Garcia’s picture

Assigned: vprocessor » Manuel Garcia
Manuel Garcia’s picture

Assigned: Manuel Garcia » Unassigned

I had a look, but this isn't just a standard reroll... we have to do things differently nowadays. Below my humble review:

  1. +++ b/core/includes/form.inc
    @@ -4115,7 +4115,7 @@ function theme_number($variables) {
    -  element_set_attributes($element, array('id', 'name', 'value', 'step', 'min', 'max', 'placeholder'));
    +  element_set_attributes($element, array('id', 'name', 'value', 'step', 'min', 'max', 'size', 'placeholder'));
    

    Theme function is gone now, and this change is already in Drupal\Core\Render\Element\Number::preRenderNumber()

  2. +++ b/core/includes/theme.inc
    @@ -1627,6 +1627,89 @@ function theme_status_messages($variables) {
    +/**
    + * Returns HTML for a compound label/heading with content.
    + *
    + * @param array $variables
    + *   An associative array containing:
    + *   - element: An associative array containing the properties of the element.
    + *     Properties used:
    + *     - #title: (optional) A label/heading to output.
    + *     - #children: The content associated with the title.
    + *     - #attributes: (optional) An array of attributes for the wrapping
    + *       container element.
    + *     - #description: (optional) A description to append to the item.
    + *     - #description_attributes: (optional) An array of attributes for the
    + *       description.
    + *
    + * @see theme_form_element()
    + * @see theme_form_element_label()
    + */
    +function theme_item($variables) {
    +  $element = &$variables['element'];
    +  $output = '';
    +
    +  if ($element['#title'] !== '') {
    +    $title = filter_xss_admin($element['#title']);
    +    $output .= '<h4 class="label">' . $title . '</h4>';
    +  }
    +  $output .= $element['#children'];
    +
    +  if (!empty($element['#description'])) {
    +    $output .= '<div' . new Attribute($element['#description_attributes']) . '>' . $element['#description'] . "</div>\n";
    +  }
    +
    +  // Lastly, only wrap the item in a container if there are any attributes.
    +  if (!empty($element['#attributes'])) {
    +    $output = '<div' . new Attribute($element['#attributes']) . '>' . $output . '</div>';
    +  }
    +  return $output;
    +}
    +
    

    I don't think we should be adding new theme functions.

  3. +++ b/core/includes/theme.inc
    @@ -1627,6 +1627,89 @@ function theme_status_messages($variables) {
    @@ -1645,7 +1728,7 @@ function theme_status_messages($variables) {
    
    @@ -1645,7 +1728,7 @@ function theme_status_messages($variables) {
     function theme_link($variables) {
    -  return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . new Attribute($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
    +  return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . new Attribute($variables['options']['attributes']) . '>' . (!empty($variables['options']['html']) ? $variables['text'] : check_plain($variables['text'])) . '</a>';
     }
    

    theme_link was removed [#2061877]

  4. +++ b/core/includes/theme.inc
    @@ -2997,6 +3080,9 @@ function drupal_common_theme() {
    +    'item' => array(
    +      'render element' => 'element',
    +    ),
    

    Same, we should probably add a new #type instead?

vprocessor’s picture

Assigned: Unassigned » vprocessor
vprocessor’s picture

Assigned: vprocessor » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
5.74 KB

rerolled

Status: Needs review » Needs work

The last submitted patch, 39: theme.item_.39.patch, failed testing.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.