Issue #2926914 by tim.plunkett, xjm, larowlan, tedbow, EclipseGc: Rewrite \Drupal\layout_builder\Section to represent the entire section, not just the block info
parent
3cf0815a54
commit
2e16f2a35d
|
@ -6,6 +6,7 @@ use Drupal\Core\DependencyInjection\ClassResolverInterface;
|
|||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
|
@ -63,13 +64,9 @@ class AddSectionController implements ContainerInjectionInterface {
|
|||
* The controller response.
|
||||
*/
|
||||
public function build(EntityInterface $entity, $delta, $plugin_id) {
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $entity->layout_builder__layout;
|
||||
$field_list->addItem($delta, [
|
||||
'layout' => $plugin_id,
|
||||
'layout_settings' => [],
|
||||
'section' => [],
|
||||
]);
|
||||
$field_list->insertSection($delta, new Section($plugin_id));
|
||||
|
||||
$this->layoutTempstoreRepository->set($entity);
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ use Drupal\Core\Plugin\PluginFormInterface;
|
|||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\layout_builder\LayoutSectionBuilder;
|
||||
use Drupal\layout_builder\Field\LayoutSectionItemInterface;
|
||||
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
|
||||
|
@ -111,20 +111,20 @@ class LayoutBuilderController implements ContainerInjectionInterface {
|
|||
$entity_id = $entity->id();
|
||||
$entity_type_id = $entity->getEntityTypeId();
|
||||
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $entity->layout_builder__layout;
|
||||
|
||||
// For a new layout override, begin with a single section of one column.
|
||||
if (!$is_rebuilding && $field_list->isEmpty()) {
|
||||
$field_list->addItem(0, ['layout' => 'layout_onecol']);
|
||||
if (!$is_rebuilding && $field_list->count() === 0) {
|
||||
$field_list->appendSection(new Section('layout_onecol'));
|
||||
$this->layoutTempstoreRepository->set($entity);
|
||||
}
|
||||
|
||||
$output = [];
|
||||
$count = 0;
|
||||
foreach ($field_list as $item) {
|
||||
foreach ($field_list->getSections() as $section) {
|
||||
$output[] = $this->buildAddSectionLink($entity_type_id, $entity_id, $count);
|
||||
$output[] = $this->buildAdministrativeSection($item, $entity, $count);
|
||||
$output[] = $this->buildAdministrativeSection($section, $entity, $count);
|
||||
$count++;
|
||||
}
|
||||
$output[] = $this->buildAddSectionLink($entity_type_id, $entity_id, $count);
|
||||
|
@ -179,8 +179,8 @@ class LayoutBuilderController implements ContainerInjectionInterface {
|
|||
/**
|
||||
* Builds the render array for the layout section while editing.
|
||||
*
|
||||
* @param \Drupal\layout_builder\Field\LayoutSectionItemInterface $item
|
||||
* The layout section item.
|
||||
* @param \Drupal\layout_builder\Section $section
|
||||
* The layout section.
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The entity.
|
||||
* @param int $delta
|
||||
|
@ -189,12 +189,12 @@ class LayoutBuilderController implements ContainerInjectionInterface {
|
|||
* @return array
|
||||
* The render array for a given section.
|
||||
*/
|
||||
protected function buildAdministrativeSection(LayoutSectionItemInterface $item, EntityInterface $entity, $delta) {
|
||||
protected function buildAdministrativeSection(Section $section, EntityInterface $entity, $delta) {
|
||||
$entity_type_id = $entity->getEntityTypeId();
|
||||
$entity_id = $entity->id();
|
||||
|
||||
$layout = $this->layoutManager->createInstance($item->layout, $item->layout_settings);
|
||||
$build = $this->builder->buildSectionFromLayout($layout, $item->section);
|
||||
$layout = $section->getLayout();
|
||||
$build = $section->toRenderArray();
|
||||
$layout_definition = $layout->getPluginDefinition();
|
||||
|
||||
foreach ($layout_definition->getRegions() as $region => $info) {
|
||||
|
|
|
@ -69,32 +69,29 @@ class MoveBlockController implements ContainerInjectionInterface {
|
|||
* An AJAX response.
|
||||
*/
|
||||
public function build(EntityInterface $entity, $delta_from, $delta_to, $region_from, $region_to, $block_uuid, $preceding_block_uuid = NULL) {
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
|
||||
$field = $entity->layout_builder__layout->get($delta_from);
|
||||
$section = $field->getSection();
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $entity->layout_builder__layout;
|
||||
$section = $field_list->getSection($delta_from);
|
||||
|
||||
$block = $section->getBlock($region_from, $block_uuid);
|
||||
$section->removeBlock($region_from, $block_uuid);
|
||||
$component = $section->getComponent($block_uuid);
|
||||
$section->removeComponent($block_uuid);
|
||||
|
||||
// If the block is moving from one section to another, update the original
|
||||
// section and load the new one.
|
||||
if ($delta_from !== $delta_to) {
|
||||
$field->updateFromSection($section);
|
||||
$field = $entity->layout_builder__layout->get($delta_to);
|
||||
$section = $field->getSection();
|
||||
$section = $field_list->getSection($delta_to);
|
||||
}
|
||||
|
||||
// If a preceding block was specified, insert after that. Otherwise add the
|
||||
// block to the front.
|
||||
$component->setRegion($region_to);
|
||||
if (isset($preceding_block_uuid)) {
|
||||
$section->insertBlock($region_to, $block_uuid, $block, $preceding_block_uuid);
|
||||
$section->insertAfterComponent($preceding_block_uuid, $component);
|
||||
}
|
||||
else {
|
||||
$section->addBlock($region_to, $block_uuid, $block);
|
||||
$section->appendComponent($component);
|
||||
}
|
||||
|
||||
$field->updateFromSection($section);
|
||||
|
||||
$this->layoutTempstoreRepository->set($entity);
|
||||
return $this->rebuildLayout($entity);
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder\Field;
|
||||
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\layout_builder\Section;
|
||||
|
||||
/**
|
||||
* Defines an interface for the layout section field item.
|
||||
*
|
||||
* @internal
|
||||
* Layout Builder is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*
|
||||
* @property string layout
|
||||
* @property array[] layout_settings
|
||||
* @property array[] section
|
||||
*/
|
||||
interface LayoutSectionItemInterface extends FieldItemInterface {
|
||||
|
||||
/**
|
||||
* Gets a domain object for the layout section.
|
||||
*
|
||||
* @return \Drupal\layout_builder\Section
|
||||
* The layout section.
|
||||
*/
|
||||
public function getSection();
|
||||
|
||||
/**
|
||||
* Updates the stored value based on the domain object.
|
||||
*
|
||||
* @param \Drupal\layout_builder\Section $section
|
||||
* The layout section.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function updateFromSection(Section $section);
|
||||
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
namespace Drupal\layout_builder\Field;
|
||||
|
||||
use Drupal\Core\Field\FieldItemList;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionStorageInterface;
|
||||
|
||||
/**
|
||||
* Defines a item list class for layout section fields.
|
||||
|
@ -11,22 +13,65 @@ use Drupal\Core\Field\FieldItemList;
|
|||
*
|
||||
* @see \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem
|
||||
*/
|
||||
class LayoutSectionItemList extends FieldItemList implements LayoutSectionItemListInterface {
|
||||
class LayoutSectionItemList extends FieldItemList implements SectionStorageInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addItem($index, $value) {
|
||||
if ($this->get($index)) {
|
||||
$start = array_slice($this->list, 0, $index);
|
||||
$end = array_slice($this->list, $index);
|
||||
$item = $this->createItem($index, $value);
|
||||
public function insertSection($delta, Section $section) {
|
||||
if ($this->get($delta)) {
|
||||
/** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
|
||||
$item = $this->createItem($delta);
|
||||
$item->section = $section;
|
||||
|
||||
$start = array_slice($this->list, 0, $delta);
|
||||
$end = array_slice($this->list, $delta);
|
||||
$this->list = array_merge($start, [$item], $end);
|
||||
}
|
||||
else {
|
||||
$item = $this->appendItem($value);
|
||||
$this->appendSection($section);
|
||||
}
|
||||
return $item;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function appendSection(Section $section) {
|
||||
$this->appendItem()->section = $section;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSections() {
|
||||
$sections = [];
|
||||
/** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
|
||||
foreach ($this->list as $delta => $item) {
|
||||
$sections[$delta] = $item->section;
|
||||
}
|
||||
return $sections;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSection($delta) {
|
||||
/** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
|
||||
if (!$item = $this->get($delta)) {
|
||||
throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" entity', $delta, $this->getEntity()->label()));
|
||||
}
|
||||
|
||||
return $item->section;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeSection($delta) {
|
||||
$this->removeItem($delta);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder\Field;
|
||||
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
|
||||
/**
|
||||
* Defines a item list class for layout section fields.
|
||||
*
|
||||
* @internal
|
||||
* Layout Builder is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*
|
||||
* @see \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem
|
||||
*/
|
||||
interface LayoutSectionItemListInterface extends FieldItemListInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Drupal\layout_builder\Field\LayoutSectionItemInterface|null
|
||||
* The layout section item, if it exists.
|
||||
*/
|
||||
public function get($index);
|
||||
|
||||
/**
|
||||
* Adds a new item to the list.
|
||||
*
|
||||
* If an item exists at the given index, the item at that position and others
|
||||
* after it are shifted backward.
|
||||
*
|
||||
* @param int $index
|
||||
* The position of the item in the list.
|
||||
* @param mixed $value
|
||||
* The value of the item to be stored at the specified position.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\TypedDataInterface
|
||||
* The item that was appended.
|
||||
*
|
||||
* @todo Move to \Drupal\Core\TypedData\ListInterface directly in
|
||||
* https://www.drupal.org/node/2907417.
|
||||
*/
|
||||
public function addItem($index, $value);
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\layout_builder\Form;
|
||||
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
|
||||
/**
|
||||
* Provides a form to add a block.
|
||||
|
@ -29,7 +30,7 @@ class AddBlockForm extends ConfigureBlockFormBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function submitBlock(Section $section, $region, $uuid, array $configuration) {
|
||||
$section->addBlock($region, $uuid, $configuration);
|
||||
$section->appendComponent(new SectionComponent($uuid, $region, $configuration));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -246,11 +246,10 @@ abstract class ConfigureBlockFormBase extends FormBase {
|
|||
|
||||
$configuration = $this->block->getConfiguration();
|
||||
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
|
||||
$field = $this->entity->layout_builder__layout->get($this->delta);
|
||||
$section = $field->getSection();
|
||||
$this->submitBlock($section, $this->region, $configuration['uuid'], ['block' => $configuration]);
|
||||
$field->updateFromSection($section);
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $this->entity->layout_builder__layout;
|
||||
$section = $field_list->getSection($this->delta);
|
||||
$this->submitBlock($section, $this->region, $configuration['uuid'], $configuration);
|
||||
|
||||
$this->layoutTempstoreRepository->set($this->entity);
|
||||
$form_state->setRedirectUrl($this->entity->toUrl('layout-builder'));
|
||||
|
|
|
@ -14,6 +14,7 @@ use Drupal\Core\Plugin\PluginFormInterface;
|
|||
use Drupal\Core\Plugin\PluginWithFormsInterface;
|
||||
use Drupal\layout_builder\Controller\LayoutRebuildTrait;
|
||||
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
|
@ -121,14 +122,15 @@ class ConfigureSectionForm extends FormBase {
|
|||
$this->delta = $delta;
|
||||
$this->isUpdate = is_null($plugin_id);
|
||||
|
||||
$configuration = [];
|
||||
if ($this->isUpdate) {
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
|
||||
$field = $this->entity->layout_builder__layout->get($this->delta);
|
||||
$plugin_id = $field->layout;
|
||||
$configuration = $field->layout_settings;
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $this->entity->layout_builder__layout;
|
||||
$section = $field_list->getSection($this->delta);
|
||||
}
|
||||
$this->layout = $this->layoutManager->createInstance($plugin_id, $configuration);
|
||||
else {
|
||||
$section = new Section($plugin_id);
|
||||
}
|
||||
$this->layout = $section->getLayout();
|
||||
|
||||
$form['#tree'] = TRUE;
|
||||
$form['layout_settings'] = [];
|
||||
|
@ -166,19 +168,13 @@ class ConfigureSectionForm extends FormBase {
|
|||
$plugin_id = $this->layout->getPluginId();
|
||||
$configuration = $this->layout->getConfiguration();
|
||||
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $this->entity->layout_builder__layout;
|
||||
if ($this->isUpdate) {
|
||||
$field = $field_list->get($this->delta);
|
||||
$field->layout = $plugin_id;
|
||||
$field->layout_settings = $configuration;
|
||||
$field_list->getSection($this->delta)->setLayoutSettings($configuration);
|
||||
}
|
||||
else {
|
||||
$field_list->addItem($this->delta, [
|
||||
'layout' => $plugin_id,
|
||||
'layout_settings' => $configuration,
|
||||
'section' => [],
|
||||
]);
|
||||
$field_list->insertSection($this->delta, new Section($plugin_id, $configuration));
|
||||
}
|
||||
|
||||
$this->layoutTempstoreRepository->set($this->entity);
|
||||
|
|
|
@ -60,11 +60,9 @@ class RemoveBlockForm extends LayoutRebuildConfirmFormBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function handleEntity(EntityInterface $entity, FormStateInterface $form_state) {
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
|
||||
$field = $entity->layout_builder__layout->get($this->delta);
|
||||
$section = $field->getSection();
|
||||
$section->removeBlock($this->region, $this->uuid);
|
||||
$field->updateFromSection($section);
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $this->entity->layout_builder__layout;
|
||||
$field_list->getSection($this->delta)->removeComponent($this->uuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,14 +40,11 @@ class UpdateBlockForm extends ConfigureBlockFormBase {
|
|||
* The form array.
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $entity = NULL, $delta = NULL, $region = NULL, $uuid = NULL) {
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
|
||||
$field = $entity->layout_builder__layout->get($delta);
|
||||
$block = $field->getSection()->getBlock($region, $uuid);
|
||||
if (empty($block['block']['id'])) {
|
||||
throw new \InvalidArgumentException('Invalid UUID specified');
|
||||
}
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $entity->layout_builder__layout;
|
||||
$plugin = $field_list->getSection($delta)->getComponent($uuid)->getPlugin();
|
||||
|
||||
return parent::buildForm($form, $form_state, $entity, $delta, $region, $block['block']['id'], $block['block']);
|
||||
return parent::buildForm($form, $form_state, $entity, $delta, $region, $plugin->getPluginId(), $plugin->getConfiguration());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,7 +58,7 @@ class UpdateBlockForm extends ConfigureBlockFormBase {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
protected function submitBlock(Section $section, $region, $uuid, array $configuration) {
|
||||
$section->updateBlock($region, $uuid, $configuration);
|
||||
$section->getComponent($uuid)->setConfiguration($configuration);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,14 +2,11 @@
|
|||
|
||||
namespace Drupal\layout_builder;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Layout\LayoutInterface;
|
||||
use Drupal\Core\Layout\LayoutPluginManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\StringTranslation\StringTranslationTrait;
|
||||
|
||||
|
@ -17,6 +14,8 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
|
|||
* Builds the UI for layout sections.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @todo Remove in https://www.drupal.org/project/drupal/issues/2928450.
|
||||
*/
|
||||
class LayoutSectionBuilder {
|
||||
|
||||
|
@ -84,37 +83,21 @@ class LayoutSectionBuilder {
|
|||
*
|
||||
* @param \Drupal\Core\Layout\LayoutInterface $layout
|
||||
* The ID of the layout.
|
||||
* @param array $section
|
||||
* An array of configuration, keyed first by region and then by block UUID.
|
||||
* @param \Drupal\layout_builder\SectionComponent[] $components
|
||||
* An array of components.
|
||||
*
|
||||
* @return array
|
||||
* The render array for a given section.
|
||||
*/
|
||||
public function buildSectionFromLayout(LayoutInterface $layout, array $section) {
|
||||
$cacheability = CacheableMetadata::createFromRenderArray([]);
|
||||
|
||||
public function buildSectionFromLayout(LayoutInterface $layout, array $components) {
|
||||
$regions = [];
|
||||
$weight = 0;
|
||||
foreach ($section as $region => $blocks) {
|
||||
if (!is_array($blocks)) {
|
||||
throw new \InvalidArgumentException(sprintf('The "%s" region in the "%s" layout has invalid configuration', $region, $layout->getPluginId()));
|
||||
}
|
||||
|
||||
foreach ($blocks as $uuid => $configuration) {
|
||||
if (!is_array($configuration) || !isset($configuration['block'])) {
|
||||
throw new \InvalidArgumentException(sprintf('The block with UUID of "%s" has invalid configuration', $uuid));
|
||||
}
|
||||
|
||||
if ($block_output = $this->buildBlock($uuid, $configuration['block'], $cacheability)) {
|
||||
$block_output['#weight'] = $weight++;
|
||||
$regions[$region][$uuid] = $block_output;
|
||||
}
|
||||
foreach ($components as $component) {
|
||||
if ($output = $component->toRenderArray()) {
|
||||
$regions[$component->getRegion()][$component->getUuid()] = $output;
|
||||
}
|
||||
}
|
||||
|
||||
$result = $layout->build($regions);
|
||||
$cacheability->applyTo($result);
|
||||
return $result;
|
||||
return $layout->build($regions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,78 +107,15 @@ class LayoutSectionBuilder {
|
|||
* The ID of the layout.
|
||||
* @param array $layout_settings
|
||||
* The configuration for the layout.
|
||||
* @param array $section
|
||||
* An array of configuration, keyed first by region and then by block UUID.
|
||||
* @param \Drupal\layout_builder\SectionComponent[] $components
|
||||
* An array of components.
|
||||
*
|
||||
* @return array
|
||||
* The render array for a given section.
|
||||
*/
|
||||
public function buildSection($layout_id, array $layout_settings, array $section) {
|
||||
public function buildSection($layout_id, array $layout_settings, array $components) {
|
||||
$layout = $this->layoutPluginManager->createInstance($layout_id, $layout_settings);
|
||||
return $this->buildSectionFromLayout($layout, $section);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the render array for a given block.
|
||||
*
|
||||
* @param string $uuid
|
||||
* The UUID of this block instance.
|
||||
* @param array $configuration
|
||||
* An array of configuration relevant to the block instance. Must contain
|
||||
* the plugin ID with the key 'id'.
|
||||
* @param \Drupal\Core\Cache\CacheableMetadata $cacheability
|
||||
* The cacheability metadata.
|
||||
*
|
||||
* @return array|null
|
||||
* The render array representing this block, if accessible. NULL otherwise.
|
||||
*/
|
||||
protected function buildBlock($uuid, array $configuration, CacheableMetadata $cacheability) {
|
||||
$block = $this->getBlock($uuid, $configuration);
|
||||
|
||||
$access = $block->access($this->account, TRUE);
|
||||
$cacheability->addCacheableDependency($access);
|
||||
|
||||
$block_output = NULL;
|
||||
if ($access->isAllowed()) {
|
||||
$block_output = [
|
||||
'#theme' => 'block',
|
||||
'#configuration' => $block->getConfiguration(),
|
||||
'#plugin_id' => $block->getPluginId(),
|
||||
'#base_plugin_id' => $block->getBaseId(),
|
||||
'#derivative_plugin_id' => $block->getDerivativeId(),
|
||||
'content' => $block->build(),
|
||||
];
|
||||
$cacheability->addCacheableDependency($block);
|
||||
}
|
||||
return $block_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a block instance.
|
||||
*
|
||||
* @param string $uuid
|
||||
* The UUID of this block instance.
|
||||
* @param array $configuration
|
||||
* An array of configuration relevant to the block instance. Must contain
|
||||
* the plugin ID with the key 'id'.
|
||||
*
|
||||
* @return \Drupal\Core\Block\BlockPluginInterface
|
||||
* The block instance.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
* Thrown when the configuration parameter does not contain 'id'.
|
||||
*/
|
||||
protected function getBlock($uuid, array $configuration) {
|
||||
if (!isset($configuration['id'])) {
|
||||
throw new PluginException(sprintf('No plugin ID specified for block with "%s" UUID', $uuid));
|
||||
}
|
||||
|
||||
$block = $this->blockManager->createInstance($configuration['id'], $configuration);
|
||||
if ($block instanceof ContextAwarePluginInterface) {
|
||||
$contexts = $this->contextRepository->getRuntimeContexts(array_values($block->getContextMapping()));
|
||||
$this->contextHandler->applyContextMapping($block, $contexts);
|
||||
}
|
||||
return $block;
|
||||
return $this->buildSectionFromLayout($layout, $components);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder\Plugin\DataType;
|
||||
|
||||
use Drupal\Core\TypedData\TypedData;
|
||||
use Drupal\layout_builder\Section;
|
||||
|
||||
/**
|
||||
* Provides a data type wrapping \Drupal\layout_builder\Section.
|
||||
*
|
||||
* @DataType(
|
||||
* id = "layout_section",
|
||||
* label = @Translation("Layout Section"),
|
||||
* description = @Translation("A layout section"),
|
||||
* )
|
||||
*/
|
||||
class SectionData extends TypedData {
|
||||
|
||||
/**
|
||||
* The section object.
|
||||
*
|
||||
* @var \Drupal\layout_builder\Section
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setValue($value, $notify = TRUE) {
|
||||
if ($value && !$value instanceof Section) {
|
||||
throw new \InvalidArgumentException(sprintf('Value assigned to "%s" is not a valid section', $this->getName()));
|
||||
}
|
||||
parent::setValue($value, $notify);
|
||||
}
|
||||
|
||||
}
|
|
@ -78,9 +78,9 @@ class LayoutSectionFormatter extends FormatterBase implements ContainerFactoryPl
|
|||
public function viewElements(FieldItemListInterface $items, $langcode) {
|
||||
$elements = [];
|
||||
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface[] $items */
|
||||
foreach ($items as $delta => $item) {
|
||||
$elements[$delta] = $this->builder->buildSection($item->layout, $item->layout_settings, $item->section);
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $items */
|
||||
foreach ($items->getSections() as $delta => $section) {
|
||||
$elements[$delta] = $section->toRenderArray();
|
||||
}
|
||||
|
||||
return $elements;
|
||||
|
|
|
@ -7,8 +7,6 @@ use Drupal\Core\Field\FieldItemBase;
|
|||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\TypedData\MapDataDefinition;
|
||||
use Drupal\layout_builder\Field\LayoutSectionItemInterface;
|
||||
use Drupal\layout_builder\Section;
|
||||
|
||||
/**
|
||||
|
@ -25,22 +23,16 @@ use Drupal\layout_builder\Section;
|
|||
* no_ui = TRUE,
|
||||
* cardinality = \Drupal\Core\Field\FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
* )
|
||||
*
|
||||
* @property \Drupal\layout_builder\Section section
|
||||
*/
|
||||
class LayoutSectionItem extends FieldItemBase implements LayoutSectionItemInterface {
|
||||
class LayoutSectionItem extends FieldItemBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
|
||||
// Prevent early t() calls by using the TranslatableMarkup.
|
||||
$properties['layout'] = DataDefinition::create('string')
|
||||
->setLabel(new TranslatableMarkup('Layout'))
|
||||
->setSetting('case_sensitive', FALSE)
|
||||
->setRequired(TRUE);
|
||||
$properties['layout_settings'] = MapDataDefinition::create('map')
|
||||
->setLabel(new TranslatableMarkup('Layout Settings'))
|
||||
->setRequired(FALSE);
|
||||
$properties['section'] = MapDataDefinition::create('map')
|
||||
$properties['section'] = DataDefinition::create('layout_section')
|
||||
->setLabel(new TranslatableMarkup('Layout Section'))
|
||||
->setRequired(FALSE);
|
||||
|
||||
|
@ -73,17 +65,6 @@ class LayoutSectionItem extends FieldItemBase implements LayoutSectionItemInterf
|
|||
public static function schema(FieldStorageDefinitionInterface $field_definition) {
|
||||
$schema = [
|
||||
'columns' => [
|
||||
'layout' => [
|
||||
'type' => 'varchar',
|
||||
'length' => '255',
|
||||
'binary' => FALSE,
|
||||
],
|
||||
'layout_settings' => [
|
||||
'type' => 'blob',
|
||||
'size' => 'normal',
|
||||
// @todo Address in https://www.drupal.org/node/2914503.
|
||||
'serialize' => TRUE,
|
||||
],
|
||||
'section' => [
|
||||
'type' => 'blob',
|
||||
'size' => 'normal',
|
||||
|
@ -100,10 +81,8 @@ class LayoutSectionItem extends FieldItemBase implements LayoutSectionItemInterf
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
|
||||
$values['layout'] = 'layout_onecol';
|
||||
$values['layout_settings'] = [];
|
||||
// @todo Expand this in https://www.drupal.org/node/2912331.
|
||||
$values['section'] = [];
|
||||
$values['section'] = new Section('layout_onecol');
|
||||
return $values;
|
||||
}
|
||||
|
||||
|
@ -111,22 +90,7 @@ class LayoutSectionItem extends FieldItemBase implements LayoutSectionItemInterf
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEmpty() {
|
||||
return empty($this->layout);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSection() {
|
||||
return new Section($this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function updateFromSection(Section $section) {
|
||||
$this->section = $section->getValue();
|
||||
return $this;
|
||||
return empty($this->section);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,158 +5,303 @@ namespace Drupal\layout_builder;
|
|||
/**
|
||||
* Provides a domain object for layout sections.
|
||||
*
|
||||
* A section is a multi-dimensional array, keyed first by region machine name,
|
||||
* then by block UUID, containing block configuration values.
|
||||
* A section consists of three parts:
|
||||
* - The layout plugin ID for the layout applied to the section (for example,
|
||||
* 'layout_onecol').
|
||||
* - An array of settings for the layout plugin.
|
||||
* - An array of components that can be rendered in the section.
|
||||
*
|
||||
* @internal
|
||||
* Layout Builder is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*
|
||||
* @see \Drupal\Core\Layout\LayoutDefinition
|
||||
* @see \Drupal\layout_builder\SectionComponent
|
||||
*
|
||||
* @todo Determine whether an interface will be provided for this in
|
||||
* https://www.drupal.org/project/drupal/issues/2930334.
|
||||
*/
|
||||
class Section {
|
||||
|
||||
/**
|
||||
* The section data.
|
||||
* The layout plugin ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $layoutId;
|
||||
|
||||
/**
|
||||
* The layout plugin settings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $section;
|
||||
protected $layoutSettings = [];
|
||||
|
||||
/**
|
||||
* An array of components, keyed by UUID.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionComponent[]
|
||||
*/
|
||||
protected $components = [];
|
||||
|
||||
/**
|
||||
* Constructs a new Section.
|
||||
*
|
||||
* @param array $section
|
||||
* The section data.
|
||||
* @param string $layout_id
|
||||
* The layout plugin ID.
|
||||
* @param array $layout_settings
|
||||
* (optional) The layout plugin settings.
|
||||
* @param \Drupal\layout_builder\SectionComponent[] $components
|
||||
* (optional) The components.
|
||||
*/
|
||||
public function __construct(array $section) {
|
||||
$this->section = $section;
|
||||
public function __construct($layout_id, array $layout_settings = [], array $components = []) {
|
||||
$this->layoutId = $layout_id;
|
||||
$this->layoutSettings = $layout_settings;
|
||||
foreach ($components as $component) {
|
||||
$this->setComponent($component);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the section.
|
||||
* Returns the renderable array for this section.
|
||||
*
|
||||
* @return array
|
||||
* The section data.
|
||||
* A renderable array representing the content of the section.
|
||||
*/
|
||||
public function getValue() {
|
||||
return $this->section;
|
||||
public function toRenderArray() {
|
||||
$regions = [];
|
||||
foreach ($this->getComponents() as $component) {
|
||||
if ($output = $component->toRenderArray()) {
|
||||
$regions[$component->getRegion()][$component->getUuid()] = $output;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getLayout()->build($regions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration of a given block from a region.
|
||||
* Gets the layout plugin for this section.
|
||||
*
|
||||
* @return \Drupal\Core\Layout\LayoutInterface
|
||||
* The layout plugin.
|
||||
*/
|
||||
public function getLayout() {
|
||||
return $this->layoutPluginManager()->createInstance($this->getLayoutId(), $this->getLayoutSettings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the layout plugin ID for this section.
|
||||
*
|
||||
* @return string
|
||||
* The layout plugin ID.
|
||||
*
|
||||
* @internal
|
||||
* This method should only be used by code responsible for storing the data.
|
||||
*/
|
||||
public function getLayoutId() {
|
||||
return $this->layoutId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the layout plugin settings for this section.
|
||||
*
|
||||
* @return mixed[]
|
||||
* The layout plugin settings.
|
||||
*
|
||||
* @internal
|
||||
* This method should only be used by code responsible for storing the data.
|
||||
*/
|
||||
public function getLayoutSettings() {
|
||||
return $this->layoutSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the layout plugin settings for this section.
|
||||
*
|
||||
* @param mixed[] $layout_settings
|
||||
* The layout plugin settings.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setLayoutSettings(array $layout_settings) {
|
||||
$this->layoutSettings = $layout_settings;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the components of the section.
|
||||
*
|
||||
* @return \Drupal\layout_builder\SectionComponent[]
|
||||
* The components.
|
||||
*/
|
||||
public function getComponents() {
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component for a given UUID.
|
||||
*
|
||||
* @param string $region
|
||||
* The region name.
|
||||
* @param string $uuid
|
||||
* The UUID of the block to retrieve.
|
||||
* The UUID of the component to retrieve.
|
||||
*
|
||||
* @return array
|
||||
* The block configuration.
|
||||
* @return \Drupal\layout_builder\SectionComponent
|
||||
* The component.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown when the expected region or UUID do not exist.
|
||||
* Thrown when the expected UUID does not exist.
|
||||
*/
|
||||
public function getBlock($region, $uuid) {
|
||||
if (!isset($this->section[$region])) {
|
||||
throw new \InvalidArgumentException('Invalid region');
|
||||
public function getComponent($uuid) {
|
||||
if (!isset($this->components[$uuid])) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid UUID "%s"', $uuid));
|
||||
}
|
||||
|
||||
if (!isset($this->section[$region][$uuid])) {
|
||||
throw new \InvalidArgumentException('Invalid UUID');
|
||||
}
|
||||
|
||||
return $this->section[$region][$uuid];
|
||||
return $this->components[$uuid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the configuration of a given block from a region.
|
||||
* Helper method to set a component.
|
||||
*
|
||||
* @param string $region
|
||||
* The region name.
|
||||
* @param string $uuid
|
||||
* The UUID of the block to retrieve.
|
||||
* @param array $configuration
|
||||
* The block configuration.
|
||||
* @param \Drupal\layout_builder\SectionComponent $component
|
||||
* The component.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown when the expected region or UUID do not exist.
|
||||
*/
|
||||
public function updateBlock($region, $uuid, array $configuration) {
|
||||
if (!isset($this->section[$region])) {
|
||||
throw new \InvalidArgumentException('Invalid region');
|
||||
}
|
||||
|
||||
if (!isset($this->section[$region][$uuid])) {
|
||||
throw new \InvalidArgumentException('Invalid UUID');
|
||||
}
|
||||
|
||||
$this->section[$region][$uuid] = $configuration;
|
||||
|
||||
protected function setComponent(SectionComponent $component) {
|
||||
$this->components[$component->getUuid()] = $component;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given block from a region.
|
||||
* Removes a given component from a region.
|
||||
*
|
||||
* @param string $region
|
||||
* The region name.
|
||||
* @param string $uuid
|
||||
* The UUID of the block to remove.
|
||||
* The UUID of the component to remove.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeBlock($region, $uuid) {
|
||||
unset($this->section[$region][$uuid]);
|
||||
$this->section = array_filter($this->section);
|
||||
public function removeComponent($uuid) {
|
||||
unset($this->components[$uuid]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a block to the front of a region.
|
||||
* Appends a component to the end of a region.
|
||||
*
|
||||
* @param string $region
|
||||
* The region name.
|
||||
* @param string $uuid
|
||||
* The UUID of the block to add.
|
||||
* @param array $configuration
|
||||
* The block configuration.
|
||||
* @param \Drupal\layout_builder\SectionComponent $component
|
||||
* The component being appended.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addBlock($region, $uuid, array $configuration) {
|
||||
$this->section += [$region => []];
|
||||
$this->section[$region] = array_merge([$uuid => $configuration], $this->section[$region]);
|
||||
public function appendComponent(SectionComponent $component) {
|
||||
$component->setWeight($this->getNextHighestWeight($component->getRegion()));
|
||||
$this->setComponent($component);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a block after a specified existing block in a region.
|
||||
* Returns the next highest weight of the component in a region.
|
||||
*
|
||||
* @param string $region
|
||||
* The region name.
|
||||
* @param string $uuid
|
||||
* The UUID of the block to insert.
|
||||
* @param array $configuration
|
||||
* The block configuration.
|
||||
*
|
||||
* @return int
|
||||
* A number higher than the highest weight of the component in the region.
|
||||
*/
|
||||
protected function getNextHighestWeight($region) {
|
||||
$components = $this->getComponentsByRegion($region);
|
||||
$weights = array_map(function (SectionComponent $component) {
|
||||
return $component->getWeight();
|
||||
}, $components);
|
||||
return $weights ? max($weights) + 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the components for a specific region.
|
||||
*
|
||||
* @param string $region
|
||||
* The region name.
|
||||
*
|
||||
* @return \Drupal\layout_builder\SectionComponent[]
|
||||
* An array of components in the specified region, sorted by weight.
|
||||
*/
|
||||
protected function getComponentsByRegion($region) {
|
||||
$components = array_filter($this->getComponents(), function (SectionComponent $component) use ($region) {
|
||||
return $component->getRegion() === $region;
|
||||
});
|
||||
uasort($components, function (SectionComponent $a, SectionComponent $b) {
|
||||
return $a->getWeight() > $b->getWeight() ? 1 : -1;
|
||||
});
|
||||
return $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a component after a specified existing component.
|
||||
*
|
||||
* @param string $preceding_uuid
|
||||
* The UUID of the existing block to insert after.
|
||||
* The UUID of the existing component to insert after.
|
||||
* @param \Drupal\layout_builder\SectionComponent $component
|
||||
* The component being inserted.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown when the expected region does not exist.
|
||||
* Thrown when the expected UUID does not exist.
|
||||
*/
|
||||
public function insertBlock($region, $uuid, array $configuration, $preceding_uuid) {
|
||||
if (!isset($this->section[$region])) {
|
||||
throw new \InvalidArgumentException('Invalid region');
|
||||
public function insertAfterComponent($preceding_uuid, SectionComponent $component) {
|
||||
// Find the delta of the specified UUID.
|
||||
$uuids = array_keys($this->getComponentsByRegion($component->getRegion()));
|
||||
$delta = array_search($preceding_uuid, $uuids, TRUE);
|
||||
if ($delta === FALSE) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid preceding UUID "%s"', $preceding_uuid));
|
||||
}
|
||||
return $this->insertComponent($delta + 1, $component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a component at a specified delta.
|
||||
*
|
||||
* @param int $delta
|
||||
* The zero-based delta in which to insert the component.
|
||||
* @param \Drupal\layout_builder\SectionComponent $new_component
|
||||
* The component being inserted.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \OutOfBoundsException
|
||||
* Thrown when the specified delta is invalid.
|
||||
*/
|
||||
public function insertComponent($delta, SectionComponent $new_component) {
|
||||
$components = $this->getComponentsByRegion($new_component->getRegion());
|
||||
$count = count($components);
|
||||
if ($delta > $count) {
|
||||
throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" component', $delta, $new_component->getUuid()));
|
||||
}
|
||||
|
||||
$slice_id = array_search($preceding_uuid, array_keys($this->section[$region]));
|
||||
if ($slice_id === FALSE) {
|
||||
throw new \InvalidArgumentException('Invalid preceding UUID');
|
||||
// If the delta is the end of the list, append the component instead.
|
||||
if ($delta === $count) {
|
||||
return $this->appendComponent($new_component);
|
||||
}
|
||||
|
||||
$before = array_slice($this->section[$region], 0, $slice_id + 1);
|
||||
$after = array_slice($this->section[$region], $slice_id + 1);
|
||||
$this->section[$region] = array_merge($before, [$uuid => $configuration], $after);
|
||||
// Find the weight of the component that exists at the specified delta.
|
||||
$weight = array_values($components)[$delta]->getWeight();
|
||||
$this->setComponent($new_component->setWeight($weight++));
|
||||
|
||||
// Increase the weight of every subsequent component.
|
||||
foreach (array_slice($components, $delta) as $component) {
|
||||
$component->setWeight($weight++);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the layout plugin manager.
|
||||
*
|
||||
* @return \Drupal\Core\Layout\LayoutPluginManagerInterface
|
||||
* The layout plugin manager.
|
||||
*/
|
||||
protected function layoutPluginManager() {
|
||||
return \Drupal::service('plugin.manager.core.layout');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
|
||||
/**
|
||||
* Provides a value object for a section component.
|
||||
*
|
||||
* A component represents the smallest part of a layout (for example, a block).
|
||||
* Components wrap a renderable plugin, currently using
|
||||
* \Drupal\Core\Block\BlockPluginInterface, and contain the layout region
|
||||
* within the section layout where the component will be rendered.
|
||||
*
|
||||
* @internal
|
||||
* Layout Builder is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*
|
||||
* @see \Drupal\Core\Layout\LayoutDefinition
|
||||
* @see \Drupal\layout_builder\Section
|
||||
* @see \Drupal\layout_builder\SectionStorageInterface
|
||||
*
|
||||
* @todo Determine whether to retain the name 'component' in
|
||||
* https://www.drupal.org/project/drupal/issues/2929783.
|
||||
* @todo Determine whether an interface will be provided for this in
|
||||
* https://www.drupal.org/project/drupal/issues/2930334.
|
||||
*/
|
||||
class SectionComponent {
|
||||
|
||||
/**
|
||||
* The UUID of the component.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $uuid;
|
||||
|
||||
/**
|
||||
* The region the component is placed in.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $region;
|
||||
|
||||
/**
|
||||
* An array of plugin configuration.
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* The weight of the component.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $weight = 0;
|
||||
|
||||
/**
|
||||
* Any additional properties and values.
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
protected $additional = [];
|
||||
|
||||
/**
|
||||
* Constructs a new SectionComponent.
|
||||
*
|
||||
* @param string $uuid
|
||||
* The UUID.
|
||||
* @param string $region
|
||||
* The region.
|
||||
* @param mixed[] $configuration
|
||||
* The plugin configuration.
|
||||
* @param mixed[] $additional
|
||||
* An additional values.
|
||||
*/
|
||||
public function __construct($uuid, $region, array $configuration = [], array $additional = []) {
|
||||
$this->uuid = $uuid;
|
||||
$this->region = $region;
|
||||
$this->configuration = $configuration;
|
||||
$this->additional = $additional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the renderable array for this component.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array representing the content of the component.
|
||||
*/
|
||||
public function toRenderArray() {
|
||||
$output = [];
|
||||
|
||||
$plugin = $this->getPlugin();
|
||||
// @todo Figure out the best way to unify fields and blocks and components
|
||||
// in https://www.drupal.org/node/1875974.
|
||||
if ($plugin instanceof BlockPluginInterface) {
|
||||
$access = $plugin->access($this->currentUser(), TRUE);
|
||||
$cacheability = CacheableMetadata::createFromObject($access);
|
||||
|
||||
if ($access->isAllowed()) {
|
||||
$cacheability->addCacheableDependency($plugin);
|
||||
// @todo Move this to BlockBase in https://www.drupal.org/node/2931040.
|
||||
$output = [
|
||||
'#theme' => 'block',
|
||||
'#configuration' => $plugin->getConfiguration(),
|
||||
'#plugin_id' => $plugin->getPluginId(),
|
||||
'#base_plugin_id' => $plugin->getBaseId(),
|
||||
'#derivative_plugin_id' => $plugin->getDerivativeId(),
|
||||
'#weight' => $this->getWeight(),
|
||||
'content' => $plugin->build(),
|
||||
];
|
||||
}
|
||||
$cacheability->applyTo($output);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any arbitrary property for the component.
|
||||
*
|
||||
* @param string $property
|
||||
* The property to retrieve.
|
||||
*
|
||||
* @return mixed
|
||||
* The value for that property, or NULL if the property does not exist.
|
||||
*/
|
||||
public function get($property) {
|
||||
if (property_exists($this, $property)) {
|
||||
$value = isset($this->{$property}) ? $this->{$property} : NULL;
|
||||
}
|
||||
else {
|
||||
$value = isset($this->additional[$property]) ? $this->additional[$property] : NULL;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value to an arbitrary property for the component.
|
||||
*
|
||||
* @param string $property
|
||||
* The property to use for the value.
|
||||
* @param mixed $value
|
||||
* The value to set.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set($property, $value) {
|
||||
if (property_exists($this, $property)) {
|
||||
$this->{$property} = $value;
|
||||
}
|
||||
else {
|
||||
$this->additional[$property] = $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the region for the component.
|
||||
*
|
||||
* @return string
|
||||
* The region.
|
||||
*/
|
||||
public function getRegion() {
|
||||
return $this->region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the region for the component.
|
||||
*
|
||||
* @param string $region
|
||||
* The region.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRegion($region) {
|
||||
$this->region = $region;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the weight of the component.
|
||||
*
|
||||
* @return int
|
||||
* The zero-based weight of the component.
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
* Thrown if the weight was never set.
|
||||
*/
|
||||
public function getWeight() {
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the weight of the component.
|
||||
*
|
||||
* @param int $weight
|
||||
* The zero-based weight of the component.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWeight($weight) {
|
||||
$this->weight = $weight;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component plugin configuration.
|
||||
*
|
||||
* @return mixed[]
|
||||
* The component plugin configuration.
|
||||
*/
|
||||
protected function getConfiguration() {
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the plugin configuration.
|
||||
*
|
||||
* @param mixed[] $configuration
|
||||
* The plugin configuration.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setConfiguration(array $configuration) {
|
||||
$this->configuration = $configuration;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin ID.
|
||||
*
|
||||
* @return string
|
||||
* The plugin ID.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
* Thrown if the plugin ID cannot be found.
|
||||
*/
|
||||
protected function getPluginId() {
|
||||
if (empty($this->configuration['id'])) {
|
||||
throw new PluginException(sprintf('No plugin ID specified for component with "%s" UUID', $this->uuid));
|
||||
}
|
||||
return $this->configuration['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID for this component.
|
||||
*
|
||||
* @return string
|
||||
* The UUID.
|
||||
*/
|
||||
public function getUuid() {
|
||||
return $this->uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin for this component.
|
||||
*
|
||||
* @return \Drupal\Component\Plugin\PluginInspectionInterface
|
||||
* The plugin.
|
||||
*/
|
||||
public function getPlugin() {
|
||||
$plugin = $this->pluginManager()->createInstance($this->getPluginId(), $this->getConfiguration());
|
||||
if ($plugin instanceof ContextAwarePluginInterface) {
|
||||
$contexts = $this->contextRepository()->getRuntimeContexts(array_values($plugin->getContextMapping()));
|
||||
$this->contextHandler()->applyContextMapping($plugin, $contexts);
|
||||
}
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the component plugin manager.
|
||||
*
|
||||
* @return \Drupal\Core\Block\BlockManagerInterface
|
||||
* The plugin manager.
|
||||
*/
|
||||
protected function pluginManager() {
|
||||
return \Drupal::service('plugin.manager.block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the context repository.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextRepositoryInterface
|
||||
* The context repository.
|
||||
*/
|
||||
protected function contextRepository() {
|
||||
return \Drupal::service('context.repository');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the context handler.
|
||||
*
|
||||
* @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
|
||||
* The context handler.
|
||||
*/
|
||||
protected function contextHandler() {
|
||||
return \Drupal::service('context.handler');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the current user.
|
||||
*
|
||||
* @return \Drupal\Core\Session\AccountInterface
|
||||
* The current user.
|
||||
*/
|
||||
protected function currentUser() {
|
||||
return \Drupal::currentUser();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\layout_builder;
|
||||
|
||||
/**
|
||||
* Defines the interface for an object that stores layout sections.
|
||||
*
|
||||
* @internal
|
||||
* Layout Builder is currently experimental and should only be leveraged by
|
||||
* experimental modules and development releases of contributed modules.
|
||||
* See https://www.drupal.org/core/experimental for more information.
|
||||
*
|
||||
* @see \Drupal\layout_builder\Section
|
||||
*/
|
||||
interface SectionStorageInterface extends \Countable {
|
||||
|
||||
/**
|
||||
* Gets the layout sections.
|
||||
*
|
||||
* @return \Drupal\layout_builder\Section[]
|
||||
* An array of sections.
|
||||
*/
|
||||
public function getSections();
|
||||
|
||||
/**
|
||||
* Gets a domain object for the layout section.
|
||||
*
|
||||
* @param int $delta
|
||||
* The delta of the section.
|
||||
*
|
||||
* @return \Drupal\layout_builder\Section
|
||||
* The layout section.
|
||||
*/
|
||||
public function getSection($delta);
|
||||
|
||||
/**
|
||||
* Appends a new section to the end of the list.
|
||||
*
|
||||
* @param \Drupal\layout_builder\Section $section
|
||||
* The section to append.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function appendSection(Section $section);
|
||||
|
||||
/**
|
||||
* Inserts a new section at a given delta.
|
||||
*
|
||||
* If a section exists at the given index, the section at that position and
|
||||
* others after it are shifted backward.
|
||||
*
|
||||
* @param int $delta
|
||||
* The delta of the section.
|
||||
* @param \Drupal\layout_builder\Section $section
|
||||
* The section to insert.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function insertSection($delta, Section $section);
|
||||
|
||||
/**
|
||||
* Removes the section at the given delta.
|
||||
*
|
||||
* @param int $delta
|
||||
* The delta of the section.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeSection($delta);
|
||||
|
||||
}
|
|
@ -4,6 +4,8 @@ namespace Drupal\Tests\layout_builder\Functional;
|
|||
|
||||
use Drupal\Core\Entity\Entity\EntityViewDisplay;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
|
@ -56,19 +58,14 @@ class LayoutSectionTest extends BrowserTestBase {
|
|||
$data['block_with_context'] = [
|
||||
[
|
||||
[
|
||||
'layout' => 'layout_onecol',
|
||||
'section' => [
|
||||
'content' => [
|
||||
'baz' => [
|
||||
'block' => [
|
||||
'id' => 'test_context_aware',
|
||||
'context_mapping' => [
|
||||
'user' => '@user.current_user_context:current_user',
|
||||
],
|
||||
],
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'test_context_aware',
|
||||
'context_mapping' => [
|
||||
'user' => '@user.current_user_context:current_user',
|
||||
],
|
||||
],
|
||||
],
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
[
|
||||
|
@ -86,16 +83,11 @@ class LayoutSectionTest extends BrowserTestBase {
|
|||
$data['single_section_single_block'] = [
|
||||
[
|
||||
[
|
||||
'layout' => 'layout_onecol',
|
||||
'section' => [
|
||||
'content' => [
|
||||
'baz' => [
|
||||
'block' => [
|
||||
'id' => 'system_powered_by_block',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'system_powered_by_block',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
'.layout--onecol',
|
||||
|
@ -107,37 +99,23 @@ class LayoutSectionTest extends BrowserTestBase {
|
|||
$data['multiple_sections'] = [
|
||||
[
|
||||
[
|
||||
'layout' => 'layout_onecol',
|
||||
'section' => [
|
||||
'content' => [
|
||||
'baz' => [
|
||||
'block' => [
|
||||
'id' => 'system_powered_by_block',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'system_powered_by_block',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
[
|
||||
'layout' => 'layout_twocol',
|
||||
'section' => [
|
||||
'first' => [
|
||||
'foo' => [
|
||||
'block' => [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'foo text',
|
||||
],
|
||||
],
|
||||
],
|
||||
'second' => [
|
||||
'bar' => [
|
||||
'block' => [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'bar text',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'section' => new Section('layout_twocol', [], [
|
||||
'foo' => new SectionComponent('foo', 'first', [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'foo text',
|
||||
]),
|
||||
'bar' => new SectionComponent('bar', 'second', [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'bar text',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
[
|
||||
|
@ -177,16 +155,11 @@ class LayoutSectionTest extends BrowserTestBase {
|
|||
public function testLayoutSectionFormatterAccess() {
|
||||
$node = $this->createSectionNode([
|
||||
[
|
||||
'layout' => 'layout_onecol',
|
||||
'section' => [
|
||||
'content' => [
|
||||
'baz' => [
|
||||
'block' => [
|
||||
'id' => 'test_access',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'test_access',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -216,41 +189,27 @@ class LayoutSectionTest extends BrowserTestBase {
|
|||
|
||||
$entity = $this->createSectionNode([
|
||||
[
|
||||
'layout' => 'layout_onecol',
|
||||
'section' => [
|
||||
'content' => [
|
||||
'baz' => [
|
||||
'block' => [
|
||||
'id' => 'system_powered_by_block',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'section' => new Section('layout_onecol', [], [
|
||||
'baz' => new SectionComponent('baz', 'content', [
|
||||
'id' => 'system_powered_by_block',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
]);
|
||||
$entity->addTranslation('es', [
|
||||
'title' => 'Translated node title',
|
||||
$this->fieldName => [
|
||||
[
|
||||
'layout' => 'layout_twocol',
|
||||
'section' => [
|
||||
'first' => [
|
||||
'foo' => [
|
||||
'block' => [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'foo text',
|
||||
],
|
||||
],
|
||||
],
|
||||
'second' => [
|
||||
'bar' => [
|
||||
'block' => [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'bar text',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'section' => new Section('layout_twocol', [], [
|
||||
'foo' => new SectionComponent('foo', 'first', [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'foo text',
|
||||
]),
|
||||
'bar' => new SectionComponent('bar', 'second', [
|
||||
'id' => 'test_block_instantiation',
|
||||
'display_message' => 'bar text',
|
||||
]),
|
||||
]),
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
|
|
@ -8,6 +8,7 @@ use Drupal\entity_test\Entity\EntityTestBaseFieldDisplay;
|
|||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\layout_builder\Section;
|
||||
|
||||
/**
|
||||
* Ensures that Layout Builder and Field Layout are compatible with each other.
|
||||
|
@ -108,13 +109,9 @@ class LayoutBuilderFieldLayoutCompatibilityTest extends KernelTestBase {
|
|||
$this->assertSame($original_markup, $new_markup);
|
||||
|
||||
// Add a layout override.
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
|
||||
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
|
||||
$field_list = $entity->layout_builder__layout;
|
||||
$field_list->appendItem([
|
||||
'layout' => 'layout_onecol',
|
||||
'layout_settings' => [],
|
||||
'section' => [],
|
||||
]);
|
||||
$field_list->appendSection(new Section('layout_onecol'));
|
||||
$entity->save();
|
||||
|
||||
// The rendered entity has now changed. The non-configurable field is shown
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestBaseFieldDisplay;
|
||||
|
||||
/**
|
||||
* Tests the field type for Layout Sections.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\layout_builder\Field\LayoutSectionItemList
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutSectionItemListTest extends SectionStorageTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'field',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEntity(array $section_data) {
|
||||
$this->installEntitySchema('entity_test_base_field_display');
|
||||
layout_builder_add_layout_section_field('entity_test_base_field_display', 'entity_test_base_field_display');
|
||||
|
||||
$entity = EntityTestBaseFieldDisplay::create([
|
||||
'name' => 'The test entity',
|
||||
'layout_builder__layout' => $section_data,
|
||||
]);
|
||||
$entity->save();
|
||||
return $entity->get('layout_builder__layout');
|
||||
}
|
||||
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\layout_builder\Field\LayoutSectionItemInterface;
|
||||
use Drupal\layout_builder\Field\LayoutSectionItemListInterface;
|
||||
use Drupal\Tests\field\Kernel\FieldKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the field type for Layout Sections.
|
||||
*
|
||||
* @group layout_builder
|
||||
*/
|
||||
class LayoutSectionItemTest extends FieldKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['layout_builder', 'layout_discovery'];
|
||||
|
||||
/**
|
||||
* Tests using entity fields of the layout section field type.
|
||||
*/
|
||||
public function testLayoutSectionItem() {
|
||||
layout_builder_add_layout_section_field('entity_test', 'entity_test');
|
||||
|
||||
$entity = EntityTest::create();
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
|
||||
$field_list = $entity->layout_builder__layout;
|
||||
|
||||
// Test sample item generation.
|
||||
$field_list->generateSampleItems();
|
||||
$this->entityValidateAndSave($entity);
|
||||
|
||||
$field = $field_list->get(0);
|
||||
$this->assertInstanceOf(LayoutSectionItemInterface::class, $field);
|
||||
$this->assertInstanceOf(FieldItemInterface::class, $field);
|
||||
$this->assertSame('section', $field->mainPropertyName());
|
||||
$this->assertSame('layout_onecol', $field->layout);
|
||||
$this->assertSame([], $field->layout_settings);
|
||||
$this->assertSame([], $field->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testLayoutSectionItemList() {
|
||||
layout_builder_add_layout_section_field('entity_test', 'entity_test');
|
||||
|
||||
$entity = EntityTest::create();
|
||||
/** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
|
||||
$field_list = $entity->layout_builder__layout;
|
||||
$this->assertInstanceOf(LayoutSectionItemListInterface::class, $field_list);
|
||||
$this->assertInstanceOf(FieldItemListInterface::class, $field_list);
|
||||
$entity->save();
|
||||
|
||||
$field_list->appendItem(['layout' => 'layout_twocol']);
|
||||
$field_list->appendItem(['layout' => 'layout_onecol']);
|
||||
$field_list->appendItem(['layout' => 'layout_threecol_25_50_25']);
|
||||
$this->assertSame([
|
||||
['layout' => 'layout_twocol'],
|
||||
['layout' => 'layout_onecol'],
|
||||
['layout' => 'layout_threecol_25_50_25'],
|
||||
], $field_list->getValue());
|
||||
|
||||
$field_list->addItem(1, ['layout' => 'layout_threecol_33_34_33']);
|
||||
$this->assertSame([
|
||||
['layout' => 'layout_twocol'],
|
||||
['layout' => 'layout_threecol_33_34_33'],
|
||||
['layout' => 'layout_onecol'],
|
||||
['layout' => 'layout_threecol_25_50_25'],
|
||||
], $field_list->getValue());
|
||||
|
||||
$field_list->addItem($field_list->count(), ['layout' => 'layout_twocol_bricks']);
|
||||
$this->assertSame([
|
||||
['layout' => 'layout_twocol'],
|
||||
['layout' => 'layout_threecol_33_34_33'],
|
||||
['layout' => 'layout_onecol'],
|
||||
['layout' => 'layout_threecol_25_50_25'],
|
||||
['layout' => 'layout_twocol_bricks'],
|
||||
], $field_list->getValue());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\layout_builder\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
|
||||
/**
|
||||
* Provides a base class for testing implementations of section storage.
|
||||
*/
|
||||
abstract class SectionStorageTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'layout_builder',
|
||||
'layout_discovery',
|
||||
'layout_test',
|
||||
'user',
|
||||
'entity_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The section storage implementation.
|
||||
*
|
||||
* @var \Drupal\layout_builder\SectionStorageInterface
|
||||
*/
|
||||
protected $sectionStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$section_data = [
|
||||
[
|
||||
'section' => new Section('layout_test_plugin', [], [
|
||||
'first-uuid' => new SectionComponent('first-uuid', 'content'),
|
||||
]),
|
||||
],
|
||||
[
|
||||
'section' => new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content'),
|
||||
]),
|
||||
],
|
||||
];
|
||||
$this->sectionStorage = $this->getEntity($section_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the section storage entity.
|
||||
*
|
||||
* @param array $section_data
|
||||
* An array of section data.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* The entity.
|
||||
*/
|
||||
abstract protected function getEntity(array $section_data);
|
||||
|
||||
/**
|
||||
* @covers ::getSections
|
||||
*/
|
||||
public function testGetSections() {
|
||||
$expected = [
|
||||
new Section('layout_test_plugin', [], [
|
||||
'first-uuid' => new SectionComponent('first-uuid', 'content'),
|
||||
]),
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content'),
|
||||
]),
|
||||
];
|
||||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getSection
|
||||
*/
|
||||
public function testGetSection() {
|
||||
$this->assertInstanceOf(Section::class, $this->sectionStorage->getSection(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getSection
|
||||
*/
|
||||
public function testGetSectionInvalidDelta() {
|
||||
$this->setExpectedException(\OutOfBoundsException::class, 'Invalid delta "2" for the "The test entity"');
|
||||
$this->sectionStorage->getSection(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertSection
|
||||
*/
|
||||
public function testInsertSection() {
|
||||
$expected = [
|
||||
new Section('layout_test_plugin', [], [
|
||||
'first-uuid' => new SectionComponent('first-uuid', 'content'),
|
||||
]),
|
||||
new Section('setting_1'),
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content'),
|
||||
]),
|
||||
];
|
||||
|
||||
$this->sectionStorage->insertSection(1, new Section('setting_1'));
|
||||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::appendSection
|
||||
*/
|
||||
public function testAppendSection() {
|
||||
$expected = [
|
||||
new Section('layout_test_plugin', [], [
|
||||
'first-uuid' => new SectionComponent('first-uuid', 'content'),
|
||||
]),
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content'),
|
||||
]),
|
||||
new Section('foo'),
|
||||
];
|
||||
|
||||
$this->sectionStorage->appendSection(new Section('foo'));
|
||||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::removeSection
|
||||
*/
|
||||
public function testRemoveSection() {
|
||||
$expected = [
|
||||
new Section('layout_test_plugin', ['setting_1' => 'bar'], [
|
||||
'second-uuid' => new SectionComponent('second-uuid', 'content'),
|
||||
]),
|
||||
];
|
||||
|
||||
$this->sectionStorage->removeSection(0);
|
||||
$this->assertSections($expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the field list has the expected sections.
|
||||
*
|
||||
* @param \Drupal\layout_builder\Section[] $expected
|
||||
* The expected sections.
|
||||
*/
|
||||
protected function assertSections(array $expected) {
|
||||
$result = $this->sectionStorage->getSections();
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertSame(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,8 @@ use Drupal\Core\Access\AccessResult;
|
|||
use Drupal\Core\Block\BlockManagerInterface;
|
||||
use Drupal\Core\Block\BlockPluginInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Layout\LayoutDefinition;
|
||||
use Drupal\Core\Layout\LayoutInterface;
|
||||
use Drupal\Core\Layout\LayoutPluginManagerInterface;
|
||||
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
|
||||
|
@ -14,6 +16,7 @@ use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
|
|||
use Drupal\Core\Plugin\ContextAwarePluginInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\layout_builder\LayoutSectionBuilder;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
|
||||
|
@ -86,7 +89,16 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
$this->layoutSectionBuilder = new LayoutSectionBuilder($this->account->reveal(), $this->layoutPluginManager->reveal(), $this->blockManager->reveal(), $this->contextHandler->reveal(), $this->contextRepository->reveal());
|
||||
|
||||
$this->layout = $this->prophesize(LayoutInterface::class);
|
||||
$this->layout->getPluginDefinition()->willReturn(new LayoutDefinition([]));
|
||||
$this->layout->build(Argument::type('array'))->willReturnArgument(0);
|
||||
$this->layoutPluginManager->createInstance('layout_onecol', [])->willReturn($this->layout->reveal());
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('current_user', $this->account->reveal());
|
||||
$container->set('plugin.manager.block', $this->blockManager->reveal());
|
||||
$container->set('context.handler', $this->contextHandler->reveal());
|
||||
$container->set('context.repository', $this->contextRepository->reveal());
|
||||
\Drupal::setContainer($container);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,8 +114,12 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => $block_content,
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
$this->layout->build(['content' => ['some_uuid' => $render_array]])->willReturnArgument(0);
|
||||
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
|
||||
|
@ -120,20 +136,9 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
$block->getConfiguration()->willReturn([]);
|
||||
|
||||
$section = [
|
||||
'content' => [
|
||||
'some_uuid' => [
|
||||
'block' => [
|
||||
'id' => 'block_plugin_id',
|
||||
],
|
||||
],
|
||||
],
|
||||
new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
|
||||
];
|
||||
$expected = [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
'content' => [
|
||||
'some_uuid' => $render_array,
|
||||
],
|
||||
|
@ -146,7 +151,6 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
* @covers ::buildSection
|
||||
*/
|
||||
public function testBuildSectionAccessDenied() {
|
||||
$this->layout->build([])->willReturn([]);
|
||||
|
||||
$block = $this->prophesize(BlockPluginInterface::class);
|
||||
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
|
||||
|
@ -156,19 +160,17 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
$block->build()->shouldNotBeCalled();
|
||||
|
||||
$section = [
|
||||
'content' => [
|
||||
'some_uuid' => [
|
||||
'block' => [
|
||||
'id' => 'block_plugin_id',
|
||||
],
|
||||
],
|
||||
],
|
||||
new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
|
||||
];
|
||||
$expected = [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
'content' => [
|
||||
'some_uuid' => [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
|
||||
|
@ -179,23 +181,14 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
* @covers ::buildSection
|
||||
*/
|
||||
public function testBuildSectionEmpty() {
|
||||
$this->layout->build([])->willReturn([]);
|
||||
|
||||
$section = [];
|
||||
$expected = [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
$expected = [];
|
||||
$result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildSection
|
||||
* @covers ::getBlock
|
||||
*/
|
||||
public function testContextAwareBlock() {
|
||||
$render_array = [
|
||||
|
@ -206,8 +199,12 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
'#base_plugin_id' => 'block_plugin_id',
|
||||
'#derivative_plugin_id' => NULL,
|
||||
'content' => [],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
];
|
||||
$this->layout->build(['content' => ['some_uuid' => $render_array]])->willReturnArgument(0);
|
||||
|
||||
$block = $this->prophesize(BlockPluginInterface::class)->willImplement(ContextAwarePluginInterface::class);
|
||||
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
|
||||
|
@ -228,20 +225,9 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
$this->contextHandler->applyContextMapping($block->reveal(), [])->shouldBeCalled();
|
||||
|
||||
$section = [
|
||||
'content' => [
|
||||
'some_uuid' => [
|
||||
'block' => [
|
||||
'id' => 'block_plugin_id',
|
||||
],
|
||||
],
|
||||
],
|
||||
new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
|
||||
];
|
||||
$expected = [
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => [],
|
||||
'max-age' => -1,
|
||||
],
|
||||
'content' => [
|
||||
'some_uuid' => $render_array,
|
||||
],
|
||||
|
@ -252,50 +238,10 @@ class LayoutSectionBuilderTest extends UnitTestCase {
|
|||
|
||||
/**
|
||||
* @covers ::buildSection
|
||||
* @covers ::getBlock
|
||||
*/
|
||||
public function testBuildSectionMissingPluginId() {
|
||||
$section = [
|
||||
'content' => [
|
||||
'some_uuid' => [
|
||||
'block' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->setExpectedException(PluginException::class, 'No plugin ID specified for block with "some_uuid" UUID');
|
||||
$this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::buildSection
|
||||
*
|
||||
* @dataProvider providerTestBuildSectionMalformedData
|
||||
*/
|
||||
public function testBuildSectionMalformedData($section, $message) {
|
||||
$this->layout->build(Argument::type('array'))->willReturnArgument(0);
|
||||
$this->layout->getPluginId()->willReturn('the_plugin_id');
|
||||
$this->setExpectedException(\InvalidArgumentException::class, $message);
|
||||
$this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides test data for ::testBuildSectionMalformedData().
|
||||
*/
|
||||
public function providerTestBuildSectionMalformedData() {
|
||||
$data = [];
|
||||
$data['invalid_region'] = [
|
||||
['content' => 'bar'],
|
||||
'The "content" region in the "the_plugin_id" layout has invalid configuration',
|
||||
];
|
||||
$data['invalid_configuration'] = [
|
||||
['content' => ['some_uuid' => 'bar']],
|
||||
'The block with UUID of "some_uuid" has invalid configuration',
|
||||
];
|
||||
$data['invalid_blocks'] = [
|
||||
['content' => ['some_uuid' => []]],
|
||||
'The block with UUID of "some_uuid" has invalid configuration',
|
||||
];
|
||||
return $data;
|
||||
$this->setExpectedException(PluginException::class, 'No plugin ID specified for component with "some_uuid" UUID');
|
||||
$this->layoutSectionBuilder->buildSection('layout_onecol', [], [new SectionComponent('some_uuid', 'content')]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Drupal\Tests\layout_builder\Unit;
|
||||
|
||||
use Drupal\layout_builder\Section;
|
||||
use Drupal\layout_builder\SectionComponent;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
|
@ -24,242 +25,158 @@ class SectionTest extends UnitTestCase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->section = new Section([
|
||||
'empty-region' => [],
|
||||
'some-region' => [
|
||||
'existing-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'existing-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'ordered-region' => [
|
||||
'first-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'first-block-id',
|
||||
],
|
||||
],
|
||||
'second-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'second-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->section = new Section('layout_onecol', [], [
|
||||
new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']),
|
||||
(new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
(new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__construct
|
||||
* @covers ::getValue
|
||||
* @covers ::setComponent
|
||||
* @covers ::getComponents
|
||||
*/
|
||||
public function testGetValue() {
|
||||
public function testGetComponents() {
|
||||
$expected = [
|
||||
'empty-region' => [],
|
||||
'some-region' => [
|
||||
'existing-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'existing-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'ordered-region' => [
|
||||
'first-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'first-block-id',
|
||||
],
|
||||
],
|
||||
'second-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'second-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
];
|
||||
$result = $this->section->getValue();
|
||||
$this->assertSame($expected, $result);
|
||||
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getBlock
|
||||
* @covers ::getComponent
|
||||
*/
|
||||
public function testGetBlockInvalidRegion() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
|
||||
$this->section->getBlock('invalid-region', 'existing-uuid');
|
||||
public function testGetComponentInvalidUuid() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID "invalid-uuid"');
|
||||
$this->section->getComponent('invalid-uuid');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getBlock
|
||||
* @covers ::getComponent
|
||||
*/
|
||||
public function testGetBlockInvalidUuid() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID');
|
||||
$this->section->getBlock('some-region', 'invalid-uuid');
|
||||
public function testGetComponent() {
|
||||
$expected = new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']);
|
||||
|
||||
$this->assertEquals($expected, $this->section->getComponent('existing-uuid'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getBlock
|
||||
* @covers ::removeComponent
|
||||
* @covers ::getComponentsByRegion
|
||||
*/
|
||||
public function testGetBlock() {
|
||||
$expected = ['block' => ['id' => 'existing-block-id']];
|
||||
|
||||
$block = $this->section->getBlock('some-region', 'existing-uuid');
|
||||
$this->assertSame($expected, $block);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::removeBlock
|
||||
*/
|
||||
public function testRemoveBlock() {
|
||||
$this->section->removeBlock('some-region', 'existing-uuid');
|
||||
public function testRemoveComponent() {
|
||||
$expected = [
|
||||
'ordered-region' => [
|
||||
'first-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'first-block-id',
|
||||
],
|
||||
],
|
||||
'second-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'second-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
];
|
||||
$this->assertSame($expected, $this->section->getValue());
|
||||
|
||||
$this->section->removeComponent('first-uuid');
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::addBlock
|
||||
* @covers ::appendComponent
|
||||
* @covers ::getNextHighestWeight
|
||||
* @covers ::getComponentsByRegion
|
||||
*/
|
||||
public function testAddBlock() {
|
||||
$this->section->addBlock('some-region', 'new-uuid', []);
|
||||
public function testAppendComponent() {
|
||||
$expected = [
|
||||
'empty-region' => [],
|
||||
'some-region' => [
|
||||
'new-uuid' => [],
|
||||
'existing-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'existing-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'ordered-region' => [
|
||||
'first-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'first-block-id',
|
||||
],
|
||||
],
|
||||
'second-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'second-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
'new-uuid' => (new SectionComponent('new-uuid', 'some-region', []))->setWeight(1),
|
||||
];
|
||||
$this->assertSame($expected, $this->section->getValue());
|
||||
|
||||
$this->section->appendComponent(new SectionComponent('new-uuid', 'some-region'));
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertBlock
|
||||
* @covers ::insertAfterComponent
|
||||
*/
|
||||
public function testInsertBlock() {
|
||||
$this->section->insertBlock('ordered-region', 'new-uuid', [], 'first-uuid');
|
||||
public function testInsertAfterComponent() {
|
||||
$expected = [
|
||||
'empty-region' => [],
|
||||
'some-region' => [
|
||||
'existing-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'existing-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'ordered-region' => [
|
||||
'first-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'first-block-id',
|
||||
],
|
||||
],
|
||||
'new-uuid' => [],
|
||||
'second-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'second-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(4),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(3),
|
||||
];
|
||||
$this->assertSame($expected, $this->section->getValue());
|
||||
|
||||
$this->section->insertAfterComponent('first-uuid', new SectionComponent('new-uuid', 'ordered-region'));
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertBlock
|
||||
* @covers ::insertAfterComponent
|
||||
*/
|
||||
public function testInsertBlockInvalidRegion() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
|
||||
$this->section->insertBlock('invalid-region', 'new-uuid', [], 'first-uuid');
|
||||
public function testInsertAfterComponentValidUuidRegionMismatch() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID "existing-uuid"');
|
||||
$this->section->insertAfterComponent('existing-uuid', new SectionComponent('new-uuid', 'ordered-region'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::insertBlock
|
||||
* @covers ::insertAfterComponent
|
||||
*/
|
||||
public function testInsertBlockInvalidUuid() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID');
|
||||
$this->section->insertBlock('ordered-region', 'new-uuid', [], 'invalid-uuid');
|
||||
public function testInsertAfterComponentInvalidUuid() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID "invalid-uuid"');
|
||||
$this->section->insertAfterComponent('invalid-uuid', new SectionComponent('new-uuid', 'ordered-region'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::updateBlock
|
||||
* @covers ::insertComponent
|
||||
* @covers ::getComponentsByRegion
|
||||
*/
|
||||
public function testUpdateBlock() {
|
||||
$this->section->updateBlock('some-region', 'existing-uuid', [
|
||||
'block' => [
|
||||
'id' => 'existing-block-id',
|
||||
'settings' => [
|
||||
'foo' => 'bar',
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
public function testInsertComponent() {
|
||||
$expected = [
|
||||
'empty-region' => [],
|
||||
'some-region' => [
|
||||
'existing-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'existing-block-id',
|
||||
'settings' => [
|
||||
'foo' => 'bar',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'ordered-region' => [
|
||||
'first-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'first-block-id',
|
||||
],
|
||||
],
|
||||
'second-uuid' => [
|
||||
'block' => [
|
||||
'id' => 'second-block-id',
|
||||
],
|
||||
],
|
||||
],
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(4),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(3),
|
||||
'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(2),
|
||||
];
|
||||
$this->assertSame($expected, $this->section->getValue());
|
||||
|
||||
$this->section->insertComponent(0, new SectionComponent('new-uuid', 'ordered-region'));
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::updateBlock
|
||||
* @covers ::insertComponent
|
||||
*/
|
||||
public function testUpdateBlockInvalidRegion() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
|
||||
$this->section->updateBlock('invalid-region', 'new-uuid', []);
|
||||
public function testInsertComponentAppend() {
|
||||
$expected = [
|
||||
'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
|
||||
'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
|
||||
'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
|
||||
'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(4),
|
||||
];
|
||||
|
||||
$this->section->insertComponent(2, new SectionComponent('new-uuid', 'ordered-region'));
|
||||
$this->assertComponents($expected, $this->section);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::updateBlock
|
||||
* @covers ::insertComponent
|
||||
*/
|
||||
public function testUpdateBlockInvalidUuid() {
|
||||
$this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID');
|
||||
$this->section->updateBlock('ordered-region', 'new-uuid', []);
|
||||
public function testInsertComponentInvalidDelta() {
|
||||
$this->setExpectedException(\OutOfBoundsException::class, 'Invalid delta "7" for the "new-uuid" component');
|
||||
$this->section->insertComponent(7, new SectionComponent('new-uuid', 'ordered-region'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the section has the expected components.
|
||||
*
|
||||
* @param \Drupal\layout_builder\SectionComponent[] $expected
|
||||
* The expected sections.
|
||||
* @param \Drupal\layout_builder\Section $section
|
||||
* The section storage to check.
|
||||
*/
|
||||
protected function assertComponents(array $expected, Section $section) {
|
||||
$result = $section->getComponents();
|
||||
$this->assertEquals($expected, $result);
|
||||
$this->assertSame(array_keys($expected), array_keys($result));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue