Commit 2250c09a authored by webchick's avatar webchick

Issue #1939008 by sun, joelpittet, gnuget, Gokul N K, sphism, drupalninja99,...

Issue #1939008 by sun, joelpittet, gnuget, Gokul N K, sphism, drupalninja99, c4rl, Cottser, mdrummond, long wave, steveoliver, andypost, Fabianx | jenlampton: Convert theme_table() to Twig.
parent 96597bbe
......@@ -1411,9 +1411,11 @@ function drupal_pre_render_table(array $element) {
}
/**
* Returns HTML for a table.
* Prepares variables for table templates.
*
* @param $variables
* Default template: table.html.twig.
*
* @param array $variables
* An associative array containing:
* - header: An array containing the table headers. Each element of the array
* can be either a localized string or an associative array with the
......@@ -1495,83 +1497,66 @@ function drupal_pre_render_table(array $element) {
* - empty: The message to display in an extra row if table does not have any
* rows.
*/
function theme_table($variables) {
$header = $variables['header'];
$rows = $variables['rows'];
$attributes = $variables['attributes'];
$caption = $variables['caption'];
$colgroups = $variables['colgroups'];
$sticky = $variables['sticky'];
$responsive = $variables['responsive'];
$empty = $variables['empty'];
$output = '<table' . new Attribute($attributes) . ">\n";
if (isset($caption)) {
$output .= '<caption>' . $caption . "</caption>\n";
}
function template_preprocess_table(&$variables) {
$is_sticky = !empty($variables['sticky']);
$is_responsive = !empty($variables['responsive']);
// Format the table columns:
if (count($colgroups)) {
foreach ($colgroups as $colgroup) {
$attributes = array();
if (!empty($variables['colgroups'])) {
foreach ($variables['colgroups'] as &$colgroup) {
// Check if we're dealing with a simple or complex column
if (isset($colgroup['data'])) {
foreach ($colgroup as $key => $value) {
if ($key == 'data') {
$cols = $value;
}
else {
$attributes[$key] = $value;
}
}
$cols = $colgroup['data'];
unset($colgroup['data']);
$colgroup_attributes = $colgroup;
}
else {
$cols = $colgroup;
$colgroup_attributes = array();
}
// Build colgroup
if (is_array($cols) && count($cols)) {
$output .= ' <colgroup' . new Attribute($attributes) . '>';
foreach ($cols as $col) {
$output .= ' <col' . new Attribute($col) . ' />';
$colgroup = array();
$colgroup['attributes'] = new Attribute($colgroup_attributes);
$colgroup['cols'] = array();
// Build columns.
if (is_array($cols) && !empty($cols)) {
foreach ($cols as $col_key => $col) {
$colgroup['cols'][$col_key]['attributes'] = new Attribute($col);
}
$output .= " </colgroup>\n";
}
else {
$output .= ' <colgroup' . new Attribute($attributes) . " />\n";
}
}
}
// Add the 'empty' row message if available.
if (!count($rows) && $empty) {
if (empty($variables['rows']) && isset($variables['empty'])) {
$header_count = 0;
foreach ($header as $header_cell) {
if (is_array($header_cell)) {
$header_count += isset($header_cell['colspan']) ? $header_cell['colspan'] : 1;
foreach ($variables['header'] as $header_cell) {
if (is_array($header_cell) && isset($header_cell['colspan'])) {
$header_count += $header_cell['colspan'];
}
else {
$header_count++;
}
}
$rows[] = array(array('data' => $empty, 'colspan' => $header_count, 'class' => array('empty', 'message')));
$variables['rows'][] = array(array(
'data' => $variables['empty'],
'colspan' => $header_count,
'class' => array('empty', 'message'),
));
}
$responsive = array();
// Build an associative array of responsive classes keyed by column.
$responsive_classes = array();
// Format the table header:
if (count($header)) {
$ts = tablesort_init($header);
// HTML requires that the thead tag has tr tags in it followed by tbody
// tags. Using ternary operator to check and see if we have any rows.
$output .= (count($rows) ? ' <thead><tr>' : ' <tr>');
$i = 0;
foreach ($header as $cell) {
$i++;
$ts = array();
if (!empty($variables['header'])) {
$ts = tablesort_init($variables['header']);
foreach ($variables['header'] as $col_key => $cell) {
if (!is_array($cell)) {
$cell_content = $cell;
$cell_attributes = '';
$cell_attributes = new Attribute();
$is_header = TRUE;
}
else {
......@@ -1590,10 +1575,10 @@ function theme_table($variables) {
// must be transferred to the content cells.
if (!empty($cell['class']) && is_array($cell['class'])) {
if (in_array(RESPONSIVE_PRIORITY_MEDIUM, $cell['class'])) {
$responsive[$i] = RESPONSIVE_PRIORITY_MEDIUM;
$responsive_classes[$col_key] = RESPONSIVE_PRIORITY_MEDIUM;
}
elseif (in_array(RESPONSIVE_PRIORITY_LOW, $cell['class'])) {
$responsive[$i] = RESPONSIVE_PRIORITY_LOW;
$responsive_classes[$col_key] = RESPONSIVE_PRIORITY_LOW;
}
}
......@@ -1601,54 +1586,50 @@ function theme_table($variables) {
$cell_content = drupal_render($cell_content);
}
tablesort_header($cell_content, $cell, $header, $ts);
tablesort_header($cell_content, $cell, $variables['header'], $ts);
// tablesort_header() removes the 'sort' and 'field' keys.
$cell_attributes = new Attribute($cell);
}
$cell_tag = $is_header ? 'th' : 'td';
$output .= '<' . $cell_tag . $cell_attributes . '>' . $cell_content . '</' . $cell_tag . '>';
$variables['header'][$col_key] = array();
$variables['header'][$col_key]['tag'] = $is_header ? 'th' : 'td';
$variables['header'][$col_key]['attributes'] = $cell_attributes;
$variables['header'][$col_key]['content'] = $cell_content;
}
// Using ternary operator to close the tags based on whether or not there are rows
$output .= (count($rows) ? " </tr></thead>\n" : "</tr>\n");
}
else {
$ts = array();
}
// Format the table rows:
if (count($rows)) {
$output .= "<tbody>\n";
if (!empty($variables['rows'])) {
$flip = array('even' => 'odd', 'odd' => 'even');
$class = 'even';
foreach ($rows as $row) {
foreach ($variables['rows'] as $row_key => $row) {
// Check if we're dealing with a simple or complex row
if (isset($row['data'])) {
$cells = $row['data'];
$no_striping = isset($row['no_striping']) ? $row['no_striping'] : FALSE;
// Set the attributes array and exclude 'data' and 'no_striping'.
$attributes = $row;
unset($attributes['data']);
unset($attributes['no_striping']);
$row_attributes = $row;
unset($row_attributes['data']);
unset($row_attributes['no_striping']);
}
else {
$cells = $row;
$attributes = array();
$row_attributes = array();
$no_striping = FALSE;
}
if (count($cells)) {
// Add odd/even class
if (!$no_striping) {
$class = $flip[$class];
$attributes['class'][] = $class;
}
// Build row
$output .= ' <tr' . new Attribute($attributes) . '>';
$i = 0;
foreach ($cells as $cell) {
$i++;
// Add odd/even class.
if (!$no_striping) {
$class = $flip[$class];
$row_attributes['class'][] = $class;
}
// Build row.
$variables['rows'][$row_key] = array();
$variables['rows'][$row_key]['attributes'] = new Attribute($row_attributes);
$variables['rows'][$row_key]['cells'] = array();
if (!empty($cells)) {
foreach ($cells as $col_key => $cell) {
if (!is_array($cell)) {
$cell_content = $cell;
$cell_attributes = array();
......@@ -1671,26 +1652,22 @@ function theme_table($variables) {
}
}
// Add active class if needed for sortable tables.
if (isset($header[$i]['data']) && $header[$i]['data'] == $ts['name'] && !empty($header[$i]['field'])) {
if (isset($variables['header'][$col_key]['data']) && $variables['header'][$col_key]['data'] == $ts['name'] && !empty($variables['header'][$col_key]['field'])) {
$cell_attributes['class'][] = 'active';
}
// Copy RESPONSIVE_PRIORITY_LOW/RESPONSIVE_PRIORITY_MEDIUM
// class from header to cell as needed.
if (isset($responsive[$i])) {
$cell_attributes['class'][] = $responsive[$i];
if (isset($responsive_classes[$col_key])) {
$cell_attributes['class'][] = $responsive_classes[$col_key];
}
$cell_tag = $is_header ? 'th' : 'td';
$output .= '<' . $cell_tag . new Attribute($cell_attributes) . '>' . $cell_content . '</' . $cell_tag . '>';
$variables['rows'][$row_key]['cells'][$col_key]['tag'] = $is_header ? 'th' : 'td';
$variables['rows'][$row_key]['cells'][$col_key]['attributes'] = new Attribute($cell_attributes);
$variables['rows'][$row_key]['cells'][$col_key]['content'] = $cell_content;
}
$output .= " </tr>\n";
}
}
$output .= "</tbody>\n";
}
$output .= "</table>\n";
return $output;
}
/**
......@@ -2577,6 +2554,7 @@ function drupal_common_theme() {
),
'table' => array(
'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'sticky' => FALSE, 'responsive' => TRUE, 'empty' => ''),
'template' => 'table',
),
'tablesort_indicator' => array(
'variables' => array('style' => NULL),
......
......@@ -9,6 +9,7 @@
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\Entity\ConfigEntityStorage;
use Drupal\simpletest\WebTestBase;
......@@ -106,8 +107,12 @@ public function testConfigurationRename() {
$entity_type = \Drupal::entityManager()->getDefinition($config_entity_type);
$old_id = ConfigEntityStorage::getIDFromConfigName($names['old_name'], $entity_type->getConfigPrefix());
$new_id = ConfigEntityStorage::getIDFromConfigName($names['new_name'], $entity_type->getConfigPrefix());
$this->assertText('-' . $entity_type->getKey('id') . ': ' . $old_id);
$this->assertText('+' . $entity_type->getKey('id') . ': ' . $new_id);
$id_key = $entity_type->getKey('id');
$text = "$id_key: $old_id";
$this->assertTextPattern('/\-\s+' . preg_quote($text, '/') . '/', "'-$text' found.");
$text = "$id_key: $new_id";
$this->assertTextPattern('/\+\s+' . preg_quote($text, '/') . '/', "'+$text' found.");
}
// Run the import.
......@@ -119,4 +124,27 @@ public function testConfigurationRename() {
$this->assertIdentical($staged_type, $content_type->type);
}
/**
* Asserts that a Perl regex pattern is found in the text content.
*
* @param string $pattern
* Perl regex to look for including the regex delimiters.
* @param string $message
* (optional) A message to display with the assertion.
*
* @return bool
* TRUE on pass, FALSE on failure.
*/
protected function assertTextPattern($pattern, $message = NULL) {
// @see WebTestBase::assertTextHelper()
if ($this->plainTextContent === FALSE) {
$this->plainTextContent = Xss::filter($this->drupalGetContent(), array());
}
// @see WebTestBase::assertPattern()
if (!$message) {
$message = String::format('Pattern "@pattern" found', array('@pattern' => $pattern));
}
return $this->assert((bool) preg_match($pattern, $this->plainTextContent), $message);
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Theme;
use Drupal\Component\Utility\String;
use Drupal\simpletest\DrupalUnitTestBase;
/**
......@@ -41,10 +42,10 @@ function testThemeTableStickyHeaders() {
'#rows' => $rows,
'#sticky' => TRUE,
);
$this->content = drupal_render($table);
$this->render($table);
$js = _drupal_add_js();
$this->assertTrue(isset($js['core/misc/tableheader.js']), 'tableheader.js was included when $sticky = TRUE.');
$this->assertRaw('sticky-enabled', 'Table has a class of sticky-enabled when $sticky = TRUE.');
$this->assertTrue(isset($js['core/misc/tableheader.js']), 'tableheader.js found.');
$this->assertRaw('sticky-enabled');
drupal_static_reset('_drupal_add_js');
}
......@@ -66,10 +67,10 @@ function testThemeTableNoStickyHeaders() {
'#colgroups' => $colgroups,
'#sticky' => FALSE,
);
$this->content = drupal_render($table);
$this->render($table);
$js = _drupal_add_js();
$this->assertFalse(isset($js['core/misc/tableheader.js']), 'tableheader.js was not included because $sticky = FALSE.');
$this->assertNoRaw('sticky-enabled', 'Table does not have a class of sticky-enabled because $sticky = FALSE.');
$this->assertFalse(isset($js['core/misc/tableheader.js']), 'tableheader.js not found.');
$this->assertNoRaw('sticky-enabled');
drupal_static_reset('_drupal_add_js');
}
......@@ -79,9 +80,9 @@ function testThemeTableNoStickyHeaders() {
*/
function testThemeTableWithEmptyMessage() {
$header = array(
t('Header 1'),
'Header 1',
array(
'data' => t('Header 2'),
'data' => 'Header 2',
'colspan' => 2,
),
);
......@@ -89,11 +90,12 @@ function testThemeTableWithEmptyMessage() {
'#type' => 'table',
'#header' => $header,
'#rows' => array(),
'#empty' => t('No strings available.'),
'#empty' => 'Empty row.',
);
$this->content = drupal_render($table);
$this->assertRaw('<tr class="odd"><td colspan="3" class="empty message">No strings available.</td>', 'Correct colspan was set on empty message.');
$this->assertRaw('<thead><tr><th>Header 1</th>', 'Table header was printed.');
$this->render($table);
$this->removeWhiteSpace();
$this->assertRaw('<thead><tr><th>Header 1</th><th colspan="2">Header 2</th></tr>', 'Table header found.');
$this->assertRaw('<tr class="odd"><td colspan="3" class="empty message">Empty row.</td>', 'Colspan on #empty row found.');
}
/**
......@@ -110,7 +112,7 @@ function testThemeTableWithNoStriping() {
'#type' => 'table',
'#rows' => $rows,
);
$this->content = drupal_render($table);
$this->render($table);
$this->assertNoRaw('class="odd"', 'Odd/even classes were not added because $no_striping = TRUE.');
$this->assertNoRaw('no_striping', 'No invalid no_striping HTML attribute was printed.');
}
......@@ -130,10 +132,32 @@ function testThemeTableHeaderCellOption() {
'#type' => 'table',
'#rows' => $rows,
);
$this->content = drupal_render($table);
$this->render($table);
$this->removeWhiteSpace();
$this->assertRaw('<th>1</th><td>1</td><td>1</td>', 'The th and td tags was printed correctly.');
}
/**
* Renders a given render array.
*
* @param array $elements
* The render array elements to render.
*
* @return string
* The rendered HTML.
*/
protected function render(array $elements) {
$this->content = drupal_render($elements);
$this->verbose('<pre>' . String::checkPlain($this->content));
}
/**
* Removes all white-space between HTML tags from $this->content.
*/
protected function removeWhiteSpace() {
$this->content = preg_replace('@>\s+<@', '><', $this->content);
}
/**
* Asserts that a raw string appears in $this->content.
*
......
{#
/**
* @file
* Default theme implementation to display a table.
*
* Available variables:
* - attributes: HTML attributes to apply to the <table> tag.
* - caption: A localized string for the <caption> tag.
* - colgroups: Column groups. Each group contains the following properties:
* - attributes: HTML attributes to apply to the <col> tag.
* Note: Drupal currently supports only one table header row, see
* http://drupal.org/node/893530 and
* http://api.drupal.org/api/drupal/includes!theme.inc/function/theme_table/7#comment-5109.
* - header: Table header cells. Each cell contains the following properties:
* - tag: The HTML tag name to use; either TH or TD.
* - attributes: HTML attributes to apply to the tag.
* - content: A localized string for the title of the column.
* - field: Field name (required for column sorting).
* - sort: Default sort order for this column ("asc" or "desc").
* - sticky: A flag indicating whether to use a "sticky" table header.
* - rows: Table rows. Each row contains the following properties:
* - attributes: HTML attributes to apply to the <tr> tag.
* - data: Table cells.
* - no_striping: A flag indicating that the row should receive no
* 'even / odd' styling. Defaults to FALSE.
* - cells: Table cells of the row. Each cell contains the following keys:
* - tag: The HTML tag name to use; either TH or TD.
* - attributes: Any HTML attributes, such as "colspan", to apply to the
* table cell.
* - content: The string to display in the table cell.
* - empty: The message to display in an extra row if table does not have
* any rows.
*
* @see template_preprocess_table()
*
* @ingroup themeable
*/
#}
<table{{ attributes }}>
{% if caption %}
<caption>{{ caption }}</caption>
{% endif %}
{% for colgroup in colgroups %}
{% if colgroup.cols %}
<colgroup{{ colgroup.attributes }}>
{% for col in colgroup.cols %}
<col{{ col.attributes }} />
{% endfor %}
</colgroup>
{% else %}
<colgroup{{ colgroup.attributes }} />
{% endif %}
{% endfor %}
{% if header %}
<thead>
<tr>
{% for cell in header %}
<{{ cell.tag }}{{ cell.attributes }}>
{{- cell.content -}}
</{{ cell.tag }}>
{% endfor %}
</tr>
</thead>
{% endif %}
{% if rows %}
<tbody>
{% for row in rows %}
<tr{{ row.attributes }}>
{% for cell in row.cells %}
<{{ cell.tag }}{{ cell.attributes }}>
{{- cell.content -}}
</{{ cell.tag }}>
{% endfor %}
</tr>
{% endfor %}
</tbody>
{% endif %}
</table>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment