From 7f766377f2c4629c2a58f4a73631e39923e7e51f Mon Sep 17 00:00:00 2001 From: Mark Carver Date: Mon, 1 Jul 2013 23:39:18 -0500 Subject: Issue #1903746 by Mark Carver, mgifford, tkoleary, DaneMacaulay: Replace the views grid table template with one using divs --- .../views/config/schema/views.style.schema.yml | 17 +- core/modules/views/css/views.module.css | 10 ++ .../lib/Drupal/views/Plugin/views/style/Grid.php | 64 +++++--- .../Drupal/views/Tests/Plugin/StyleGridTest.php | 107 +++++++++++++ .../views/templates/views-view-grid.html.twig | 51 +++--- .../test_views/views.view.test_grid.yml | 71 ++++++++ core/modules/views/views.theme.inc | 178 ++++++++++----------- 7 files changed, 362 insertions(+), 136 deletions(-) create mode 100644 core/modules/views/lib/Drupal/views/Tests/Plugin/StyleGridTest.php create mode 100644 core/modules/views/tests/modules/views_test_config/test_views/views.view.test_grid.yml diff --git a/core/modules/views/config/schema/views.style.schema.yml b/core/modules/views/config/schema/views.style.schema.yml index 15dae70..ef1aed4 100644 --- a/core/modules/views/config/schema/views.style.schema.yml +++ b/core/modules/views/config/schema/views.style.schema.yml @@ -29,15 +29,24 @@ views.style.grid: columns: type: integer label: 'Number of columns' + automatic_width: + type: boolean + label: 'Automatic width' alignment: type: string label: 'Alignment' - fill_single_line: + row_class_custom: + type: string + label: 'Custom row classes' + row_class_default: type: boolean - label: 'Fill up single line' - summary: + label: 'Default views row classes' + col_class_custom: type: string - label: 'Table summary' + label: 'Custom column classes' + col_class_default: + type: boolean + label: 'Default views column classes' views.style.table: type: views_style diff --git a/core/modules/views/css/views.module.css b/core/modules/views/css/views.module.css index 7ca2f49..851a244 100644 --- a/core/modules/views/css/views.module.css +++ b/core/modules/views/css/views.module.css @@ -22,3 +22,13 @@ .view .progress-disabled { float: none; } + +/* grid style column align */ +.views-view-grid .views-col { + float: left; +} +.views-view-grid .views-row { + clear: both; + float: left; + width: 100%; +} diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/Grid.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/Grid.php index 0631661..cb45df6 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/style/Grid.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/Grid.php @@ -33,28 +33,22 @@ class Grid extends StylePluginBase { protected $usesRowPlugin = TRUE; /** - * Does the style plugin support custom css class for the rows. - * - * @var bool - */ - protected $usesRowClass = TRUE; - - /** - * Set default options + * {@inheritdoc} */ protected function defineOptions() { $options = parent::defineOptions(); - $options['columns'] = array('default' => '4'); + $options['automatic_width'] = array('default' => TRUE); $options['alignment'] = array('default' => 'horizontal'); - $options['fill_single_line'] = array('default' => TRUE, 'bool' => TRUE); - $options['summary'] = array('default' => ''); - + $options['col_class_custom'] = array('default' => ''); + $options['col_class_default'] = array('default' => TRUE); + $options['row_class_custom'] = array('default' => ''); + $options['row_class_default'] = array('default' => TRUE); return $options; } /** - * Render the given style. + * {@inheritdoc} */ public function buildOptionsForm(&$form, &$form_state) { parent::buildOptionsForm($form, $form_state); @@ -63,7 +57,13 @@ public function buildOptionsForm(&$form, &$form_state) { '#title' => t('Number of columns'), '#default_value' => $this->options['columns'], '#required' => TRUE, - '#min' => 0, + '#min' => 1, + ); + $form['automatic_width'] = array( + '#type' => 'checkbox', + '#title' => t('Automatic width'), + '#description' => t('The width of each column will be calculated automatically based on the number of columns provided. If additional classes are entered or a theme injects classes based on a grid system, disabling this option may prove beneficial.'), + '#default_value' => $this->options['automatic_width'], ); $form['alignment'] = array( '#type' => 'radios', @@ -72,20 +72,36 @@ public function buildOptionsForm(&$form, &$form_state) { '#default_value' => $this->options['alignment'], '#description' => t('Horizontal alignment will place items starting in the upper left and moving right. Vertical alignment will place items starting in the upper left and moving down.'), ); - - $form['fill_single_line'] = array( + $form['col_class_default'] = array( + '#title' => t('Default column classes'), + '#description' => t('Add the default views column classes like views-col, col-1 and clearfix to the output. You can use this to quickly reduce the amount of markup the view provides by default, at the cost of making it more difficult to apply CSS.'), '#type' => 'checkbox', - '#title' => t('Fill up single line'), - '#description' => t('If you disable this option, a grid with only one row will have the same number of table cells () as items. Disabling it can cause problems with your CSS.'), - '#default_value' => !empty($this->options['fill_single_line']), + '#default_value' => $this->options['col_class_default'], ); - - $form['summary'] = array( + $form['col_class_custom'] = array( + '#title' => t('Custom column class'), + '#description' => t('Additional classes to provide on each column. Separated by a space.'), + '#type' => 'textfield', + '#default_value' => $this->options['col_class'], + ); + if ($this->usesFields()) { + $form['col_class_custom']['#description'] .= ' ' . t('You may use field tokens from as per the "Replacement patterns" used in "Rewrite the output of this field" for all fields.'); + } + $form['row_class_default'] = array( + '#title' => t('Default row classes'), + '#description' => t('Adds the default views row classes like views-row, row-1 and clearfix to the output. You can use this to quickly reduce the amount of markup the view provides by default, at the cost of making it more difficult to apply CSS.'), + '#type' => 'checkbox', + '#default_value' => $this->options['row_class_default'], + ); + $form['row_class_custom'] = array( + '#title' => t('Custom row class'), + '#description' => t('Additional classes to provide on each row. Separated by a space.'), '#type' => 'textfield', - '#title' => t('Table summary'), - '#description' => t('This value will be displayed as table-summary attribute in the html. Set this for better accessiblity of your site.'), - '#default_value' => $this->options['summary'], + '#default_value' => $this->options['row_class_custom'], ); + if ($this->usesFields()) { + $form['row_class_custom']['#description'] .= ' ' . t('You may use field tokens from as per the "Replacement patterns" used in "Rewrite the output of this field" for all fields.'); + } } } diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleGridTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleGridTest.php new file mode 100644 index 0000000..da2b95c --- /dev/null +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleGridTest.php @@ -0,0 +1,107 @@ + 'Style: Grid', + 'description' => 'Tests the grid style plugin.', + 'group' => 'Views Plugins', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + $this->enableViewsTestModule(); + } + + /** + * Tests the grid style. + */ + public function testGrid() { + $view = views_get_view('test_grid'); + foreach (array('horizontal', 'vertical') as $alignment) { + $this->assertGrid($view, $alignment, 5); + $this->assertGrid($view, $alignment, 4); + $this->assertGrid($view, $alignment, 3); + $this->assertGrid($view, $alignment, 2); + $this->assertGrid($view, $alignment, 1); + } + } + + /** + * Generates a grid and asserts that it is displaying correctly. + * + * @param \Drupal\views\ViewExecutable $view + * The executable to prepare. + * @param string $alignment + * The alignment of the grid to test. + * @param int $columns + * The number of columns in the grid to test. + */ + protected function assertGrid(ViewExecutable $view, $alignment, $columns) { + $view->setDisplay('default'); + $view->initStyle(); + $view->initHandlers(); + $view->initQuery(); + $view->style_plugin->options['alignment'] = $alignment; + $view->style_plugin->options['columns'] = $columns; + $this->executeView($view); + $output = $view->preview(); + $output = drupal_render($output); + $this->drupalSetContent($output, 'internal://test-grid'); + if (!in_array($alignment, $this->alignmentsTested)) { + $result = $this->xpath('//div[contains(@class, "views-view-grid") and contains(@class, :alignment) and contains(@class, :columns)]', array(':alignment' => $alignment, ':columns' => 'cols-' . $columns)); + $this->assertTrue(count($result), ucfirst($alignment) . " grid markup detected."); + $this->alignmentsTested[] = $alignment; + } + $width = '0'; + switch ($columns) { + case 5: $width = '20'; break; + case 4: $width = '25'; break; + case 3: $width = '33.3333'; break; + case 2: $width = '50'; break; + case 1: $width = '100'; break; + } + // Ensure last column exists. + $result = $this->xpath('//div[contains(@class, "views-col") and contains(@class, :columns) and starts-with(@style, :width)]', array(':columns' => 'col-' . $columns, ':width' => 'width: ' . $width)); + $this->assertTrue(count($result), ucfirst($alignment) . " $columns column grid: last column exists and automatic width calculated correctly."); + // Ensure no extra columns were generated. + $result = $this->xpath('//div[contains(@class, "views-col") and contains(@class, :columns)]', array(':columns' => 'col-' . ($columns + 1))); + $this->assertFalse(count($result), ucfirst($alignment) . " $columns column grid: no extraneous columns exist."); + } + +} diff --git a/core/modules/views/templates/views-view-grid.html.twig b/core/modules/views/templates/views-view-grid.html.twig index 8b63792..b96b694 100644 --- a/core/modules/views/templates/views-view-grid.html.twig +++ b/core/modules/views/templates/views-view-grid.html.twig @@ -4,32 +4,47 @@ * Default theme implementation for views to display rows in a grid. * * Available variables: - * - attributes: HTML attributes for the table element. + * - attributes: HTML attributes for the wrapping div element. * - title: The title of this group of rows. - * - rows: A list of rows. Each row contains a list of columns. - * - row_classes: HTML classes for each row including the row number and first - * or last. - * - column_classes: HTML classes for each column including the row number and - * first or last. + * - view: The view object. + * - rows: The rendered view results array. + * - options: The view plugin style options. + * - items: Contains a nested array of grid items. The structure of this array + * - depends on the value of options.alignment. + * - row_attributes: HTML attributes for each grid row. The structure of this + * array depends on the value of options.alignment. + * - col_attributes: HTML attributes for each grid column. The structure of this + * array depends on the value of options.alignment. * * @see template_preprocess_views_view_grid() * * @ingroup themeable + * @ingroup views_templates */ #} {% if title %}

{{ title }}

{% endif %} - - - {% for row_number, columns in rows %} - - {% for column_number, item in columns %} - - {{ item }} - - {% endfor %} - + +{% if options.alignment == 'horizontal' %} + {% for row_key, row in items %} + + {% for col_key, col in row %} + + {{ items[row_key][col_key] }} + {% endfor %} - - + + {% endfor %} +{% else %} + {% for col_key, column in items %} + + {% for row_key, row in column %} + + {{ items[col_key][row_key] }} + + {% endfor %} + + {% endfor %} +{% endif %} + diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_grid.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_grid.yml new file mode 100644 index 0000000..c2a8915 --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_grid.yml @@ -0,0 +1,71 @@ +base_table: views_test_data +core: '8' +description: '' +status: '1' +display: + default: + display_options: + defaults: + fields: '0' + pager: '0' + pager_options: '0' + sorts: '0' + fields: + age: + field: age + id: age + relationship: none + table: views_test_data + plugin_id: numeric + id: + field: id + id: id + relationship: none + table: views_test_data + plugin_id: numeric + name: + field: name + id: name + relationship: none + table: views_test_data + plugin_id: string + pager: + options: + offset: '0' + type: none + pager_options: { } + sorts: + id: + field: id + id: id + order: ASC + relationship: none + table: views_test_data + plugin_id: numeric + style: + type: grid + options: + grouping: { } + columns: '4' + automatic_width: '1' + alignment: horizontal + col_class_default: '1' + col_class_custom: '' + row_class_default: '1' + row_class_custom: '' + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: '0' + page_1: + display_options: + path: test-grid + display_plugin: page + display_title: 'Page display' + id: page_1 + position: '1' +label: '' +id: test_grid +tag: '' diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index 444633b..b14498a 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -746,56 +746,98 @@ function template_preprocess_views_view_table(&$vars) { * - rows: An array of row items. Each row is an array of content. */ function template_preprocess_views_view_grid(&$vars) { - $view = $vars['view']; - $options = $view->style_plugin->options; - $handler = $view->style_plugin; - $default_row_class = isset($options['default_row_class']) ? $options['default_row_class'] : TRUE; - $row_class_special = isset($options['row_class_special']) ? $options['row_class_special'] : TRUE; + $options = $vars['options'] = $vars['view']->style_plugin->options; + $horizontal = ($options['alignment'] === 'horizontal'); + + $vars['attributes'] = new Attribute(array( + 'class' => array( + 'views-view-grid', + $options['alignment'], + 'cols-' . $options['columns'], + 'clearfix', + ), + )); - $columns = $options['columns']; - $vars['attributes']['class'][] = 'views-view-grid cols-' . $columns; - - $rows = array(); - $row_indexes = array(); - - if ($options['alignment'] == 'horizontal') { - $row = array(); - $col_count = 0; - $row_count = 0; - $count = 0; - foreach ($vars['rows'] as $row_index => $item) { - $count++; - $row[] = $item; - $row_indexes[$row_count][$col_count] = $row_index; - $col_count++; - if ($count % $columns == 0) { - $rows[] = $row; - $row = array(); - $col_count = 0; - $row_count++; + $col = 0; + $row = 0; + $items = array(); + $remainders = count($vars['rows']) % $options['columns']; + $num_rows = floor(count($vars['rows']) / $options['columns']); + // Iterate over the view rows array. + foreach ($vars['rows'] as $row_index => $item) { + // Add the current views data row to the rows array. + if ($horizontal) { + $items[$row][$col] = $item; + } + else { + $items[$col][$row] = $item; + } + + // Create attributes for row. + $row_attributes = array('class' => array()); + // Add default views row classes. + if ($options['row_class_default']) { + $row_attributes['class'][] = 'views-row'; + $row_attributes['class'][] = 'row-' . ($row + 1); + if ($horizontal) { + $row_attributes['class'][] = 'clearfix'; } } - if ($row) { - // Fill up the last line only if it's configured, but this is default. - if (!empty($handler->options['fill_single_line']) && count($rows)) { - for ($i = 0; $i < ($columns - $col_count); $i++) { - $row[] = ''; - } + // Add custom row classes. + $row_class = array_filter(explode(' ', $options['row_class_custom'])); + if (!empty($row_class)) { + $row_attributes['class'] = array_merge($row_attributes['class'], $row_class); + } + // Add row attributes to variables array. + if ($horizontal) { + $vars['row_attributes'][$row] = new Attribute($row_attributes); + } + else { + $vars['row_attributes'][$col][$row] = new Attribute($row_attributes); + } + + // Create attributes for columns. + $col_attributes = array('class' => array()); + // Add default views column classes. + if ($options['col_class_default']) { + $col_attributes['class'][] = 'views-col'; + $col_attributes['class'][] = 'col-' . ($col + 1); + if (!$horizontal) { + $col_attributes['class'][] = 'clearfix'; } - $rows[] = $row; } - } - else { - $num_rows = floor(count($vars['rows']) / $columns); - // The remainders are the 'odd' columns that are slightly longer. - $remainders = count($vars['rows']) % $columns; - $row = 0; - $col = 0; - foreach ($vars['rows'] as $count => $item) { - $rows[$row][$col] = $item; - $row_indexes[$row][$col] = $count; - $row++; + // Add custom column classes. + $col_class = array_filter(explode(' ', $options['col_class_custom'])); + if (!empty($col_class)) { + $col_attributes['class'] = array_merge($col_attributes['class'], $col_class); + } + // Add width to columns. + if ($options['automatic_width']) { + $col_attributes['style'] = 'width: ' . (100 / $options['columns']) . '%;'; + } + // Add column attributes to variables array. + if ($horizontal) { + $vars['col_attributes'][$row][$col] = new Attribute($col_attributes); + } + else { + $vars['col_attributes'][$col] = new Attribute($col_attributes); + } + // Increase, decrease or reset the appropriate integers. + if ($horizontal) { + if ($col == 0 && $col != ($options['columns'] - 1)) { + $col++; + } + elseif ($col >= ($options['columns'] - 1)) { + $col = 0; + $row++; + } + else { + $col++; + } + } + else { + $row++; if (!$remainders && $row == $num_rows) { $row = 0; $col++; @@ -806,53 +848,9 @@ function template_preprocess_views_view_grid(&$vars) { $remainders--; } } - for ($i = 0; $i < count($rows[0]); $i++) { - // This should be a string so this is ok. - if (!isset($rows[count($rows) - 1][$i])) { - $rows[count($rows) - 1][$i] = ''; - } - } - } - - // Apply the row classes. - foreach ($rows as $row_number => $row) { - $row_classes = array(); - if ($default_row_class) { - $row_classes['class'][] = 'row-' . ($row_number + 1); - } - if ($row_class_special) { - if ($row_number == 0) { - $row_classes['class'][] = 'row-first'; - } - if (count($rows) == ($row_number + 1)) { - $row_classes['class'][] = 'row-last'; - } - } - $row_classes = new Attribute($row_classes); - - foreach ($rows[$row_number] as $column_number => $item) { - $vars['column_classes'][$row_number][$column_number] = array(); - if ($default_row_class) { - $vars['column_classes'][$row_number][$column_number]['class'][] = 'col-' . ($column_number + 1); - } - if ($row_class_special) { - if ($column_number == 0) { - $vars['column_classes'][$row_number][$column_number]['class'][] = 'col-first'; - } - elseif (count($rows[$row_number]) == ($column_number + 1)) { - $vars['column_classes'][$row_number][$column_number]['class'][] = 'col-last'; - } - } - if (isset($row_indexes[$row_number][$column_number]) && $column_class = $view->style_plugin->getRowClass($row_indexes[$row_number][$column_number])) { - $vars['column_classes'][$row_number][$column_number]['class'][] = $column_class; - } - $vars['column_classes'][$row_number][$column_number] = new Attribute($vars['column_classes'][$row_number][$column_number]); - } - } - $vars['rows'] = $rows; - if (!empty($handler->options['summary'])) { - $vars['attributes']['summary'] = $handler->options['summary']; } + // Save the grid items to the variables array. + $vars['items'] = $items; } /** -- 1.8.2