Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 85
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
PreviewPanel
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 4
56
0.00% covered (danger)
0.00%
0 / 1
 create
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 keyboardShortcuts
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 build
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 alterPreviewPlaceholder
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\display_builder\Island;
6
7use Drupal\Core\Render\RendererInterface;
8use Drupal\Core\StringTranslation\TranslatableMarkup;
9use Drupal\display_builder\Attribute\Island;
10use Drupal\display_builder\DisplayBuilderHelpers;
11use Drupal\display_builder\InstanceInterface;
12use Drupal\display_builder\Island\IslandPluginBase;
13use Drupal\display_builder\Island\IslandReloadEventsTrait;
14use Drupal\display_builder\Island\IslandType;
15use Drupal\ui_patterns\Element\ComponentElementBuilder;
16use Symfony\Component\DependencyInjection\ContainerInterface;
17
18/**
19 * Preview island plugin implementation.
20 */
21#[Island(
22  id: 'preview',
23  enabled_by_default: TRUE,
24  label: new TranslatableMarkup('Preview'),
25  description: new TranslatableMarkup('Show a real time preview of the display.'),
26  type: IslandType::View,
27  default_region: 'main',
28  icon: 'binoculars',
29)]
30class PreviewPanel extends IslandPluginBase {
31
32  use IslandReloadEventsTrait;
33
34  /**
35   * The component element builder.
36   */
37  protected ComponentElementBuilder $componentElementBuilder;
38
39  /**
40   * The renderer.
41   */
42  protected RendererInterface $renderer;
43
44  /**
45   * {@inheritdoc}
46   */
47  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
48    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
49    $instance->componentElementBuilder = $container->get('ui_patterns.component_element_builder');
50    $instance->renderer = $container->get('renderer');
51
52    return $instance;
53  }
54
55  /**
56   * {@inheritdoc}
57   */
58  public static function keyboardShortcuts(): array {
59    return [
60      'key' => 'p',
61      'help' => t('Show the preview'),
62    ];
63  }
64
65  /**
66   * {@inheritdoc}
67   */
68  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
69    if (empty($data)) {
70      return [];
71    }
72
73    $this->alterPreviewPlaceholder($data);
74
75    $returned = [];
76
77    foreach ($data as $slot) {
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
90        $build = $this->buildPlaceholder($this->t('[Placeholder] No preview'));
91      }
92
93      $returned[] = $build;
94    }
95
96    return $returned;
97  }
98
99  /**
100   * Replace placeholder for preview.
101   *
102   * Some block source will not be created, create a simple placeholder to have
103   * a preview instead of nothing.
104   *
105   * @todo move as an issue to UI Patterns?
106   *
107   * @param array $data
108   *   The instance data to replace.
109   */
110  private function alterPreviewPlaceholder(array &$data): void {
111    $replacements = [
112      [
113        'search' => ['plugin_id' => 'system_messages_block'],
114        'new_value_title' => new TranslatableMarkup('[Placeholder] Block messages'),
115      ],
116      [
117        'search' => ['source_id' => 'page_title'],
118        'new_value_title' => new TranslatableMarkup('[Placeholder] Page title'),
119      ],
120      [
121        'search' => ['source_id' => 'main_page_content'],
122        'new_value_title' => new TranslatableMarkup('[Placeholder] Page content'),
123        'new_value_class' => 'db-preview-placeholder-lg',
124      ],
125      [
126        'search' => ['source_id' => 'view_attachment_before'],
127        'new_value_title' => new TranslatableMarkup('[Placeholder] View attachment before'),
128        'new_value_class' => 'db-preview-placeholder-md',
129      ],
130      [
131        'search' => ['source_id' => 'view_exposed'],
132        'new_value_title' => new TranslatableMarkup('[Placeholder] View exposed form'),
133        'new_value_class' => 'db-preview-placeholder-md',
134      ],
135      [
136        'search' => ['source_id' => 'view_header'],
137        'new_value_title' => new TranslatableMarkup('[Placeholder] View header'),
138        'new_value_class' => 'db-preview-placeholder-md',
139      ],
140      [
141        'search' => ['source_id' => 'view_rows'],
142        'new_value_title' => new TranslatableMarkup('[Placeholder] View rows'),
143        'new_value_class' => 'db-preview-placeholder-lg',
144      ],
145      [
146        'search' => ['source_id' => 'view_attachment_after'],
147        'new_value_title' => new TranslatableMarkup('[Placeholder] View attachment after'),
148        'new_value_class' => 'db-preview-placeholder-md',
149      ],
150      [
151        'search' => ['source_id' => 'view_pager'],
152        'new_value_title' => new TranslatableMarkup('[Placeholder] View pager'),
153      ],
154      [
155        'search' => ['source_id' => 'view_more'],
156        'new_value_title' => new TranslatableMarkup('[Placeholder] View more'),
157      ],
158      [
159        'search' => ['source_id' => 'view_footer'],
160        'new_value_title' => new TranslatableMarkup('[Placeholder] View footer'),
161        'new_value_class' => 'db-preview-placeholder-md',
162      ],
163      [
164        'search' => ['source_id' => 'view_feed_icons'],
165        'new_value_title' => new TranslatableMarkup('[Placeholder] View feed icons'),
166      ],
167    ];
168
169    DisplayBuilderHelpers::findAndReplaceInArray($data, $replacements);
170  }
171
172}

Paths

Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

PreviewPanel->alterPreviewPlaceholder
110  private function alterPreviewPlaceholder(array &$data): void {
111    $replacements = [
112      [
113        'search' => ['plugin_id' => 'system_messages_block'],
114        'new_value_title' => new TranslatableMarkup('[Placeholder] Block messages'),
115      ],
116      [
117        'search' => ['source_id' => 'page_title'],
118        'new_value_title' => new TranslatableMarkup('[Placeholder] Page title'),
119      ],
120      [
121        'search' => ['source_id' => 'main_page_content'],
122        'new_value_title' => new TranslatableMarkup('[Placeholder] Page content'),
123        'new_value_class' => 'db-preview-placeholder-lg',
124      ],
125      [
126        'search' => ['source_id' => 'view_attachment_before'],
127        'new_value_title' => new TranslatableMarkup('[Placeholder] View attachment before'),
128        'new_value_class' => 'db-preview-placeholder-md',
129      ],
130      [
131        'search' => ['source_id' => 'view_exposed'],
132        'new_value_title' => new TranslatableMarkup('[Placeholder] View exposed form'),
133        'new_value_class' => 'db-preview-placeholder-md',
134      ],
135      [
136        'search' => ['source_id' => 'view_header'],
137        'new_value_title' => new TranslatableMarkup('[Placeholder] View header'),
138        'new_value_class' => 'db-preview-placeholder-md',
139      ],
140      [
141        'search' => ['source_id' => 'view_rows'],
142        'new_value_title' => new TranslatableMarkup('[Placeholder] View rows'),
143        'new_value_class' => 'db-preview-placeholder-lg',
144      ],
145      [
146        'search' => ['source_id' => 'view_attachment_after'],
147        'new_value_title' => new TranslatableMarkup('[Placeholder] View attachment after'),
148        'new_value_class' => 'db-preview-placeholder-md',
149      ],
150      [
151        'search' => ['source_id' => 'view_pager'],
152        'new_value_title' => new TranslatableMarkup('[Placeholder] View pager'),
153      ],
154      [
155        'search' => ['source_id' => 'view_more'],
156        'new_value_title' => new TranslatableMarkup('[Placeholder] View more'),
157      ],
158      [
159        'search' => ['source_id' => 'view_footer'],
160        'new_value_title' => new TranslatableMarkup('[Placeholder] View footer'),
161        'new_value_class' => 'db-preview-placeholder-md',
162      ],
163      [
164        'search' => ['source_id' => 'view_feed_icons'],
165        'new_value_title' => new TranslatableMarkup('[Placeholder] View feed icons'),
166      ],
167    ];
168
169    DisplayBuilderHelpers::findAndReplaceInArray($data, $replacements);
170  }
PreviewPanel->build
68  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
69    if (empty($data)) {
 
70      return [];
68  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
69    if (empty($data)) {
 
73    $this->alterPreviewPlaceholder($data);
74
75    $returned = [];
76
77    foreach ($data as $slot) {
 
77    foreach ($data as $slot) {
 
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
 
90        $build = $this->buildPlaceholder($this->t('[Placeholder] No preview'));
91      }
92
93      $returned[] = $build;
 
77    foreach ($data as $slot) {
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
90        $build = $this->buildPlaceholder($this->t('[Placeholder] No preview'));
91      }
92
93      $returned[] = $build;
 
77    foreach ($data as $slot) {
 
77    foreach ($data as $slot) {
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
90        $build = $this->buildPlaceholder($this->t('[Placeholder] No preview'));
91      }
92
93      $returned[] = $build;
94    }
95
96    return $returned;
97  }
68  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
69    if (empty($data)) {
 
73    $this->alterPreviewPlaceholder($data);
74
75    $returned = [];
76
77    foreach ($data as $slot) {
 
77    foreach ($data as $slot) {
 
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
 
77    foreach ($data as $slot) {
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
90        $build = $this->buildPlaceholder($this->t('[Placeholder] No preview'));
91      }
92
93      $returned[] = $build;
 
77    foreach ($data as $slot) {
 
77    foreach ($data as $slot) {
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
90        $build = $this->buildPlaceholder($this->t('[Placeholder] No preview'));
91      }
92
93      $returned[] = $build;
94    }
95
96    return $returned;
97  }
68  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
69    if (empty($data)) {
 
73    $this->alterPreviewPlaceholder($data);
74
75    $returned = [];
76
77    foreach ($data as $slot) {
 
77    foreach ($data as $slot) {
 
77    foreach ($data as $slot) {
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
90        $build = $this->buildPlaceholder($this->t('[Placeholder] No preview'));
91      }
92
93      $returned[] = $build;
94    }
95
96    return $returned;
97  }
68  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
69    if (empty($data)) {
 
73    $this->alterPreviewPlaceholder($data);
74
75    $returned = [];
76
77    foreach ($data as $slot) {
 
77    foreach ($data as $slot) {
78      $build = $this->componentElementBuilder->buildSource([], 'content', [], $slot, $this->configuration['contexts'] ?? []);
79      $build = $build['#slots']['content'][0] ?? [];
80
81      // Some sources (e.g. a comment field's "Add comment" form) throw once
82      // actually rendered rather than producing empty markup - @see
83      // RenderableBuilderTrait::isRenderEmptyOrFailing(). Unlike
84      // BuilderPanel, which already guards every source it builds the same
85      // way, nothing here previously checked this, so a throwing source
86      // shipped its #lazy_builder unresolved into the page, breaking Drupal
87      // core's own BigPipe processing of unrelated placeholders (e.g. the
88      // Navigation module's admin toolbar) later in the same request.
89      if ($this->isRenderEmptyOrFailing($this->renderer, $build)) {
90        $build = $this->buildPlaceholder($this->t('[Placeholder] No preview'));
91      }
92
93      $returned[] = $build;
94    }
95
96    return $returned;
97  }
PreviewPanel->create
47  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
48    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
49    $instance->componentElementBuilder = $container->get('ui_patterns.component_element_builder');
50    $instance->renderer = $container->get('renderer');
51
52    return $instance;
53  }
PreviewPanel->keyboardShortcuts
60      'key' => 'p',
61      'help' => t('Show the preview'),
62    ];
63  }