Commit d757e2c5 authored by Cristina Chumillas's avatar Cristina Chumillas
Browse files

Issue #3070558 by bnjmnm, lauriii, huzooka, mherchel, katherined,...

Issue #3070558 by bnjmnm, lauriii, huzooka, mherchel, katherined, kostyashupenko, rkoller, saschaeggi, andrewmacpherson, ckrina, dww, smustgrave, mgifford, klonos, shaal, andypost: Implement bulk operation designs
parent 6d7ec44e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1121,6 +1121,7 @@ starterkit
starzzzz
statuscode
stdclass
stickied
stitle
streamwrapper
streamwrappers
@@ -1326,6 +1327,7 @@ unaliased
unallowed
unassigning
unassigns
unhides
unban
unbans
unbundleable
+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);
  }

}
+2 −0
Original line number Diff line number Diff line
@@ -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:
+12 −0
Original line number Diff line number Diff line
@@ -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:
+81 −0
Original line number Diff line number Diff line
@@ -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