From 7a695724a3a2a53d4d7ac88abbf3b32b017ee8af Mon Sep 17 00:00:00 2001 From: webchick <webchick@24967.no-reply.drupal.org> Date: Thu, 6 Feb 2014 20:26:52 -0800 Subject: [PATCH] Issue #2152207 by steveoliver, joelpittet, gnuget, idflood, hussainweb, shanethehat, jenlampton, kpa, AnythonyR, EVIIILJ, kgoel, Cottser, dsdeiz, hanpersand: Convert theme_details() to Twig --- core/includes/form.inc | 50 ++++++++----------- core/includes/theme.inc | 1 + .../Drupal/system/Tests/Common/RenderTest.php | 44 ++++++++++++---- .../system/templates/details.html.twig | 33 ++++++++++++ 4 files changed, 87 insertions(+), 41 deletions(-) create mode 100644 core/modules/system/templates/details.html.twig diff --git a/core/includes/form.inc b/core/includes/form.inc index 35879a3a2fdc..d0231a2dcc59 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -979,44 +979,34 @@ function theme_fieldset($variables) { } /** - * Returns HTML for a details form element and its children. + * Prepares variables for details element templates. * - * @param $variables + * Default template: details.html.twig. + * + * @param array $variables * An associative array containing: * - element: An associative array containing the properties of the element. - * Properties used: #attributes, #children, #collapsed, #description, #id, - * #title, #value. + * Properties used: #attributes, #children, #collapsed, #collapsible, + * #description, #id, #title, #value. * * @ingroup themeable */ -function theme_details($variables) { +function template_preprocess_details(&$variables) { $element = $variables['element']; - element_set_attributes($element, array('id')); - _form_set_attributes($element, array('form-wrapper')); - - $output = '<details' . new Attribute($element['#attributes']) . '>'; + $variables['attributes'] = $element['#attributes']; + $variables['summary_attributes'] = new Attribute(); if (!empty($element['#title'])) { - $summary_attributes = new Attribute(array( - 'role' => 'button', - )); + $variables['summary_attributes']['role'] = 'button'; if (!empty($element['#attributes']['id'])) { - $summary_attributes['aria-controls'] = $element['#attributes']['id']; + $variables['summary_attributes']['aria-controls'] = $element['#attributes']['id']; } - $summary_attributes['aria-expanded'] = empty($element['#attributes']['open']) ? FALSE : TRUE; - $summary_attributes['aria-pressed'] = $summary_attributes['aria-expanded']; - $output .= '<summary' . $summary_attributes . '>' . $element['#title'] . '</summary>'; - } - $output .= '<div class="details-wrapper">'; - if (!empty($element['#description'])) { - $output .= '<div class="details-description">' . $element['#description'] . '</div>'; + $variables['summary_attributes']['aria-expanded'] = empty($element['#attributes']['open']) ? FALSE : TRUE; + $variables['summary_attributes']['aria-pressed'] = $variables['summary_attributes']['aria-expanded']; } - $output .= $element['#children']; - if (isset($element['#value'])) { - $output .= $element['#value']; - } - $output .= '</div>'; - $output .= "</details>\n"; - return $output; + $variables['title'] = (!empty($element['#title'])) ? $element['#title'] : ''; + $variables['description'] = (!empty($element['#description'])) ? $element['#description'] : ''; + $variables['children'] = (isset($element['#children'])) ? $element['#children'] : ''; + $variables['value'] = (isset($element['#value'])) ? $element['#value'] : ''; } /** @@ -1980,11 +1970,11 @@ function form_process_group(&$element, &$form_state) { * The modified element. */ function form_pre_render_details($element) { + element_set_attributes($element, array('id')); + // The .form-wrapper class is required for #states to treat details like // containers. - if (!isset($element['#attributes']['class'])) { - $element['#attributes']['class'] = array(); - } + _form_set_attributes($element, array('form-wrapper')); // Collapsible details. $element['#attached']['library'][] = array('system', 'drupal.collapse'); diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 7065a805ae2a..bc1fbbfe0865 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -2682,6 +2682,7 @@ function drupal_common_theme() { ), 'details' => array( 'render element' => 'element', + 'template' => 'details', ), 'radios' => array( 'render element' => 'element', diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php index 750250d3f4a9..a42f111bcfee 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderTest.php @@ -598,9 +598,6 @@ function testDrupalRenderChildrenPostRenderCache() { $element = array('#cache' => $element['#cache']); $cached_element = cache()->get(drupal_render_cid_create($element))->data; $expected_element = array( - '#markup' => '<details class="form-wrapper" open="open"><summary role="button" aria-expanded>Parent</summary><div class="details-wrapper"><details class="form-wrapper" open="open"><summary role="button" aria-expanded>Child</summary><div class="details-wrapper">Subchild</div></details> -</div></details> -', '#attached' => array( 'js' => array( array('type' => 'setting', 'data' => array('foo' => 'bar')) @@ -618,7 +615,17 @@ function testDrupalRenderChildrenPostRenderCache() { ) ), ); - $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.'); + + $dom = filter_dom_load($cached_element['#markup']); + $xpath = new \DOMXPath($dom); + $parent = $xpath->query('//details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Parent"]')->length; + $child = $xpath->query('//details[@class="form-wrapper" and @open="open"]/div[@class="details-wrapper"]/details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Child"]')->length; + $subchild = $xpath->query('//details[@class="form-wrapper" and @open="open"]/div[@class="details-wrapper"]/details[@class="form-wrapper" and @open="open"]/div [@class="details-wrapper" and text()="Subchild"]')->length; + $this->assertTrue($parent && $child && $subchild, 'The correct data is cached: the stored #markup is not affected by #post_render_cache callbacks.'); + + // Remove markup because it's compared above in the xpath. + unset($cached_element['#markup']); + $this->assertIdentical($cached_element, $expected_element, 'The correct data is cached: the stored #attached properties are not affected by #post_render_cache callbacks.'); // GET request: #cache enabled, cache hit. drupal_static_reset('_drupal_add_js'); @@ -674,9 +681,6 @@ function testDrupalRenderChildrenPostRenderCache() { $cached_parent_element = cache()->get(drupal_render_cid_create($element))->data; $cached_child_element = cache()->get(drupal_render_cid_create($element['child']))->data; $expected_parent_element = array( - '#markup' => '<details class="form-wrapper" open="open"><summary role="button" aria-expanded>Parent</summary><div class="details-wrapper"><details class="form-wrapper" open="open"><summary role="button" aria-expanded>Child</summary><div class="details-wrapper">Subchild</div></details> -</div></details> -', '#attached' => array( 'js' => array( array('type' => 'setting', 'data' => array('foo' => 'bar')) @@ -694,10 +698,19 @@ function testDrupalRenderChildrenPostRenderCache() { ) ), ); - $this->assertIdentical($cached_parent_element, $expected_parent_element, 'The correct data is cached for the parent: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.'); + + $dom = filter_dom_load($cached_parent_element['#markup']); + $xpath = new \DOMXPath($dom); + $parent = $xpath->query('//details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Parent"]')->length; + $child = $xpath->query('//details[@class="form-wrapper" and @open="open"]/div[@class="details-wrapper"]/details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Child"]')->length; + $subchild = $xpath->query('//details[@class="form-wrapper" and @open="open"]/div[@class="details-wrapper"]/details[@class="form-wrapper" and @open="open"]/div [@class="details-wrapper" and text()="Subchild"]')->length; + $this->assertTrue($parent && $child && $subchild, 'The correct data is cached for the parent: the stored #markup is not affected by #post_render_cache callbacks.'); + + // Remove markup because it's compared above in the xpath. + unset($cached_parent_element['#markup']); + $this->assertIdentical($cached_parent_element, $expected_parent_element, 'The correct data is cached for the parent: the stored #attached properties are not affected by #post_render_cache callbacks.'); + $expected_child_element = array( - '#markup' => '<details class="form-wrapper" open="open"><summary role="button" aria-expanded>Child</summary><div class="details-wrapper">Subchild</div></details> -', '#attached' => array( 'library' => array( array('system', 'drupal.collapse'), @@ -710,7 +723,16 @@ function testDrupalRenderChildrenPostRenderCache() { ) ), ); - $this->assertIdentical($cached_child_element, $expected_child_element, 'The correct data is cached for the child: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.'); + + $dom = filter_dom_load($cached_child_element['#markup']); + $xpath = new \DOMXPath($dom); + $child = $xpath->query('//details[@class="form-wrapper" and @open="open"]/summary[@role="button" and @aria-expanded and text()="Child"]')->length; + $subchild = $xpath->query('//details[@class="form-wrapper" and @open="open"]/div [@class="details-wrapper" and text()="Subchild"]')->length; + $this->assertTrue($child && $subchild, 'The correct data is cached for the child: the stored #markup is not affected by #post_render_cache callbacks.'); + + // Remove markup because it's compared above in the xpath. + unset($cached_child_element['#markup']); + $this->assertIdentical($cached_child_element, $expected_child_element, 'The correct data is cached for the child: the stored #attached properties are not affected by #post_render_cache callbacks.'); // GET request: #cache enabled, cache hit, parent element. drupal_static_reset('_drupal_add_js'); diff --git a/core/modules/system/templates/details.html.twig b/core/modules/system/templates/details.html.twig new file mode 100644 index 000000000000..17ea820dd7ab --- /dev/null +++ b/core/modules/system/templates/details.html.twig @@ -0,0 +1,33 @@ +{# +/** + * @file + * Default theme implementation for a details element. + * + * Available variables + * - attributes: A list of HTML attributes for the details element. + * - title: (optional) The title of the element, may not be set. + * - description: (optional) The description of the element, may not be set. + * - children: (optional) The children of the element, may not be set. + * - value: (optional) The value of the element, may not be set. + * + * @see template_preprocess_details() + * + * @ingroup themeable + */ +#} +<details{{ attributes }}> + {%- if title -%} + <summary{{ summary_attributes }}>{{ title }}</summary> + {%- endif -%} + <div class="details-wrapper"> + {%- if description -%} + <div class="details-description">{{ description }}</div> + {%- endif -%} + {%- if children -%} + {{ children }} + {%- endif -%} + {%- if value -%} + {{ value }} + {%- endif -%} + </div> +</details> -- GitLab