diff --git a/core/modules/views/config/schema/views.data_types.schema.yml b/core/modules/views/config/schema/views.data_types.schema.yml index 0e011d73dc15f54451fbd29ea4f28ca02369877e..61af073c384048663a271bafb7c7f27a259304de 100644 --- a/core/modules/views/config/schema/views.data_types.schema.yml +++ b/core/modules/views/config/schema/views.data_types.schema.yml @@ -532,6 +532,9 @@ views_field: rel: type: string label: 'Rel Text' + aria_label: + type: string + label: 'Aria label Text' link_class: type: string label: 'Link class' diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index b60cfbf199d15a36b4c14cdb2568b7d32628e57f..b4ac416c85cbe7cf14a8bd6eb021a76dfe92b3e1 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -509,6 +509,7 @@ protected function defineOptions() { 'trim_whitespace' => ['default' => FALSE], 'alt' => ['default' => ''], 'rel' => ['default' => ''], + 'aria_label' => ['default' => ''], 'link_class' => ['default' => ''], 'prefix' => ['default' => ''], 'suffix' => ['default' => ''], @@ -893,6 +894,17 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { ], ], ]; + $form['alter']['aria_label'] = [ + '#title' => $this->t('Aria label Text'), + '#type' => 'textfield', + '#default_value' => $this->options['alter']['aria_label'], + '#description' => $this->t('Include Aria label attribute.'), + '#states' => [ + 'visible' => [ + ':input[name="options[alter][make_link]"]' => ['checked' => TRUE], + ], + ], + ]; $form['alter']['prefix'] = [ '#title' => $this->t('Prefix text'), '#type' => 'textfield', @@ -1579,6 +1591,10 @@ protected function renderAsLink($alter, $text, $tokens) { $options['attributes']['rel'] = $rel; } + if (!empty($alter['aria_label']) && $aria_label = $this->viewsTokenReplace($alter['aria_label'], $tokens)) { + $options['attributes']['aria-label'] = $aria_label; + } + $target = trim($this->viewsTokenReplace($alter['target'], $tokens)); if (!empty($target)) { $options['attributes']['target'] = $target; diff --git a/core/modules/views/src/ViewsConfigUpdater.php b/core/modules/views/src/ViewsConfigUpdater.php index 3b2709cc60c2ac7d43d8279bc8bb30c61d5220e4..0a234d4b84963608694c98fb1fe487791b3ea6be 100644 --- a/core/modules/views/src/ViewsConfigUpdater.php +++ b/core/modules/views/src/ViewsConfigUpdater.php @@ -349,4 +349,38 @@ public function processTableCssClassUpdate(ViewEntityInterface $view): bool { return $changed; } + /** + * Checks if 'aria_label' setting of a field handler needs to be updated. + * + * @param \Drupal\views\ViewEntityInterface $view + * The view entity. + * + * @return bool + * TRUE if the view has any fields that need 'aria_label' updates. + */ + public function needsAriaLabelUpdate(ViewEntityInterface $view): bool { + return $this->processDisplayHandlers($view, FALSE, function (&$handler, $handler_type) { + return $this->processAriaLabelUpdate($handler, $handler_type); + }); + } + + /** + * Processes fields and adds an empty 'aria_label' if not set. + * + * @param array $handler + * A display handler. + * @param string $handler_type + * The handler type. + * + * @return bool + * Whether the handler was updated. + */ + public function processAriaLabelUpdate(array &$handler, string $handler_type): bool { + if ($handler_type === 'field' && isset($handler['alter']) && !isset($handler['alter']['aria_label'])) { + $handler['alter']['aria_label'] = ''; + return TRUE; + } + return FALSE; + } + } diff --git a/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php b/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php index 72fd5a0cd29708c039609efff418f903a7c8bba2..a7b1659297868aa86620a42b5e22c7a4b13e497d 100644 --- a/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php +++ b/core/modules/views/tests/src/Functional/Handler/FieldWebTest.php @@ -521,7 +521,7 @@ public function testFieldClasses(): void { } /** - * Tests trimming/read-more/ellipses. + * Tests trimming/read-more/ellipses and aria-label rendering. */ public function testTextRendering(): void { /** @var \Drupal\Core\Render\RendererInterface $renderer */ @@ -681,6 +681,21 @@ public function testTextRendering(): void { return $name_field->advancedRender($row); }); $this->assertNotSubString($output, '…', 'No ellipsis should appear if the output is not trimmed'); + + // Tests for the aria-label attribute rendering. + $name_field->options['alter'] = [ + 'make_link' => TRUE, + 'path' => 'test-path', + 'aria_label' => 'Test aria label', + 'external' => FALSE, + 'alt' => '', + 'link_class' => '', + 'target' => '', + ]; + $output = (string) $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) { + return $name_field->advancedRender($row); + }); + $this->assertSubString($output, 'aria-label="Test aria label"', 'Aria label attribute should be present.'); } } diff --git a/core/modules/views/tests/src/Functional/Update/AriaLabelUpdateTest.php b/core/modules/views/tests/src/Functional/Update/AriaLabelUpdateTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2d86e66ccb5bcff2d90d396bd2d8503f37e8c934 --- /dev/null +++ b/core/modules/views/tests/src/Functional/Update/AriaLabelUpdateTest.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\views\Functional\Update; + +use Drupal\FunctionalTests\Update\UpdatePathTestBase; +use Drupal\views\Entity\View; + +/** + * Tests the upgrade path for adding aria labels to views field handler. + * + * @group Update + * @group legacy + * + * @see views_post_update_add_aria_label() + */ +class AriaLabelUpdateTest extends UpdatePathTestBase { + + /** + * {@inheritdoc} + */ + protected function setDatabaseDumpFiles(): void { + $this->databaseDumpFiles = [ + __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gz', + ]; + } + + /** + * Tests that numeric argument plugins are updated properly. + */ + public function testAriaLabelPostUpdate(): void { + $view = View::load('files'); + $data = $view->toArray(); + $this->assertArrayHasKey('alter', $data['display']['default']['display_options']['fields']['filename']); + $this->assertArrayNotHasKey('aria_label', $data['display']['default']['display_options']['fields']['filename']['alter']); + + $this->runUpdates(); + + $view = View::load('files'); + $data = $view->toArray(); + $this->assertEquals('', $data['display']['default']['display_options']['fields']['filename']['alter']['aria_label']); + + } + +} diff --git a/core/modules/views/tests/src/Functional/Update/EntityArgumentUpdateTest.php b/core/modules/views/tests/src/Functional/Update/EntityArgumentUpdateTest.php index d7a96775c55d93cc200daf67262a8224f3d11a0d..7eede1afdc43a12f717c3ed34a3ebef6166d8d6b 100644 --- a/core/modules/views/tests/src/Functional/Update/EntityArgumentUpdateTest.php +++ b/core/modules/views/tests/src/Functional/Update/EntityArgumentUpdateTest.php @@ -11,6 +11,7 @@ * Tests the upgrade path for converting numeric arguments to entity_target_id. * * @group Update + * @group legacy * * @see views_post_update_views_data_argument_plugin_id() */ diff --git a/core/modules/views/views.post_update.php b/core/modules/views/views.post_update.php index c3f352e99a4203c026860c66a84ef43f5d535d4b..77b0e7e90694bf84feca0834072032ddab57e06d 100644 --- a/core/modules/views/views.post_update.php +++ b/core/modules/views/views.post_update.php @@ -87,3 +87,15 @@ function views_post_update_table_css_class(?array &$sandbox = NULL): void { return $view_config_updater->needsTableCssClassUpdate($view); }); } + +/** + * Add ARIA labels to views for better accessibility.. + */ +function views_post_update_add_aria_label(?array &$sandbox = NULL): void { + /** @var \Drupal\views\ViewsConfigUpdater $view_config_updater */ + $view_config_updater = \Drupal::classResolver(ViewsConfigUpdater::class); + $view_config_updater->setDeprecationsEnabled(FALSE); + \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function (ViewEntityInterface $view) use ($view_config_updater): bool { + return $view_config_updater->needsAriaLabelUpdate($view); + }); +}