Skip to content
Snippets Groups Projects
Commit a73bf047 authored by Angie Byron's avatar Angie Byron
Browse files

Issue #2995689 by tedbow, bnjmnm, lauriii, bendeguz.csirmaz, andrewmacpherson,...

Issue #2995689 by tedbow, bnjmnm, lauriii, bendeguz.csirmaz, andrewmacpherson, xjm, tim.plunkett, KarenS, alwaysworking: Allow reordering blocks without a pointer device
parent 63d6b17e
No related branches found
No related tags found
No related merge requests found
Showing with 744 additions and 3 deletions
......@@ -141,3 +141,47 @@
.layout-builder-block.is-layout-builder-highlighted {
border: 4px solid #000;
}
/* Highlight the active block in the Move Block dialog. */
#drupal-off-canvas .layout-builder-components-table__block-label--current {
border-left: solid 5px;
padding-left: 17px;
}
/**
* @todo remove in https://www.drupal.org/project/drupal/issues/3042127
* This rule ensures the row weight dropdowns in the Move Block dialog
* maintain the background color of their container when they are hovered
* over or are inside the active row.
*/
#drupal-off-canvas .layout-builder-components-table__row .form-item {
background-color: transparent;
}
.layout-builder__region-label,
.layout-builder__section-label {
display: none;
}
.layout-builder--move-blocks-active .layout-builder__region-label {
display: block;
}
.layout-builder--move-blocks-active .layout-builder__section-label {
display: inline;
}
.layout__region-info {
padding: 0.5em;
text-align: center;
border-bottom: 2px dashed #979797;
}
/**
* Remove "You have unsaved changes" warning because Layout Builder always has
* unsaved changes until "Save layout" is submitted.
* @todo create issue for todo.
*/
.layout-builder-components-table .tabledrag-changed-warning {
display: none !important;
}
......@@ -227,6 +227,24 @@
'is-layout-builder-highlighted',
);
}
// Remove wrapper class added by move block form.
$('#layout-builder').removeClass('layout-builder--move-blocks-active');
/**
* If dialog has a data-add-layout-builder-wrapper attribute, get the
* value and add it as a class to the Layout Builder UI wrapper.
*
* Currently, only the move block form uses
* data-add-layout-builder-wrapper, but any dialog can use this attribute
* to add a class to the Layout Builder UI while opened.
*/
const layoutBuilderWrapperValue = $element
.find('[data-add-layout-builder-wrapper]')
.attr('data-add-layout-builder-wrapper');
if (layoutBuilderWrapperValue) {
$('#layout-builder').addClass(layoutBuilderWrapperValue);
}
}
});
......@@ -275,12 +293,15 @@
});
}
// When a dialog closes, remove the highlight from all elements.
$(window).on('dialog:afterclose', (event, dialog, $element) => {
if (Drupal.offCanvas.isOffCanvas($element)) {
// Remove the highlight from all elements.
$('.is-layout-builder-highlighted').removeClass(
'is-layout-builder-highlighted',
);
// Remove wrapper class added by move block form.
$('#layout-builder').removeClass('layout-builder--move-blocks-active');
}
});
})(jQuery, Drupal);
......@@ -102,6 +102,13 @@
if (id) {
$('[data-layout-builder-highlight-id="' + id + '"]').addClass('is-layout-builder-highlighted');
}
$('#layout-builder').removeClass('layout-builder--move-blocks-active');
var layoutBuilderWrapperValue = $element.find('[data-add-layout-builder-wrapper]').attr('data-add-layout-builder-wrapper');
if (layoutBuilderWrapperValue) {
$('#layout-builder').addClass(layoutBuilderWrapperValue);
}
}
});
......@@ -138,6 +145,8 @@
$(window).on('dialog:afterclose', function (event, dialog, $element) {
if (Drupal.offCanvas.isOffCanvas($element)) {
$('.is-layout-builder-highlighted').removeClass('is-layout-builder-highlighted');
$('#layout-builder').removeClass('layout-builder--move-blocks-active');
}
});
})(jQuery, Drupal);
\ No newline at end of file
......@@ -8,6 +8,16 @@ layout_builder_block_update:
data-dialog-type: dialog
data-dialog-renderer: off_canvas
layout_builder_block_move:
title: 'Move'
route_name: 'layout_builder.move_block_form'
group: 'layout_builder_block'
options:
attributes:
class: ['use-ajax']
data-dialog-type: dialog
data-dialog-renderer: off_canvas
layout_builder_block_remove:
title: 'Remove block'
route_name: 'layout_builder.remove_block'
......
......@@ -109,6 +109,13 @@ function layout_builder_post_update_routing_defaults() {
// Empty post-update hook.
}
/**
* Clear caches due to new link added to Layout Builder's contextual links.
*/
function layout_builder_post_update_discover_new_contextual_links() {
// Empty post-update hook.
}
/**
* Fix Layout Builder tempstore keys of existing entries.
*/
......
......@@ -110,6 +110,21 @@ layout_builder.update_block:
section_storage:
layout_builder_tempstore: TRUE
layout_builder.move_block_form:
path: '/layout_builder/move/block/{section_storage_type}/{section_storage}/{delta}/{region}/{uuid}'
defaults:
_title_callback: '\Drupal\layout_builder\Form\MoveBlockForm::title'
_form: '\Drupal\layout_builder\Form\MoveBlockForm'
requirements:
# @todo revisit in https://www.drupal.org/node/2914486
_permission: 'configure any layout'
_layout_builder_access: 'view'
options:
_admin_route: TRUE
parameters:
section_storage:
layout_builder_tempstore: TRUE
layout_builder.remove_block:
path: '/layout_builder/remove/block/{section_storage_type}/{section_storage}/{delta}/{region}/{uuid}'
defaults:
......
......@@ -255,6 +255,13 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s
'region' => $region,
'uuid' => $uuid,
],
// Add metadata about the current operations available in
// contextual links. This will invalidate the client-side cache of
// links that were cached before the 'move' link was added.
// @see layout_builder.links.contextual.yml
'metadata' => [
'operations' => 'move:update:remove',
],
],
];
}
......@@ -297,6 +304,25 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s
'@region' => $info['label'],
'@section' => $delta + 1,
]);
// Get weights of all children for use by the region label.
$weights = array_map(function ($a) {
return isset($a['#weight']) ? $a['#weight'] : 0;
}, $build[$region]);
// The region label is made visible when the move block dialog is open.
$build[$region]['region_label'] = [
'#type' => 'container',
'#attributes' => [
'class' => ['layout__region-info', 'layout-builder__region-label'],
// A more detailed version of this information is already read by
// screen readers, so this label can be hidden from them.
'aria-hidden' => TRUE,
],
'#markup' => $this->t('Region: @region', ['@region' => $info['label']]),
// Ensures the region label is displayed first.
'#weight' => min($weights) - 1,
];
}
$build['#attributes']['data-layout-update-url'] = Url::fromRoute('layout_builder.move_block', [
......@@ -333,9 +359,19 @@ protected function buildAdministrativeSection(SectionStorageInterface $section_s
'data-dialog-renderer' => 'off_canvas',
],
],
// The section label is added to sections without a "Configure Section"
// link, and is only visible when the move block dialog is open.
'section_label' => [
'#markup' => $this->t('<span class="layout-builder__section-label" aria-hidden="true">Section @section</span>', ['@section' => $delta + 1]),
'#access' => !$layout instanceof PluginFormInterface,
],
'configure' => [
'#type' => 'link',
'#title' => $this->t('Configure section <span class="visually-hidden">@section</span>', ['@section' => $delta + 1]),
// There are two instances of @section, the one wrapped in
// .visually-hidden is for screen readers. The one wrapped in
// .layout-builder__section-label is only visible when the
// move block dialog is open and it is not seen by screen readers.
'#title' => $this->t('Configure section <span class="visually-hidden">@section</span><span aria-hidden="true" class="layout-builder__section-label">@section</span>', ['@section' => $delta + 1]),
'#access' => $layout instanceof PluginFormInterface,
'#url' => Url::fromRoute('layout_builder.configure_section', [
'section_storage_type' => $storage_type,
......
<?php
namespace Drupal\layout_builder\Form;
use Drupal\Core\Ajax\AjaxFormHelperTrait;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\layout_builder\Controller\LayoutRebuildTrait;
use Drupal\layout_builder\LayoutBuilderHighlightTrait;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\SectionStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for moving a block.
*/
class MoveBlockForm extends FormBase {
use AjaxFormHelperTrait;
use LayoutBuilderHighlightTrait;
use LayoutRebuildTrait;
/**
* The section storage.
*
* @var \Drupal\layout_builder\SectionStorageInterface
*/
protected $sectionStorage;
/**
* The section delta.
*
* @var int
*/
protected $delta;
/**
* The region name.
*
* @var string
*/
protected $region;
/**
* The component uuid.
*
* @var string
*/
protected $uuid;
/**
* The Layout Tempstore.
*
* @var \Drupal\layout_builder\LayoutTempstoreRepositoryInterface
*/
protected $layoutTempstore;
/**
* Constructs a new MoveBlockForm.
*
* @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository
* The layout tempstore.
*/
public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository) {
$this->layoutTempstore = $layout_tempstore_repository;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('layout_builder.tempstore_repository')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'layout_builder_block_move';
}
/**
* Builds the move block form.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\layout_builder\SectionStorageInterface $section_storage
* The section storage being configured.
* @param int $delta
* The original delta of the section.
* @param string $region
* The original region of the block.
* @param string $uuid
* The UUID of the block being updated.
*
* @return array
* The form array.
*/
public function buildForm(array $form, FormStateInterface $form_state, SectionStorageInterface $section_storage = NULL, $delta = NULL, $region = NULL, $uuid = NULL) {
$parameters = array_slice(func_get_args(), 2);
foreach ($parameters as $parameter) {
if (is_null($parameter)) {
throw new \InvalidArgumentException('MoveBlockForm requires all parameters.');
}
}
$this->sectionStorage = $section_storage;
$this->delta = $delta;
$this->uuid = $uuid;
$this->region = $region;
$form['#attributes']['data-layout-builder-target-highlight-id'] = $this->blockUpdateHighlightId($uuid);
$sections = $section_storage->getSections();
$region_options = [];
foreach ($sections as $section_delta => $section) {
$layout = $section->getLayout();
$layout_definition = $layout->getPluginDefinition();
$section_label = $this->t('Section: @delta', ['@delta' => $section_delta + 1])->render();
foreach ($layout_definition->getRegions() as $region_name => $region_info) {
// Group regions by section.
$region_options[$section_label]["$section_delta:$region_name"] = $this->t(
'Section: @delta, Region: @region',
['@delta' => $section_delta + 1, '@region' => $region_info['label']]
);
}
}
// $this->region and $this->delta are where the block is currently placed.
// $selected_region and $selected_delta are the values from this form
// specifying where the block should be moved to.
$selected_region = $this->getSelectedRegion($form_state);
$selected_delta = $this->getSelectedDelta($form_state);
$form['region'] = [
'#type' => 'select',
'#options' => $region_options,
'#title' => $this->t('Region'),
'#default_value' => "$selected_delta:$selected_region",
'#ajax' => [
'wrapper' => 'layout-builder-components-table',
'callback' => '::getComponentsWrapper',
],
];
$current_section = $sections[$selected_delta];
$aria_label = $this->t('Blocks in Section: @section, Region: @region', ['@section' => $selected_delta + 1, '@region' => $selected_region]);
$form['components_wrapper']['components'] = [
'#type' => 'table',
'#header' => [
$this->t('Block Label'),
$this->t('Weight'),
],
'#tabledrag' => [
[
'action' => 'order',
'relationship' => 'sibling',
'group' => 'table-sort-weight',
],
],
// Create a wrapping element so that the Ajax update also replaces the
// 'Show block weights' link.
'#theme_wrappers' => [
'container' => [
'#attributes' => [
'id' => 'layout-builder-components-table',
'class' => ['layout-builder-components-table'],
'aria-label' => $aria_label,
],
],
],
];
/** @var \Drupal\layout_builder\SectionComponent[] $components */
$components = $current_section->getComponentsByRegion($selected_region);
// If the component is not in this region, add it to the listed components.
if (!isset($components[$uuid])) {
$components[$uuid] = $sections[$delta]->getComponent($uuid);
}
foreach ($components as $component_uuid => $component) {
/** @var \Drupal\Core\Block\BlockPluginInterface $plugin */
$plugin = $component->getPlugin();
$is_current_block = $component_uuid === $uuid;
$row_classes = [
'draggable',
'layout-builder-components-table__row',
];
$label['#wrapper_attributes']['class'] = ['layout-builder-components-table__block-label'];
if ($is_current_block) {
// Highlight the current block.
$label['#markup'] = $this->t('@label (current)', ['@label' => $plugin->label()]);
$label['#wrapper_attributes']['class'][] = 'layout-builder-components-table__block-label--current';
$row_classes[] = 'layout-builder-components-table__row--current';
}
else {
$label['#markup'] = $plugin->label();
}
$form['components_wrapper']['components'][$component_uuid] = [
'#attributes' => ['class' => $row_classes],
'label' => $label,
'weight' => [
'#type' => 'weight',
'#default_value' => $component->getWeight(),
'#title' => $this->t('Weight for @block block', ['@block' => $plugin->label()]),
'#title_display' => 'invisible',
'#attributes' => [
'class' => ['table-sort-weight'],
],
],
];
}
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Move'),
'#button_type' => 'primary',
];
$form['#attributes']['data-add-layout-builder-wrapper'] = 'layout-builder--move-blocks-active';
if ($this->isAjax()) {
$form['actions']['submit']['#ajax']['callback'] = '::ajaxSubmit';
}
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$region = $this->getSelectedRegion($form_state);
$delta = $this->getSelectedDelta($form_state);
$original_section = $this->sectionStorage->getSection($this->delta);
$component = $original_section->getComponent($this->uuid);
$section = $this->sectionStorage->getSection($delta);
if ($delta !== $this->delta) {
// Remove component from old section and add it to the new section.
$original_section->removeComponent($this->uuid);
$section->insertComponent(0, $component);
}
$component->setRegion($region);
foreach ($form_state->getValue('components') as $uuid => $component_info) {
$section->getComponent($uuid)->setWeight($component_info['weight']);
}
$this->layoutTempstore->set($this->sectionStorage);
}
/**
* Ajax callback for the region select element.
*
* @param array $form
* The form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* The components wrapper render array.
*/
public function getComponentsWrapper(array $form, FormStateInterface $form_state) {
return $form['components_wrapper'];
}
/**
* {@inheritdoc}
*/
protected function successfulAjaxSubmit(array $form, FormStateInterface $form_state) {
return $this->rebuildAndClose($this->sectionStorage);
}
/**
* Gets the selected region.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return string
* The current region name.
*/
protected function getSelectedRegion(FormStateInterface $form_state) {
if ($form_state->hasValue('region')) {
return explode(':', $form_state->getValue('region'), 2)[1];
}
return $this->region;
}
/**
* Gets the selected delta.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return int
* The section delta.
*/
protected function getSelectedDelta(FormStateInterface $form_state) {
if ($form_state->hasValue('region')) {
return (int) explode(':', $form_state->getValue('region'))[0];
}
return (int) $this->delta;
}
/**
* Provides a title callback.
*
* @param \Drupal\layout_builder\SectionStorageInterface $section_storage
* The section storage.
* @param int $delta
* The original delta of the section.
* @param string $uuid
* The UUID of the block being updated.
*
* @return string
* The title for the move block form.
*/
public function title(SectionStorageInterface $section_storage, $delta, $uuid) {
$block_label = $section_storage
->getSection($delta)
->getComponent($uuid)
->getPlugin()
->label();
return $this->t('Move the @block_label block', ['@block_label' => $block_label]);
}
}
......@@ -254,7 +254,7 @@ protected function getNextHighestWeight($region) {
* @return \Drupal\layout_builder\SectionComponent[]
* An array of components in the specified region, sorted by weight.
*/
protected function getComponentsByRegion($region) {
public function getComponentsByRegion($region) {
$components = array_filter($this->getComponents(), function (SectionComponent $component) use ($region) {
return $component->getRegion() === $region;
});
......
<?php
namespace Drupal\Tests\layout_builder\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\contextual\FunctionalJavascript\ContextualLinkClickTrait;
/**
* Tests moving blocks via the form.
*
* @group layout_builder
*/
class MoveBlockFormTest extends WebDriverTestBase {
use ContextualLinkClickTrait;
/**
* Path prefix for the field UI for the test bundle.
*
* @var string
*/
const FIELD_UI_PREFIX = 'admin/structure/types/manage/bundle_with_section_field';
/**
* {@inheritdoc}
*/
protected static $modules = [
'layout_builder',
'block',
'node',
'contextual',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
// @todo The Layout Builder UI relies on local tasks; fix in
// https://www.drupal.org/project/drupal/issues/2917777.
$this->drupalPlaceBlock('local_tasks_block');
$this->createContentType(['type' => 'bundle_with_section_field']);
$this->drupalLogin($this->drupalCreateUser([
'configure any layout',
'administer node display',
'administer node fields',
'access contextual links',
]));
// Enable layout builder.
$this->drupalPostForm(
static::FIELD_UI_PREFIX . '/display/default',
['layout[enabled]' => TRUE],
'Save'
);
$page->clickLink('Manage layout');
$assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display/default/layout');
$expected_block_order = [
'.block-extra-field-blocknodebundle-with-section-fieldlinks',
'.block-field-blocknodebundle-with-section-fieldbody',
];
$this->assertRegionBlocksOrder(0, 'content', $expected_block_order);
// Add a top section using the Two column layout.
$page->clickLink('Add Section');
$assert_session->waitForElementVisible('css', '#drupal-off-canvas');
$assert_session->assertWaitOnAjaxRequest();
$page->clickLink('Two column');
$assert_session->assertWaitOnAjaxRequest();
$this->assertNotEmpty($assert_session->waitForElementVisible('css', 'input[value="Add section"]'));
$page->pressButton('Add section');
$this->assertRegionBlocksOrder(1, 'content', $expected_block_order);
// Add a 'Powered by Drupal' block in the 'first' region of the new section.
$first_region_block_locator = '[data-layout-delta="0"].layout--twocol-section [data-region="first"] [data-layout-block-uuid]';
$assert_session->elementNotExists('css', $first_region_block_locator);
$assert_session->elementExists('css', '[data-layout-delta="0"].layout--twocol-section [data-region="first"] .layout-builder__add-block')->click();
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas a:contains("Powered by Drupal")'));
$assert_session->assertWaitOnAjaxRequest();
$page->clickLink('Powered by Drupal');
$this->assertNotEmpty($assert_session->waitForElementVisible('css', 'input[value="Add Block"]'));
$assert_session->assertWaitOnAjaxRequest();
$page->pressButton('Add Block');
$this->assertNotEmpty($assert_session->waitForElementVisible('css', $first_region_block_locator));
// Ensure the request has completed before the test starts.
$this->waitForNoElement('#drupal-off-canvas');
$assert_session->assertWaitOnAjaxRequest();
}
/**
* Tests moving a block.
*/
public function testMoveBlock() {
$page = $this->getSession()->getPage();
// Reorder body field in current region.
$this->openBodyMoveForm(1, 'content', ['Links', 'Body (current)']);
$this->moveBlockWithKeyboard('up', 'Body (current)', ['Body (current)*', 'Links']);
$page->pressButton('Move');
$expected_block_order = [
'.block-field-blocknodebundle-with-section-fieldbody',
'.block-extra-field-blocknodebundle-with-section-fieldlinks',
];
$this->assertRegionBlocksOrder(1, 'content', $expected_block_order);
$page->pressButton('Save layout');
$page->clickLink('Manage layout');
$this->assertRegionBlocksOrder(1, 'content', $expected_block_order);
// Move the body block into the first region above existing block.
$this->openBodyMoveForm(1, 'content', ['Body (current)', 'Links']);
$page->selectFieldOption('Region', '0:first');
$this->assertBlockTable(['Powered by Drupal', 'Body (current)']);
$this->moveBlockWithKeyboard('up', 'Body', ['Body (current)*', 'Powered by Drupal']);
$page->pressButton('Move');
$expected_block_order = [
'.block-field-blocknodebundle-with-section-fieldbody',
'.block-system-powered-by-block',
];
$this->assertRegionBlocksOrder(0, 'first', $expected_block_order);
// Ensure the body block is no longer in the content region.
$this->assertRegionBlocksOrder(1, 'content', ['.block-extra-field-blocknodebundle-with-section-fieldlinks']);
$page->pressButton('Save layout');
$page->clickLink('Manage layout');
$this->assertRegionBlocksOrder(0, 'first', $expected_block_order);
// Move into the second region that has no existing blocks.
$this->openBodyMoveForm(0, 'first', ['Body (current)', 'Powered by Drupal']);
$page->selectFieldOption('Region', '0:second');
$this->assertBlockTable(['Body (current)']);
$page->pressButton('Move');
$this->assertRegionBlocksOrder(0, 'second', ['.block-field-blocknodebundle-with-section-fieldbody']);
}
/**
* Asserts the correct block labels appear in the draggable tables.
*
* @param string[] $expected_block_labels
* The expected block labels.
*/
protected function assertBlockTable(array $expected_block_labels) {
$page = $this->getSession()->getPage();
$this->assertSession()->assertWaitOnAjaxRequest();
$block_tds = $page->findAll('css', '.layout-builder-components-table__block-label');
$this->assertCount(count($block_tds), $expected_block_labels);
/** @var \Behat\Mink\Element\NodeElement $block_td */
foreach ($block_tds as $block_td) {
$this->assertSame(array_shift($expected_block_labels), trim($block_td->getText()));
}
}
/**
* Waits for an element to be removed from the page.
*
* @param string $selector
* CSS selector.
* @param int $timeout
* (optional) Timeout in milliseconds, defaults to 10000.
*
* @todo Remove in https://www.drupal.org/node/2892440.
*/
protected function waitForNoElement($selector, $timeout = 10000) {
$condition = "(typeof jQuery !== 'undefined' && jQuery('$selector').length === 0)";
$this->assertJsCondition($condition, $timeout);
}
/**
* Moves a block in the draggable table.
*
* @param string $direction
* The direction to move the block in the table.
* @param string $block_label
* The block label.
* @param array $updated_blocks
* The updated blocks order.
*/
protected function moveBlockWithKeyboard($direction, $block_label, array $updated_blocks) {
$keys = [
'up' => 38,
'down' => 40,
];
$key = $keys[$direction];
$handle = $this->findRowHandle($block_label);
$handle->keyDown($key);
$handle->keyUp($key);
$handle->blur();
$this->assertBlockTable($updated_blocks);
}
/**
* Finds the row handle for a block in the draggable table.
*
* @param string $block_label
* The block label.
*
* @return \Behat\Mink\Element\NodeElement
* The row handle element.
*/
protected function findRowHandle($block_label) {
$assert_session = $this->assertSession();
return $assert_session->elementExists('css', "[data-drupal-selector=\"edit-components\"] td:contains(\"$block_label\") a.tabledrag-handle");
}
/**
* Asserts that blocks are in the correct order for a region.
*
* @param int $section_delta
* The section delta.
* @param string $region
* The region.
* @param array $expected_block_selectors
* The block selectors.
*/
protected function assertRegionBlocksOrder($section_delta, $region, array $expected_block_selectors) {
$page = $this->getSession()->getPage();
$assert_session = $this->assertSession();
$assert_session->assertWaitOnAjaxRequest();
$this->waitForNoElement('#drupal-off-canvas');
$region_selector = "[data-layout-delta=\"$section_delta\"] [data-region=\"$region\"]";
// Get all blocks currently in the region.
$blocks = $page->findAll('css', "$region_selector [data-layout-block-uuid]");
$this->assertCount(count($expected_block_selectors), $blocks);
/** @var \Behat\Mink\Element\NodeElement $block */
foreach ($blocks as $block) {
$block_selector = array_shift($expected_block_selectors);
$assert_session->elementsCount('css', "$region_selector $block_selector", 1);
$expected_block = $page->find('css', "$region_selector $block_selector");
$this->assertSame($expected_block->getAttribute('data-layout-block-uuid'), $block->getAttribute('data-layout-block-uuid'));
}
}
/**
* Open block for the body field.
*
* @param int $delta
* The section delta where the field should be.
* @param string $region
* The region where the field should be.
* @param array $initial_blocks
* The initial blocks that should be shown in the draggable table.
*/
protected function openBodyMoveForm($delta, $region, array $initial_blocks) {
$assert_session = $this->assertSession();
$body_field_locator = "[data-layout-delta=\"$delta\"] [data-region=\"$region\"] .block-field-blocknodebundle-with-section-fieldbody";
$this->clickContextualLink($body_field_locator, 'Move');
$assert_session->assertWaitOnAjaxRequest();
$this->assertNotEmpty($assert_session->waitForElementVisible('named', ['select', 'Region']));
$assert_session->fieldValueEquals('Region', "$delta:$region");
$this->assertBlockTable($initial_blocks);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment