Loading core/misc/cspell/dictionary.txt +2 −0 Original line number Diff line number Diff line Loading @@ -1121,6 +1121,7 @@ starterkit starzzzz statuscode stdclass stickied stitle streamwrapper streamwrappers Loading Loading @@ -1326,6 +1327,7 @@ unaliased unallowed unassigning unassigns unhides unban unbans unbundleable Loading core/tests/Drupal/FunctionalJavascriptTests/Theme/ClaroViewsBulkOperationsTest.php 0 → 100644 +102 −0 Original line number Diff line number Diff line <?php namespace Drupal\FunctionalJavascriptTests\Theme; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; use Drupal\Tests\node\Traits\ContentTypeCreationTrait; use Drupal\Tests\node\Traits\NodeCreationTrait; /** * Tests Claro's Views Bulk Operations form. * * @group claro */ class ClaroViewsBulkOperationsTest extends WebDriverTestBase { use ContentTypeCreationTrait; use NodeCreationTrait; /** * {@inheritdoc} */ protected static $modules = ['node', 'views']; /** * {@inheritdoc} */ protected $defaultTheme = 'claro'; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); // Create a Content type and two test nodes. $this->createContentType(['type' => 'page']); $this->createNode(['title' => 'Page One']); $this->createNode(['title' => 'Page Two']); // Create a user privileged enough to use exposed filters and view content. $user = $this->drupalCreateUser([ 'administer site configuration', 'access content', 'access content overview', 'edit any page content', ]); $this->drupalLogin($user); } /** * Tests the dynamic Bulk Operations form. */ public function testBulkOperationsUi() { $this->drupalGet('admin/content'); $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); $no_items_selected = 'No items selected'; $one_item_selected = '1 item selected'; $two_items_selected = '2 items selected'; $vbo_available_message = 'Bulk actions are now available'; $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$no_items_selected\")")); $select_all = $page->find('css', '.select-all > input'); $page->checkField('node_bulk_form[0]'); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$one_item_selected\")")); // When the bulk operations controls are first activated, this should be // relayed to screen readers. $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$vbo_available_message\")")); $this->assertFalse($select_all->isChecked()); $page->checkField('node_bulk_form[1]'); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$two_items_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$two_items_selected\")")); $assert_session->pageTextNotContains($vbo_available_message); $this->assertTrue($select_all->isChecked()); $page->uncheckField('node_bulk_form[0]'); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$one_item_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$one_item_selected\")")); $assert_session->pageTextNotContains($vbo_available_message); $this->assertFalse($select_all->isChecked()); $page->uncheckField('node_bulk_form[1]'); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$no_items_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$no_items_selected\")")); $assert_session->pageTextNotContains($vbo_available_message); $this->assertFalse($select_all->isChecked()); $select_all->check(); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$two_items_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$vbo_available_message\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$two_items_selected\")")); $select_all->uncheck(); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$no_items_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$no_items_selected\")")); $assert_session->pageTextNotContains($vbo_available_message); } } core/themes/claro/claro.info.yml +2 −0 Original line number Diff line number Diff line Loading @@ -122,6 +122,8 @@ libraries-extend: - claro/claro.jquery.ui core/drupal.tabledrag: - claro/claro.tabledrag core/drupal.tableselect: - claro/tableselect core/drupal.vertical-tabs: - claro/vertical-tabs file/drupal.file: Loading core/themes/claro/claro.libraries.yml +12 −0 Original line number Diff line number Diff line Loading @@ -287,6 +287,18 @@ filter: component: css/theme/filter.theme.css: {} tableselect: version: VERSION js: js/tableselect.js: {} dependencies: - core/jquery - core/drupal - core/drupal.announce - core/drupal.debounce - core/tabbable - core/once classy.book-navigation: version: VERSION css: Loading core/themes/claro/claro.theme +81 −0 Original line number Diff line number Diff line Loading @@ -408,6 +408,87 @@ function claro_form_alter(array &$form, FormStateInterface $form_state, $form_id $form['actions']['submit']['#attributes']['class'] = ['media-library-select']; $form['#attributes']['class'][] = 'media-library-views-form'; } if ($form_object instanceof ViewsForm && !empty($form['header'])) { $view = $form_state->getBuildInfo()['args'][0]; $view_title = $view->getTitle(); // Determine if the Views form includes a bulk operations form. If it does, // move it to the bottom and remove the second bulk operations submit. foreach (Element::children($form['header']) as $key) { if (strpos($key, '_bulk_form') !== FALSE) { // Move the bulk actions form from the header to its own container. $form['bulk_actions_container'] = $form['header'][$key]; unset($form['header'][$key]); // Remove the supplementary bulk operations submit button as it appears // in the same location the form was moved to. unset($form['actions']); $form['bulk_actions_container']['#attributes']['data-drupal-views-bulk-actions'] = ''; $form['bulk_actions_container']['#attributes']['class'][] = 'views-bulk-actions'; $form['bulk_actions_container']['actions']['submit']['#button_type'] = 'primary'; $form['bulk_actions_container']['actions']['submit']['#attributes']['class'][] = 'button--small'; $label = t('Perform actions on the selected items in the %view_title view', ['%view_title' => $view_title]); $label_id = $key . '_group_label'; // Group the bulk actions select and submit elements, and add a label // that makes the purpose of these elements more clear to // screenreaders. $form['bulk_actions_container']['#attributes']['role'] = 'group'; $form['bulk_actions_container']['#attributes']['aria-labelledby'] = $label_id; $form['bulk_actions_container']['group_label'] = [ '#type' => 'container', '#markup' => $label, '#attributes' => [ 'id' => $label_id, 'class' => ['visually-hidden'], ], '#weight' => -1, ]; // Add a status label for counting the number of items selected. $form['bulk_actions_container']['status'] = [ '#type' => 'container', '#markup' => t('No items selected'), '#weight' => -1, '#attributes' => [ 'class' => [ 'js-views-bulk-actions-status', 'views-bulk-actions__item', 'views-bulk-actions__item--status', 'js-show', ], 'data-drupal-views-bulk-actions-status' => '', ], ]; // Loop through bulk actions items and add the needed CSS classes. $bulk_action_item_keys = Element::children($form['bulk_actions_container'], TRUE); $bulk_last_key = NULL; $bulk_child_before_actions_key = NULL; foreach ($bulk_action_item_keys as $bulk_action_item_key) { if (!empty($form['bulk_actions_container'][$bulk_action_item_key]['#type'])) { if ($form['bulk_actions_container'][$bulk_action_item_key]['#type'] === 'actions') { // We need the key of the element that precedes the actions // element. $bulk_child_before_actions_key = $bulk_last_key; $form['bulk_actions_container'][$bulk_action_item_key]['#attributes']['class'][] = 'views-bulk-actions__item'; } if (!in_array($form['bulk_actions_container'][$bulk_action_item_key]['#type'], ['hidden', 'actions'])) { $form['bulk_actions_container'][$bulk_action_item_key]['#wrapper_attributes']['class'][] = 'views-bulk-actions__item'; $bulk_last_key = $bulk_action_item_key; } } } if ($bulk_child_before_actions_key) { $form['bulk_actions_container'][$bulk_child_before_actions_key]['#wrapper_attributes']['class'][] = 'views-bulk-actions__item--preceding-actions'; } } } } } /** Loading Loading
core/misc/cspell/dictionary.txt +2 −0 Original line number Diff line number Diff line Loading @@ -1121,6 +1121,7 @@ starterkit starzzzz statuscode stdclass stickied stitle streamwrapper streamwrappers Loading Loading @@ -1326,6 +1327,7 @@ unaliased unallowed unassigning unassigns unhides unban unbans unbundleable Loading
core/tests/Drupal/FunctionalJavascriptTests/Theme/ClaroViewsBulkOperationsTest.php 0 → 100644 +102 −0 Original line number Diff line number Diff line <?php namespace Drupal\FunctionalJavascriptTests\Theme; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; use Drupal\Tests\node\Traits\ContentTypeCreationTrait; use Drupal\Tests\node\Traits\NodeCreationTrait; /** * Tests Claro's Views Bulk Operations form. * * @group claro */ class ClaroViewsBulkOperationsTest extends WebDriverTestBase { use ContentTypeCreationTrait; use NodeCreationTrait; /** * {@inheritdoc} */ protected static $modules = ['node', 'views']; /** * {@inheritdoc} */ protected $defaultTheme = 'claro'; /** * {@inheritdoc} */ protected function setUp(): void { parent::setUp(); // Create a Content type and two test nodes. $this->createContentType(['type' => 'page']); $this->createNode(['title' => 'Page One']); $this->createNode(['title' => 'Page Two']); // Create a user privileged enough to use exposed filters and view content. $user = $this->drupalCreateUser([ 'administer site configuration', 'access content', 'access content overview', 'edit any page content', ]); $this->drupalLogin($user); } /** * Tests the dynamic Bulk Operations form. */ public function testBulkOperationsUi() { $this->drupalGet('admin/content'); $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); $no_items_selected = 'No items selected'; $one_item_selected = '1 item selected'; $two_items_selected = '2 items selected'; $vbo_available_message = 'Bulk actions are now available'; $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$no_items_selected\")")); $select_all = $page->find('css', '.select-all > input'); $page->checkField('node_bulk_form[0]'); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$one_item_selected\")")); // When the bulk operations controls are first activated, this should be // relayed to screen readers. $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$vbo_available_message\")")); $this->assertFalse($select_all->isChecked()); $page->checkField('node_bulk_form[1]'); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$two_items_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$two_items_selected\")")); $assert_session->pageTextNotContains($vbo_available_message); $this->assertTrue($select_all->isChecked()); $page->uncheckField('node_bulk_form[0]'); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$one_item_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$one_item_selected\")")); $assert_session->pageTextNotContains($vbo_available_message); $this->assertFalse($select_all->isChecked()); $page->uncheckField('node_bulk_form[1]'); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$no_items_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$no_items_selected\")")); $assert_session->pageTextNotContains($vbo_available_message); $this->assertFalse($select_all->isChecked()); $select_all->check(); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$two_items_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$vbo_available_message\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$two_items_selected\")")); $select_all->uncheck(); $this->assertNotNull($assert_session->waitForElementVisible('css', ".js-views-bulk-actions-status:contains(\"$no_items_selected\")")); $this->assertNotNull($assert_session->waitForElement('css', "#drupal-live-announce:contains(\"$no_items_selected\")")); $assert_session->pageTextNotContains($vbo_available_message); } }
core/themes/claro/claro.info.yml +2 −0 Original line number Diff line number Diff line Loading @@ -122,6 +122,8 @@ libraries-extend: - claro/claro.jquery.ui core/drupal.tabledrag: - claro/claro.tabledrag core/drupal.tableselect: - claro/tableselect core/drupal.vertical-tabs: - claro/vertical-tabs file/drupal.file: Loading
core/themes/claro/claro.libraries.yml +12 −0 Original line number Diff line number Diff line Loading @@ -287,6 +287,18 @@ filter: component: css/theme/filter.theme.css: {} tableselect: version: VERSION js: js/tableselect.js: {} dependencies: - core/jquery - core/drupal - core/drupal.announce - core/drupal.debounce - core/tabbable - core/once classy.book-navigation: version: VERSION css: Loading
core/themes/claro/claro.theme +81 −0 Original line number Diff line number Diff line Loading @@ -408,6 +408,87 @@ function claro_form_alter(array &$form, FormStateInterface $form_state, $form_id $form['actions']['submit']['#attributes']['class'] = ['media-library-select']; $form['#attributes']['class'][] = 'media-library-views-form'; } if ($form_object instanceof ViewsForm && !empty($form['header'])) { $view = $form_state->getBuildInfo()['args'][0]; $view_title = $view->getTitle(); // Determine if the Views form includes a bulk operations form. If it does, // move it to the bottom and remove the second bulk operations submit. foreach (Element::children($form['header']) as $key) { if (strpos($key, '_bulk_form') !== FALSE) { // Move the bulk actions form from the header to its own container. $form['bulk_actions_container'] = $form['header'][$key]; unset($form['header'][$key]); // Remove the supplementary bulk operations submit button as it appears // in the same location the form was moved to. unset($form['actions']); $form['bulk_actions_container']['#attributes']['data-drupal-views-bulk-actions'] = ''; $form['bulk_actions_container']['#attributes']['class'][] = 'views-bulk-actions'; $form['bulk_actions_container']['actions']['submit']['#button_type'] = 'primary'; $form['bulk_actions_container']['actions']['submit']['#attributes']['class'][] = 'button--small'; $label = t('Perform actions on the selected items in the %view_title view', ['%view_title' => $view_title]); $label_id = $key . '_group_label'; // Group the bulk actions select and submit elements, and add a label // that makes the purpose of these elements more clear to // screenreaders. $form['bulk_actions_container']['#attributes']['role'] = 'group'; $form['bulk_actions_container']['#attributes']['aria-labelledby'] = $label_id; $form['bulk_actions_container']['group_label'] = [ '#type' => 'container', '#markup' => $label, '#attributes' => [ 'id' => $label_id, 'class' => ['visually-hidden'], ], '#weight' => -1, ]; // Add a status label for counting the number of items selected. $form['bulk_actions_container']['status'] = [ '#type' => 'container', '#markup' => t('No items selected'), '#weight' => -1, '#attributes' => [ 'class' => [ 'js-views-bulk-actions-status', 'views-bulk-actions__item', 'views-bulk-actions__item--status', 'js-show', ], 'data-drupal-views-bulk-actions-status' => '', ], ]; // Loop through bulk actions items and add the needed CSS classes. $bulk_action_item_keys = Element::children($form['bulk_actions_container'], TRUE); $bulk_last_key = NULL; $bulk_child_before_actions_key = NULL; foreach ($bulk_action_item_keys as $bulk_action_item_key) { if (!empty($form['bulk_actions_container'][$bulk_action_item_key]['#type'])) { if ($form['bulk_actions_container'][$bulk_action_item_key]['#type'] === 'actions') { // We need the key of the element that precedes the actions // element. $bulk_child_before_actions_key = $bulk_last_key; $form['bulk_actions_container'][$bulk_action_item_key]['#attributes']['class'][] = 'views-bulk-actions__item'; } if (!in_array($form['bulk_actions_container'][$bulk_action_item_key]['#type'], ['hidden', 'actions'])) { $form['bulk_actions_container'][$bulk_action_item_key]['#wrapper_attributes']['class'][] = 'views-bulk-actions__item'; $bulk_last_key = $bulk_action_item_key; } } } if ($bulk_child_before_actions_key) { $form['bulk_actions_container'][$bulk_child_before_actions_key]['#wrapper_attributes']['class'][] = 'views-bulk-actions__item--preceding-actions'; } } } } } /** Loading