Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 59 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
ApiPreviewController | |
0.00% |
0 / 59 |
|
0.00% |
0 / 8 |
380 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getBlockPreview | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getPresetPreview | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
getComponentPreview | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
12 | |||
generateBlock | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
2 | |||
generateStory | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
generateComponent | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
90 | |||
renderSource | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Drupal\display_builder\Controller; |
6 | |
7 | use Drupal\Core\Controller\ControllerBase; |
8 | use Drupal\Core\Render\HtmlResponse; |
9 | use Drupal\Core\Render\RendererInterface; |
10 | use Drupal\Core\Theme\ComponentPluginManager; |
11 | use Drupal\display_builder\RenderableBuilderTrait; |
12 | use Drupal\ui_patterns_library\StoryPluginManager; |
13 | use Symfony\Component\DependencyInjection\Attribute\Autowire; |
14 | |
15 | /** |
16 | * Returns preview responses for Display builder routes. |
17 | */ |
18 | class 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 | $build = $builder->buildSource([], 'content', [], $data, []) ?? []; |
217 | |
218 | return $build['#slots']['content'][0] ?? []; |
219 | } |
220 | |
221 | } |