Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiPreviewController
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 8
420
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
n/a
0 / 0
n/a
0 / 0
0.00% covered (danger)
0.00%
0 / 1
2
 getBlockPreview
0.00% covered (danger)
0.00%
0 / 5
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
 getPresetPreview
0.00% covered (danger)
0.00%
0 / 7
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
 getComponentPreview
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 generateBlock
0.00% covered (danger)
0.00%
0 / 7
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
 generateStory
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 generateComponent
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
90
 renderSource
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Controller;
6
7use Drupal\Core\Controller\ControllerBase;
8use Drupal\Core\Render\HtmlResponse;
9use Drupal\Core\Render\RendererInterface;
10use Drupal\Core\Theme\ComponentPluginManager;
11use Drupal\display_builder\RenderableBuilderTrait;
12use Drupal\ui_patterns_library\StoryPluginManager;
13use Symfony\Component\DependencyInjection\Attribute\Autowire;
14
15/**
16 * Returns preview responses for Display builder routes.
17 */
18class ApiPreviewController extends ControllerBase {
19
20  use RenderableBuilderTrait;
21
22  /**
23   * The Pattern preset storage.
24   *
25   * @var \Drupal\Core\Entity\EntityStorageInterface
26   */
27  protected $presetConfigStorage;
28
29  public function __construct(
30    #[Autowire(service: 'plugin.manager.component_story')]
31    private StoryPluginManager $storyPluginManager,
32    #[Autowire(service: 'plugin.manager.sdc')]
33    private ComponentPluginManager $componentManager,
34    private RendererInterface $renderer,
35  ) {
36    $this->presetConfigStorage = $this->entityTypeManager()->getStorage('pattern_preset');
37  }
38
39  /**
40   * Get block preview.
41   *
42   * @param string $block_id
43   *   Block ID.
44   *
45   * @return \Drupal\Core\Render\HtmlResponse
46   *   The HTML response.
47   */
48  public function getBlockPreview(string $block_id): HtmlResponse {
49    $build = $this->generateBlock($block_id);
50
51    $html = $this->renderer->renderRoot($build);
52    $response = new HtmlResponse();
53    $response->setContent($html);
54
55    return $response;
56  }
57
58  /**
59   * Get preset preview.
60   *
61   * @param string $preset_id
62   *   Preset ID.
63   *
64   * @return \Drupal\Core\Render\HtmlResponse
65   *   The HTML response.
66   */
67  public function getPresetPreview(string $preset_id): HtmlResponse {
68    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
69    $preset = $this->presetConfigStorage->load($preset_id);
70    $data = $preset->getSources([], FALSE);
71
72    $build = $this->renderSource($data);
73
74    $html = $this->renderer->renderRoot($build);
75    $response = new HtmlResponse();
76    $response->setContent($html);
77
78    return $response;
79  }
80
81  /**
82   * Get component preview, only HTML without specific css or js.
83   *
84   * @param string $component_id
85   *   Component ID.
86   * @param string $variant_id
87   *   Component variant ID.
88   *
89   * @return \Drupal\Core\Render\HtmlResponse
90   *   The HTML response.
91   */
92  public function getComponentPreview(string $component_id, string $variant_id): HtmlResponse {
93    $ui_patterns_library = $this->moduleHandler()->moduleExists('ui_patterns_library');
94
95    $build = [];
96
97    if (!$ui_patterns_library) {
98      $build = $this->generateComponent($component_id);
99    }
100    else {
101      $stories = $this->storyPluginManager->getComponentStories($component_id);
102
103      if (empty($stories)) {
104        $build = $this->generateComponent($component_id);
105      }
106      else {
107        $story = [];
108        $first_story = \reset($stories);
109        $story[$first_story['machineName'] ?? 'default'] = $first_story;
110        $build = $this->generateStory($component_id, $variant_id, $story);
111      }
112    }
113
114    $html = $this->renderer->renderRoot($build);
115    $response = new HtmlResponse();
116    $response->setContent($html);
117
118    return $response;
119  }
120
121  /**
122   * Build renderable block.
123   *
124   * @param string $block_id
125   *   The block id to preview.
126   *
127   * @return array
128   *   A renderable array.
129   */
130  protected function generateBlock(string $block_id): array {
131    $data = [
132      'source_id' => 'block',
133      'source' => [
134        'plugin_id' => $block_id,
135      ],
136    ];
137
138    return $this->renderSource($data);
139  }
140
141  /**
142   * Generate a story.
143   *
144   * @param string $component_id
145   *   The component id.
146   * @param string $variant_id
147   *   The component variant id.
148   * @param array $stories
149   *   The stories.
150   *
151   * @return array
152   *   The render array
153   */
154  private function generateStory(string $component_id, string $variant_id, array $stories): array {
155    $html = [];
156
157    foreach (\array_keys($stories) as $story_id) {
158      $html[$story_id] = [
159        '#type' => 'component',
160        '#component' => $component_id,
161        '#story' => $story_id,
162        '#props' => ['variant' => $variant_id],
163      ];
164    }
165
166    return $html;
167  }
168
169  /**
170   * Generate a content even with empty story or no library module.
171   *
172   * @param string $component_id
173   *   The component id.
174   *
175   * @return array
176   *   The render array
177   *
178   * @todo Remove when https://www.drupal.org/project/ui_patterns/issues/3414774 is merged
179   */
180  private function generateComponent(string $component_id): array {
181    $definition = $this->componentManager->getDefinition($component_id);
182    $html = [
183      '#type' => 'component',
184      '#component' => $component_id,
185    ];
186
187    foreach ($definition['slots'] ?? [] as $slot_id => $slot) {
188      if (isset($slot['examples']) && \is_array($slot['examples']) && !empty($slot['examples'])) {
189        $html['#slots'][$slot_id] = $slot['examples'][0];
190      }
191    }
192
193    foreach ($definition['props']['properties'] ?? [] as $prop_id => $prop) {
194      if (isset($prop['examples']) && \is_array($prop['examples']) && !empty($prop['examples'])) {
195        $html['#props'][$prop_id] = $prop['examples'][0];
196      }
197    }
198
199    return $html;
200  }
201
202  /**
203   * Get renderable array for a slot source.
204   *
205   * @param array $data
206   *   The slot source data array containing:
207   *   - source_id: The source ID
208   *   - source: Array of source configuration.
209   *
210   * @return array
211   *   The renderable array for this slot source.
212   */
213  private function renderSource(array $data): array {
214    /** @var \Drupal\ui_patterns\Element\ComponentElementBuilder $builder */
215    $builder = \Drupal::service('ui_patterns.component_element_builder'); // @phpcs:ignore
216
217    try {
218      $build = $builder->buildSource([], 'content', [], $data, []) ?? [];
219
220      return $build['#slots']['content'][0] ?? [];
221    }
222    catch (\Throwable $th) {
223      return [];
224    }
225  }
226
227}

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.

ApiPreviewController->generateBlock
130  protected function generateBlock(string $block_id): array {
131    $data = [
132      'source_id' => 'block',
133      'source' => [
134        'plugin_id' => $block_id,
135      ],
136    ];
137
138    return $this->renderSource($data);
ApiPreviewController->generateComponent
180  private function generateComponent(string $component_id): array {
181    $definition = $this->componentManager->getDefinition($component_id);
182    $html = [
183      '#type' => 'component',
184      '#component' => $component_id,
185    ];
186
187    foreach ($definition['slots'] ?? [] as $slot_id => $slot) {
187    foreach ($definition['slots'] ?? [] as $slot_id => $slot) {
187    foreach ($definition['slots'] ?? [] as $slot_id => $slot) {
188      if (isset($slot['examples']) && \is_array($slot['examples']) && !empty($slot['examples'])) {
188      if (isset($slot['examples']) && \is_array($slot['examples']) && !empty($slot['examples'])) {
188      if (isset($slot['examples']) && \is_array($slot['examples']) && !empty($slot['examples'])) {
187    foreach ($definition['slots'] ?? [] as $slot_id => $slot) {
188      if (isset($slot['examples']) && \is_array($slot['examples']) && !empty($slot['examples'])) {
189        $html['#slots'][$slot_id] = $slot['examples'][0];
187    foreach ($definition['slots'] ?? [] as $slot_id => $slot) {
188      if (isset($slot['examples']) && \is_array($slot['examples']) && !empty($slot['examples'])) {
189        $html['#slots'][$slot_id] = $slot['examples'][0];
190      }
191    }
192
193    foreach ($definition['props']['properties'] ?? [] as $prop_id => $prop) {
193    foreach ($definition['props']['properties'] ?? [] as $prop_id => $prop) {
193    foreach ($definition['props']['properties'] ?? [] as $prop_id => $prop) {
194      if (isset($prop['examples']) && \is_array($prop['examples']) && !empty($prop['examples'])) {
194      if (isset($prop['examples']) && \is_array($prop['examples']) && !empty($prop['examples'])) {
194      if (isset($prop['examples']) && \is_array($prop['examples']) && !empty($prop['examples'])) {
193    foreach ($definition['props']['properties'] ?? [] as $prop_id => $prop) {
194      if (isset($prop['examples']) && \is_array($prop['examples']) && !empty($prop['examples'])) {
195        $html['#props'][$prop_id] = $prop['examples'][0];
193    foreach ($definition['props']['properties'] ?? [] as $prop_id => $prop) {
194      if (isset($prop['examples']) && \is_array($prop['examples']) && !empty($prop['examples'])) {
195        $html['#props'][$prop_id] = $prop['examples'][0];
196      }
197    }
198
199    return $html;
ApiPreviewController->generateStory
154  private function generateStory(string $component_id, string $variant_id, array $stories): array {
155    $html = [];
156
157    foreach (\array_keys($stories) as $story_id) {
157    foreach (\array_keys($stories) as $story_id) {
157    foreach (\array_keys($stories) as $story_id) {
158      $html[$story_id] = [
159        '#type' => 'component',
157    foreach (\array_keys($stories) as $story_id) {
158      $html[$story_id] = [
159        '#type' => 'component',
160        '#component' => $component_id,
161        '#story' => $story_id,
162        '#props' => ['variant' => $variant_id],
163      ];
164    }
165
166    return $html;
ApiPreviewController->getBlockPreview
48  public function getBlockPreview(string $block_id): HtmlResponse {
49    $build = $this->generateBlock($block_id);
50
51    $html = $this->renderer->renderRoot($build);
52    $response = new HtmlResponse();
53    $response->setContent($html);
54
55    return $response;
ApiPreviewController->getComponentPreview
92  public function getComponentPreview(string $component_id, string $variant_id): HtmlResponse {
93    $ui_patterns_library = $this->moduleHandler()->moduleExists('ui_patterns_library');
94
95    $build = [];
96
97    if (!$ui_patterns_library) {
97    if (!$ui_patterns_library) {
98      $build = $this->generateComponent($component_id);
101      $stories = $this->storyPluginManager->getComponentStories($component_id);
102
103      if (empty($stories)) {
103      if (empty($stories)) {
104        $build = $this->generateComponent($component_id);
107        $story = [];
108        $first_story = \reset($stories);
109        $story[$first_story['machineName'] ?? 'default'] = $first_story;
110        $build = $this->generateStory($component_id, $variant_id, $story);
111      }
112    }
113
114    $html = $this->renderer->renderRoot($build);
114    $html = $this->renderer->renderRoot($build);
115    $response = new HtmlResponse();
116    $response->setContent($html);
117
118    return $response;
ApiPreviewController->getPresetPreview
67  public function getPresetPreview(string $preset_id): HtmlResponse {
68    /** @var \Drupal\display_builder\PatternPresetInterface $preset */
69    $preset = $this->presetConfigStorage->load($preset_id);
70    $data = $preset->getSources([], FALSE);
71
72    $build = $this->renderSource($data);
73
74    $html = $this->renderer->renderRoot($build);
75    $response = new HtmlResponse();
76    $response->setContent($html);
77
78    return $response;
ApiPreviewController->renderSource
213  private function renderSource(array $data): array {
214    /** @var \Drupal\ui_patterns\Element\ComponentElementBuilder $builder */
215    $builder = \Drupal::service('ui_patterns.component_element_builder'); // @phpcs:ignore
216
217    try {
218      $build = $builder->buildSource([], 'content', [], $data, []) ?? [];
219
220      return $build['#slots']['content'][0] ?? [];
222    catch (\Throwable $th) {
223      return [];