diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php index 1f8264e..475981f 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/field/FieldPluginBase.php @@ -9,6 +9,7 @@ use Drupal\views\Plugin\views\HandlerBase; use Drupal\views\Plugin\views\display\DisplayPluginBase; +use Drupal\views\Plugin\views\style\StylePluginBase; use Drupal\views\ResultRow; use Drupal\views\ViewExecutable; @@ -265,27 +266,6 @@ public function elementWrapperType($none_supported = FALSE, $default_empty = FAL } /** - * Provide a list of elements valid for field HTML. - * - * This function can be overridden by fields that want more or fewer - * elements available, though this seems like it would be an incredibly - * rare occurence. - */ - public function getElements() { - static $elements = NULL; - if (!isset($elements)) { - // @todo Add possible html5 elements. - $elements = array( - '' => t(' - Use default -'), - '0' => t('- None -') - ); - $elements += \Drupal::config('views.settings')->get('field_rewrite_elements'); - } - - return $elements; - } - - /** * Return the class of the field. */ public function elementClasses($row_index = NULL) { @@ -548,7 +528,7 @@ public function buildOptionsForm(&$form, &$form_state) { ); $form['element_type'] = array( '#title' => t('HTML element'), - '#options' => $this->getElements(), + '#options' => $this->view->style_plugin->getHtmlElementTypes(), '#type' => 'select', '#default_value' => $this->options['element_type'], '#description' => t('Choose the HTML element to wrap around this field, e.g. H1, H2, etc.'), @@ -593,7 +573,7 @@ public function buildOptionsForm(&$form, &$form_state) { ); $form['element_label_type'] = array( '#title' => t('Label HTML element'), - '#options' => $this->getElements(FALSE), + '#options' => $this->view->style_plugin->getHtmlElementTypes(FALSE), '#type' => 'select', '#default_value' => $this->options['element_label_type'], '#description' => t('Choose the HTML element to wrap around this label, e.g. H1, H2, etc.'), @@ -637,7 +617,7 @@ public function buildOptionsForm(&$form, &$form_state) { ); $form['element_wrapper_type'] = array( '#title' => t('Wrapper HTML element'), - '#options' => $this->getElements(FALSE), + '#options' => $this->view->style_plugin->getHtmlElementTypes(FALSE), '#type' => 'select', '#default_value' => $this->options['element_wrapper_type'], '#description' => t('Choose the HTML element to wrap around this field and label, e.g. H1, H2, etc. This may not be used if the field and label are not rendered together, such as with a table.'), diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php index ffda543..1defa09 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php @@ -9,6 +9,7 @@ use Drupal\views\Plugin\views\PluginBase; use Drupal\views\Plugin\views\display\DisplayPluginBase; +use Drupal\views\Plugin\views\field\FieldPluginBase; use Drupal\views\Plugin\views\wizard\WizardInterface; use Drupal\Component\Annotation\Plugin; use Drupal\Core\Annotation\Translation; @@ -95,6 +96,13 @@ protected $groupingTheme = 'views_view_grouping'; /** + * Provides the list of HTML elements available. + * + * @var array + */ + protected $htmlElements; + + /** * Overrides \Drupal\views\Plugin\views\PluginBase::init(). * * The style options might come externally as the style can be sourced from at @@ -226,6 +234,7 @@ public function evenEmpty() { protected function defineOptions() { $options = parent::defineOptions(); $options['grouping'] = array('default' => array()); + $options['title_element_type'] = array('default' => 'h3'); if ($this->usesRowClass()) { $options['row_class'] = array('default' => ''); $options['default_row_class'] = array('default' => TRUE, 'bool' => TRUE); @@ -294,6 +303,20 @@ public function buildOptionsForm(&$form, &$form_state) { ); } } + + $form['title_element_type'] = array( + '#title' => t('Title HTML Element'), + '#type' => 'select', + '#default_value' => $this->options['title_element_type'], + '#options' => $this->getHtmlElementTypes(), + '#description' => t('Choose the HTML element to wrap around the grouping title of a view.'), + // Hide the element unless at least one grouping field is configured. + '#states' => array( + 'invisible' => array( + ':input[name="style_options[grouping][0][field]"]' => array('value' => ''), + ), + ), + ); } if ($this->usesRowClass()) { @@ -376,6 +399,24 @@ public function wizardSubmit(&$form, &$form_state, WizardInterface $wizard, &$di } /** + * Provide a list of elements valid for style/field HTML. + * + * @return array + * An array of human readable descriptions keyed by HTML tag. + */ + public function getHtmlElementTypes() { + if (!isset($this->htmlElements)) { + $this->htmlElements = array( + '' => $this->t(' - Use default -'), + '0' => $this->t('- None -') + ); + $this->htmlElements += \Drupal::config('views.settings')->get('field_rewrite_elements'); + } + + return $this->htmlElements; + } + + /** * Called by the view builder to see if this style handler wants to * interfere with the sorts. If so it should build; if it returns * any non-TRUE value, normal sorting will NOT be added to the query. @@ -464,6 +505,7 @@ public function renderGroupingSets($sets, $level = 0) { '#grouping_level' => $level, '#rows' => $set['rows'], '#title' => $set['group'], + '#title_element_type' => $this->options['title_element_type'], ); } // Render as a record set. @@ -480,6 +522,7 @@ public function renderGroupingSets($sets, $level = 0) { $single_output = $this->renderRowGroup($set['rows']); $single_output['#grouping_level'] = $level; $single_output['#title'] = $set['group']; + $single_output['#title_element_type'] = $this->options['title_element_type']; $output[] = $single_output; } } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php index 121047a..352b206 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/Table.php @@ -99,6 +99,9 @@ protected function defineOptions() { $options['description'] = array('default' => '', 'translatable' => TRUE); $options['empty_table'] = array('default' => FALSE, 'bool' => TRUE); + // Override to set the title to caption by default. + $options['title_element_type']['default'] = 'caption'; + return $options; } diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php index 7bf0e89..0c2b8c9 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldWebTest.php @@ -6,6 +6,7 @@ */ namespace Drupal\views\Tests\Handler; +use Drupal\views\Plugin\views\style\StylePluginBase; /** * Tests fields from within a UI. @@ -403,27 +404,6 @@ public function testFieldClasses() { $this->assertFalse($this->xpathContent($output, "//li[contains(@class, views-row)]//div[contains(@class, views-field)]//{$element_type}[contains(@class, :class)]", array(':class' => $random_class))); $this->assertTrue($this->xpathContent($output, "//li[contains(@class, views-row)]//div[contains(@class, views-field)]//{$element_type}")); } - - // Tests the available html elements. - $element_types = $id_field->getElements(); - $expected_elements = array( - '', - 0, - 'div', - 'span', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'p', - 'strong', - 'em', - 'marquee' - ); - - $this->assertEqual(array_keys($element_types), $expected_elements); } /** diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php index f492a13..52e23d5 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/StyleTest.php @@ -250,6 +250,33 @@ function testCustomRowClasses() { } /** + * Tests the custom element type for group titles. + */ + public function testCustomElementTitle() { + $style_plugins = array('default', 'grid', 'table', 'html_list'); + $view = views_get_view('test_view'); + + foreach ($style_plugins as $plugin) { + $view->initDisplay(); + + $style = $view->display_handler->getOption('style'); + $style['type'] = $plugin; + $style['options']['title_element_type'] = 'h6'; + $style['options']['grouping'] = 'name'; + $view->display_handler->overrideOption('style', $style); + + $view->initStyle(); + + $output = $view->preview(); + $this->drupalSetContent(drupal_render($output)); + $elements = $this->xpath('//h6'); + $this->assertEqual(count($elements), 5, format_string('Custom element tag found for @plugin plugin', array('@plugin' => $plugin))); + + $view->destroy(); + } + } + + /** * Stores a view output in the elements. */ protected function storeViewPreview($output) { diff --git a/core/modules/views/templates/views-view-grid.html.twig b/core/modules/views/templates/views-view-grid.html.twig index a5a813c..61b3977 100644 --- a/core/modules/views/templates/views-view-grid.html.twig +++ b/core/modules/views/templates/views-view-grid.html.twig @@ -6,6 +6,7 @@ * Available variables: * - attributes: HTML attributes for the wrapping element. * - title: The title of this group of rows. + * - title_element_type: The HTML tag element for the title. * - view: The view object. * - rows: The rendered view results. * - options: The view plugin style options. @@ -23,7 +24,7 @@ */ #} {% if title %} -

{{ title }}

+ <{{ title_element_type }}>{{ title }} {% endif %} {% if options.alignment == 'horizontal' %} diff --git a/core/modules/views/templates/views-view-list.html.twig b/core/modules/views/templates/views-view-list.html.twig index 8de787c..88dcc7c 100644 --- a/core/modules/views/templates/views-view-list.html.twig +++ b/core/modules/views/templates/views-view-list.html.twig @@ -9,6 +9,7 @@ * - attributes: The row's HTML attributes. * - content: The row's contents. * - title: The title of this group of rows. May be empty. + * - title_element_type: The HTML tag element for the title. * - list: @todo. * - type: Starting tag will be either a ul or ol. * - attributes: HTML attributes for the list element. @@ -22,7 +23,7 @@ {% endif %} {% if title %} -

{{ title }}

+ <{{ title_element_type }}>{{ title }} {% endif %} <{{ list.type }}{{ list.attributes }}> diff --git a/core/modules/views/templates/views-view-table.html.twig b/core/modules/views/templates/views-view-table.html.twig index 6b0b26c..6b6e5e9 100644 --- a/core/modules/views/templates/views-view-table.html.twig +++ b/core/modules/views/templates/views-view-table.html.twig @@ -7,6 +7,7 @@ * - attributes: Remaining HTML attributes for the element. * - class: HTML classes that can be used to style contextually through CSS. * - title : The title of this group of rows. + * - title_element_type: The HTML tag element for the title. * - header: The table header columns. * - attributes: Remaining HTML attributes for the element. * - content: HTML classes to apply to each header cell, indexed by @@ -32,7 +33,7 @@ {% if caption %} {{ caption }} {% else %} - {{ title }} + <{{ title_element_type }}>{{ title }} {% endif %} {% if (summary is not empty) or (description is not empty) %}
diff --git a/core/modules/views/templates/views-view-unformatted.html.twig b/core/modules/views/templates/views-view-unformatted.html.twig index e3fb2e3..f8b80e8 100644 --- a/core/modules/views/templates/views-view-unformatted.html.twig +++ b/core/modules/views/templates/views-view-unformatted.html.twig @@ -5,6 +5,7 @@ * * Available variables: * - title: The title of this group of rows. May be empty. + * - title_element_type: The HTML tag element for the title. * - rows: A list of the view's row items. * - attributes: The row's HTML attributes. * - content: The row's content. @@ -15,7 +16,7 @@ */ #} {% if title %} -

{{ title }}

+ <{{ title_element_type }}>{{ title }} {% endif %} {% for row in rows %} diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/style/StylePluginBaseTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/style/StylePluginBaseTest.php new file mode 100644 index 0000000..5ff2b90 --- /dev/null +++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/style/StylePluginBaseTest.php @@ -0,0 +1,100 @@ + 'StylePluginBase', + 'description' => 'Tests the base style plugin', + 'group' => 'Views Plugins', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + $container = new ContainerBuilder(); + $container->set('string_translation', $this->getStringTranslationStub()); + \Drupal::setContainer($container); + + $this->style = new TestStylePlugin(array(), 'test_plugin', array()); + } + + /** + * Tests getHtmlElementTypes() + */ + public function testGetHtmlElementTypes() { + $elements = array( + 'header' => 'HEADER', + 'footer' => 'FOOTER', + 'article' => 'ARTICLE', + 'section' => 'SECTION', + 'aside' => 'ASIDE', + 'details' => 'DETAILS', + 'blockquote' => 'BLOCKQUOTE', + 'figure' => 'FIGURE', + 'address' => 'ADDRESS', + 'code' => 'CODE', + 'pre' => 'PRE', + 'var' => 'VAR', + ); + $factory = $this->getConfigFactoryStub(array('views.settings' => array('field_rewrite_elements' => $elements))); + $container = \Drupal::getContainer(); + $container->set('config.factory', $factory); + + // Tests the available html elements. + $element_types = $this->style->getHTMLElementTypes(); + + $expected_elements = array( + '' => 'Default', + 0 => 'None', + ) + $elements; + + $this->assertEquals(array_keys($element_types), array_keys($expected_elements)); + } + + /** + * {@inheritdoc} + */ + protected function tearDown() { + \Drupal::setContainer(new ContainerBuilder()); + } + + +} + +/** + * A concrete instance of a style plugin. + */ +class TestStylePlugin extends StylePluginBase { + +} diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 8445804..688738f 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -71,7 +71,7 @@ function views_theme($existing, $type, $theme, $path) { // $view is an object but the core contextual_preprocess() function only // attaches contextual links when the primary theme argument is an array. 'display' => array('view_array' => array(), 'view' => NULL), - 'style' => array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL), + 'style' => array('view' => NULL, 'options' => NULL, 'rows' => NULL, 'title' => NULL, 'title_element_type' => NULL), 'row' => array('view' => NULL, 'options' => NULL, 'row' => NULL, 'field_alias' => NULL), 'exposed_form' => array('view' => NULL, 'options' => NULL), 'pager' => array( diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index 6c6cd0a..2f4eb77 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -739,7 +739,7 @@ function template_preprocess_views_view_table(&$variables) { $variables['summary'] = $handler->options['summary']; $variables['description'] = $handler->options['description']; - $variables['caption_needed'] |= !empty($variables['summary']) || !empty($variables['description']); + $variables['caption_needed'] |= !empty($variables['summary']) || !empty($variables['description']) || !empty($variables['title']); // If the table has headers and it should react responsively to columns hidden // with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM