Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 102
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
TreePanel
0.00% covered (danger)
0.00%
0 / 96
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 6
420
0.00% covered (danger)
0.00%
0 / 1
 create
0.00% covered (danger)
0.00%
0 / 3
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 / 3
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 / 8
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
 buildSingleComponent
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
90
 buildSingleBlock
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
20
 getComponentVariantLabel
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Plugin\display_builder\Island;
6
7use Drupal\Core\StringTranslation\TranslatableMarkup;
8use Drupal\display_builder\Attribute\Island;
9use Drupal\display_builder\InstanceInterface;
10use Drupal\display_builder\IslandType;
11use Drupal\display_builder\SlotSourceProxy;
12use Symfony\Component\DependencyInjection\ContainerInterface;
13
14/**
15 * Layers island plugin implementation.
16 */
17#[Island(
18  id: 'tree',
19  label: new TranslatableMarkup('Tree'),
20  description: new TranslatableMarkup('Hierarchical view of components and blocks.'),
21  type: IslandType::View,
22  icon: 'bar-chart-steps',
23)]
24class TreePanel extends BuilderPanel {
25
26  /**
27   * Proxy for slot source operations.
28   */
29  protected SlotSourceProxy $slotSourceProxy;
30
31  /**
32   * {@inheritdoc}
33   */
34  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
35    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
36    $instance->slotSourceProxy = $container->get('display_builder.slot_sources_proxy');
37
38    return $instance;
39  }
40
41  /**
42   * {@inheritdoc}
43   */
44  public static function keyboardShortcuts(): array {
45    return [
46      't' => t('Show the tree'),
47    ];
48  }
49
50  /**
51   * {@inheritdoc}
52   */
53  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
54    $builder_id = (string) $builder->id();
55
56    return [
57      '#type' => 'component',
58      '#component' => 'display_builder:panel_tree',
59      '#slots' => [
60        'items' => $this->digFromSlot($builder_id, $data),
61      ],
62    ];
63  }
64
65  /**
66   * {@inheritdoc}
67   */
68  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
69    $component_id = $data['source']['component']['component_id'] ?? NULL;
70    $instance_id = $instance_id ?: $data['node_id'];
71
72    if (!$instance_id && !$component_id) {
73      return [];
74    }
75
76    $component = $this->sdcManager->getDefinition($component_id);
77
78    if (!$component) {
79      return [];
80    }
81
82    $slots = [];
83
84    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
85      $items = [
86        '#type' => 'component',
87        '#component' => 'display_builder:tree_item',
88        '#props' => [
89          'icon' => 'box-arrow-in-right',
90        ],
91        '#slots' => [
92          'title' => $definition['title'],
93        ],
94        // Slot is needed for contextual menu paste.
95        // @see assets/js/contextual_menu.js
96        '#attributes' => [
97          'data-slot-id' => $slot_id,
98          'data-slot-title' => $definition['title'],
99          'data-node-id' => $instance_id,
100          'data-node-title' => $component['label'],
101          'data-menu-type' => 'slot',
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $items['#slots']['children'] = $this->digFromSlot($builder_id, $sources);
108      }
109
110      $slots[] = $items;
111    }
112
113    // I f a single item, expand by default.
114    if (\count($slots) === 1) {
115      $slots[0]['#props']['expanded'] = TRUE;
116    }
117
118    $name = $component['name'];
119    $variant = $this->getComponentVariantLabel($data, $component);
120
121    if ($variant) {
122      $name .= ' - ' . $variant;
123    }
124
125    return [
126      '#type' => 'component',
127      '#component' => 'display_builder:tree_item',
128      '#props' => [
129        'expanded' => TRUE,
130        'icon' => 'box',
131      ],
132      '#slots' => [
133        'title' => $name,
134        'children' => $slots,
135      ],
136      // Required for the context menu label.
137      // @see assets/js/contextual_menu.js
138      '#attributes' => [
139        'data-node-id' => $instance_id,
140        'data-node-title' => $name,
141        'data-slot-position' => $index,
142        'data-menu-type' => 'component',
143      ],
144    ];
145  }
146
147  /**
148   * {@inheritdoc}
149   */
150  public function buildSingleBlock(string $builder_id, string $instance_id, array $data, int $index = 0): array {
151    $instance_id = $instance_id ?: $data['node_id'];
152    $label = $this->slotSourceProxy->getLabelWithSummary($data, $this->configuration['contexts'] ?? []);
153
154    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
155      $label['summary'] = (string) $this->t('Field: @label', ['@label' => $label['label']]);
156    }
157
158    return [
159      '#type' => 'component',
160      '#component' => 'display_builder:tree_item',
161      '#props' => [
162        'icon' => 'view-list',
163      ],
164      '#slots' => [
165        'title' => $label['summary'],
166      ],
167      '#attributes' => [
168        'data-node-id' => $instance_id,
169        // This label is used for contextual menu.
170        // @see assets/js/contextual_menu.js
171        'data-node-title' => $label['summary'],
172        'data-slot-position' => $index,
173        'data-menu-type' => 'block',
174      ],
175    ];
176  }
177
178  /**
179   * Get the label for a component variant.
180   *
181   * @param array $data
182   *   The component data array.
183   * @param array $definition
184   *   The component definition array.
185   *
186   * @return string
187   *   The variant label or empty string if no variant is set.
188   */
189  private function getComponentVariantLabel(array $data, array $definition): string {
190    if (!isset($data['source']['component']['variant_id'])) {
191      return '';
192    }
193
194    if ($data['source']['component']['variant_id']['source_id'] !== 'select') {
195      return '';
196    }
197    $variant_id = $data['source']['component']['variant_id']['source']['value'] ?? '';
198
199    if (empty($variant_id)) {
200      return '';
201    }
202
203    return $definition['variants'][$variant_id]['title'] ?? '';
204  }
205
206}

Branches

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

TreePanel->build
53  public function build(InstanceInterface $builder, array $data = [], array $options = []): array {
54    $builder_id = (string) $builder->id();
55
56    return [
57      '#type' => 'component',
58      '#component' => 'display_builder:panel_tree',
59      '#slots' => [
60        'items' => $this->digFromSlot($builder_id, $data),
TreePanel->buildSingleBlock
150  public function buildSingleBlock(string $builder_id, string $instance_id, array $data, int $index = 0): array {
151    $instance_id = $instance_id ?: $data['node_id'];
152    $label = $this->slotSourceProxy->getLabelWithSummary($data, $this->configuration['contexts'] ?? []);
153
154    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
154    if (isset($data['source_id']) && $data['source_id'] === 'entity_field') {
155      $label['summary'] = (string) $this->t('Field: @label', ['@label' => $label['label']]);
156    }
157
158    return [
159      '#type' => 'component',
159      '#type' => 'component',
160      '#component' => 'display_builder:tree_item',
161      '#props' => [
162        'icon' => 'view-list',
163      ],
164      '#slots' => [
165        'title' => $label['summary'],
166      ],
167      '#attributes' => [
168        'data-node-id' => $instance_id,
169        // This label is used for contextual menu.
170        // @see assets/js/contextual_menu.js
171        'data-node-title' => $label['summary'],
172        'data-slot-position' => $index,
173        'data-menu-type' => 'block',
TreePanel->buildSingleComponent
68  public function buildSingleComponent(string $builder_id, string $instance_id, array $data, int $index = 0): array {
69    $component_id = $data['source']['component']['component_id'] ?? NULL;
70    $instance_id = $instance_id ?: $data['node_id'];
71
72    if (!$instance_id && !$component_id) {
72    if (!$instance_id && !$component_id) {
73      return [];
76    $component = $this->sdcManager->getDefinition($component_id);
77
78    if (!$component) {
79      return [];
82    $slots = [];
83
84    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
84    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
84    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
85      $items = [
86        '#type' => 'component',
87        '#component' => 'display_builder:tree_item',
88        '#props' => [
89          'icon' => 'box-arrow-in-right',
90        ],
91        '#slots' => [
92          'title' => $definition['title'],
93        ],
94        // Slot is needed for contextual menu paste.
95        // @see assets/js/contextual_menu.js
96        '#attributes' => [
97          'data-slot-id' => $slot_id,
98          'data-slot-title' => $definition['title'],
99          'data-node-id' => $instance_id,
100          'data-node-title' => $component['label'],
101          'data-menu-type' => 'slot',
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $items['#slots']['children'] = $this->digFromSlot($builder_id, $sources);
108      }
109
110      $slots[] = $items;
84    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
85      $items = [
86        '#type' => 'component',
87        '#component' => 'display_builder:tree_item',
88        '#props' => [
89          'icon' => 'box-arrow-in-right',
90        ],
91        '#slots' => [
92          'title' => $definition['title'],
93        ],
94        // Slot is needed for contextual menu paste.
95        // @see assets/js/contextual_menu.js
96        '#attributes' => [
97          'data-slot-id' => $slot_id,
98          'data-slot-title' => $definition['title'],
99          'data-node-id' => $instance_id,
100          'data-node-title' => $component['label'],
101          'data-menu-type' => 'slot',
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $items['#slots']['children'] = $this->digFromSlot($builder_id, $sources);
108      }
109
110      $slots[] = $items;
84    foreach ($component['slots'] ?? [] as $slot_id => $definition) {
85      $items = [
86        '#type' => 'component',
87        '#component' => 'display_builder:tree_item',
88        '#props' => [
89          'icon' => 'box-arrow-in-right',
90        ],
91        '#slots' => [
92          'title' => $definition['title'],
93        ],
94        // Slot is needed for contextual menu paste.
95        // @see assets/js/contextual_menu.js
96        '#attributes' => [
97          'data-slot-id' => $slot_id,
98          'data-slot-title' => $definition['title'],
99          'data-node-id' => $instance_id,
100          'data-node-title' => $component['label'],
101          'data-menu-type' => 'slot',
102        ],
103      ];
104
105      if (isset($data['source']['component']['slots'][$slot_id]['sources'])) {
106        $sources = $data['source']['component']['slots'][$slot_id]['sources'];
107        $items['#slots']['children'] = $this->digFromSlot($builder_id, $sources);
108      }
109
110      $slots[] = $items;
111    }
112
113    // I f a single item, expand by default.
114    if (\count($slots) === 1) {
115      $slots[0]['#props']['expanded'] = TRUE;
116    }
117
118    $name = $component['name'];
118    $name = $component['name'];
119    $variant = $this->getComponentVariantLabel($data, $component);
120
121    if ($variant) {
122      $name .= ' - ' . $variant;
123    }
124
125    return [
126      '#type' => 'component',
126      '#type' => 'component',
127      '#component' => 'display_builder:tree_item',
128      '#props' => [
129        'expanded' => TRUE,
130        'icon' => 'box',
131      ],
132      '#slots' => [
133        'title' => $name,
134        'children' => $slots,
135      ],
136      // Required for the context menu label.
137      // @see assets/js/contextual_menu.js
138      '#attributes' => [
139        'data-node-id' => $instance_id,
140        'data-node-title' => $name,
141        'data-slot-position' => $index,
142        'data-menu-type' => 'component',
TreePanel->create
34  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
35    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
36    $instance->slotSourceProxy = $container->get('display_builder.slot_sources_proxy');
37
38    return $instance;
TreePanel->getComponentVariantLabel
189  private function getComponentVariantLabel(array $data, array $definition): string {
190    if (!isset($data['source']['component']['variant_id'])) {
191      return '';
194    if ($data['source']['component']['variant_id']['source_id'] !== 'select') {
195      return '';
197    $variant_id = $data['source']['component']['variant_id']['source']['value'] ?? '';
198
199    if (empty($variant_id)) {
200      return '';
203    return $definition['variants'][$variant_id]['title'] ?? '';
TreePanel->keyboardShortcuts
46      't' => t('Show the tree'),