@amazeelabs/silverback-gutenberg 2.6.3 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/drupal/silverback_gutenberg/src/BlockSerializer.php +2 -2
- package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergCardinalityValidatorTrait.php +168 -13
- package/drupal/silverback_gutenberg/src/LinkProcessor.php +4 -4
- package/drupal/silverback_gutenberg/src/Service/MediaService.php +1 -1
- package/drupal/silverback_gutenberg/tests/src/Unit/BlockValidatorCardinalityTest.php +285 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [2.7.1](https://github.com/amazeeio-solutions/silverback-template/compare/@amazeelabs/silverback-gutenberg@2.7.0...@amazeelabs/silverback-gutenberg@2.7.1) (2026-01-22)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @amazeelabs/silverback-gutenberg
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [2.7.0](https://github.com/amazeeio-solutions/silverback-template/compare/@amazeelabs/silverback-gutenberg@2.6.3...@amazeelabs/silverback-gutenberg@2.7.0) (2025-12-09)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* **SLB-468:** cardinality validator improvement ([a9b3e39](https://github.com/amazeeio-solutions/silverback-template/commit/a9b3e394e4567e2c721c8a65b69e11a2c544040a))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [2.6.3](https://github.com/AmazeeLabs/silverback-template/compare/@amazeelabs/silverback-gutenberg@2.6.2...@amazeelabs/silverback-gutenberg@2.6.3) (2025-09-26)
|
|
7
26
|
|
|
8
27
|
**Note:** Version bump only for package @amazeelabs/silverback-gutenberg
|
|
@@ -35,7 +35,7 @@ class BlockSerializer {
|
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
protected function get_comment_delimited_block_content($block_name = null, $block_attributes = null, $block_content = '') {
|
|
38
|
+
protected function get_comment_delimited_block_content(?string $block_name = null, ?array $block_attributes = null, string $block_content = '') {
|
|
39
39
|
if ( is_null( $block_name ) ) {
|
|
40
40
|
return $block_content;
|
|
41
41
|
}
|
|
@@ -60,7 +60,7 @@ class BlockSerializer {
|
|
|
60
60
|
);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
protected function strip_core_block_namespace( $block_name = null ) {
|
|
63
|
+
protected function strip_core_block_namespace( ?string $block_name = null ) {
|
|
64
64
|
if ( is_string( $block_name ) && 0 === strpos( $block_name, 'core/' ) ) {
|
|
65
65
|
return substr( $block_name, 5 );
|
|
66
66
|
}
|
package/drupal/silverback_gutenberg/src/GutenbergValidation/GutenbergCardinalityValidatorTrait.php
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
namespace Drupal\silverback_gutenberg\GutenbergValidation;
|
|
4
4
|
|
|
5
|
+
use Drupal\Component\Utility\Html;
|
|
5
6
|
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -73,17 +74,26 @@ trait GutenbergCardinalityValidatorTrait {
|
|
|
73
74
|
return $this->validateEmptyInnerBlocks($expected_children);
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
// Count blocks
|
|
77
|
+
// Count blocks and keep references for additional validations.
|
|
77
78
|
$countInnerBlockInstances = [];
|
|
79
|
+
$innerBlocksByName = [];
|
|
78
80
|
foreach ($block['innerBlocks'] as $innerBlock) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
$blockName = $innerBlock['blockName'] ?? NULL;
|
|
82
|
+
if ($blockName === NULL) {
|
|
83
|
+
continue;
|
|
81
84
|
}
|
|
82
|
-
$countInnerBlockInstances[$
|
|
85
|
+
if (!isset($countInnerBlockInstances[$blockName])) {
|
|
86
|
+
$countInnerBlockInstances[$blockName] = 0;
|
|
87
|
+
}
|
|
88
|
+
$countInnerBlockInstances[$blockName]++;
|
|
89
|
+
$innerBlocksByName[$blockName][] = $innerBlock;
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
foreach ($expected_children as $child) {
|
|
86
|
-
|
|
93
|
+
$blockName = $child['blockName'];
|
|
94
|
+
$childBlocks = $innerBlocksByName[$blockName] ?? [];
|
|
95
|
+
|
|
96
|
+
if (!isset($countInnerBlockInstances[$blockName]) && $child['min'] > 0) {
|
|
87
97
|
$message = $this->getExpectedQuantityErrorMessage($child);
|
|
88
98
|
return [
|
|
89
99
|
'is_valid' => FALSE,
|
|
@@ -91,13 +101,12 @@ trait GutenbergCardinalityValidatorTrait {
|
|
|
91
101
|
];
|
|
92
102
|
}
|
|
93
103
|
// Minimum is set to 0, so we don't care if the block is not present.
|
|
94
|
-
if (!isset($countInnerBlockInstances[$
|
|
95
|
-
|
|
96
|
-
'is_valid' => TRUE,
|
|
97
|
-
'message' => '',
|
|
98
|
-
];
|
|
104
|
+
if (!isset($countInnerBlockInstances[$blockName]) && $child['min'] === 0) {
|
|
105
|
+
continue;
|
|
99
106
|
}
|
|
100
|
-
|
|
107
|
+
|
|
108
|
+
$blockCount = $countInnerBlockInstances[$blockName] ?? 0;
|
|
109
|
+
if ($blockCount < $child['min']) {
|
|
101
110
|
return [
|
|
102
111
|
'is_valid' => FALSE,
|
|
103
112
|
'message' => \Drupal::translation()->formatPlural($child['min'],
|
|
@@ -109,7 +118,7 @@ trait GutenbergCardinalityValidatorTrait {
|
|
|
109
118
|
]),
|
|
110
119
|
];
|
|
111
120
|
}
|
|
112
|
-
if ($child['max'] !== GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED && $
|
|
121
|
+
if ($child['max'] !== GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED && $blockCount > $child['max']) {
|
|
113
122
|
return [
|
|
114
123
|
'is_valid' => FALSE,
|
|
115
124
|
'message' => \Drupal::translation()->formatPlural($child['max'],
|
|
@@ -121,6 +130,13 @@ trait GutenbergCardinalityValidatorTrait {
|
|
|
121
130
|
]),
|
|
122
131
|
];
|
|
123
132
|
}
|
|
133
|
+
|
|
134
|
+
if (!empty($childBlocks) && !$this->hasPopulatedBlock($childBlocks)) {
|
|
135
|
+
return [
|
|
136
|
+
'is_valid' => FALSE,
|
|
137
|
+
'message' => $this->getMissingContentErrorMessage($child),
|
|
138
|
+
];
|
|
139
|
+
}
|
|
124
140
|
}
|
|
125
141
|
|
|
126
142
|
return [
|
|
@@ -172,7 +188,18 @@ trait GutenbergCardinalityValidatorTrait {
|
|
|
172
188
|
private function validateAnyInnerBlocks(array $inner_blocks, array $expected_children): array {
|
|
173
189
|
$min = $expected_children['min'];
|
|
174
190
|
$max = $expected_children['max'];
|
|
175
|
-
$
|
|
191
|
+
$innerBlockList = $inner_blocks['innerBlocks'] ?? [];
|
|
192
|
+
$count = count($innerBlockList);
|
|
193
|
+
if (
|
|
194
|
+
$count > 0 &&
|
|
195
|
+
!$this->hasPopulatedBlock($innerBlockList) &&
|
|
196
|
+
!$this->isBlockPopulated($inner_blocks)
|
|
197
|
+
) {
|
|
198
|
+
return [
|
|
199
|
+
'is_valid' => FALSE,
|
|
200
|
+
'message' => $this->getMissingContentErrorMessage(NULL),
|
|
201
|
+
];
|
|
202
|
+
}
|
|
176
203
|
if ($count < $min) {
|
|
177
204
|
return [
|
|
178
205
|
'is_valid' => FALSE,
|
|
@@ -218,4 +245,132 @@ trait GutenbergCardinalityValidatorTrait {
|
|
|
218
245
|
return $result;
|
|
219
246
|
}
|
|
220
247
|
|
|
248
|
+
private function blockHasMeaningfulHtml(array $block): bool {
|
|
249
|
+
$innerHTML = $block['innerHTML'] ?? '';
|
|
250
|
+
if (is_string($innerHTML) && $this->stringContainsContent($innerHTML)) {
|
|
251
|
+
return TRUE;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!empty($block['innerContent']) && is_array($block['innerContent'])) {
|
|
255
|
+
foreach ($block['innerContent'] as $chunk) {
|
|
256
|
+
if (is_string($chunk) && $this->stringContainsContent($chunk)) {
|
|
257
|
+
return TRUE;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return FALSE;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private function stringContainsContent(string $value): bool {
|
|
266
|
+
$decoded = Html::decodeEntities($value);
|
|
267
|
+
$stripped = trim(strip_tags($decoded));
|
|
268
|
+
if ($stripped !== '') {
|
|
269
|
+
return TRUE;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return (bool) preg_match('/<(img|video|audio|iframe|svg|figure|source|embed|object|picture)\b/i', $value);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private function blockHasMeaningfulAttributes(array $block): bool {
|
|
276
|
+
$attrs = $block['attrs'] ?? [];
|
|
277
|
+
if (empty($attrs)) {
|
|
278
|
+
return FALSE;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
foreach ($attrs as $value) {
|
|
282
|
+
if ($this->isMeaningfulValue($value)) {
|
|
283
|
+
return TRUE;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return FALSE;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private function isMeaningfulValue(mixed $value): bool {
|
|
291
|
+
if ($value === NULL) {
|
|
292
|
+
return FALSE;
|
|
293
|
+
}
|
|
294
|
+
if (is_string($value)) {
|
|
295
|
+
return trim($value) !== '';
|
|
296
|
+
}
|
|
297
|
+
if (is_bool($value)) {
|
|
298
|
+
return $value;
|
|
299
|
+
}
|
|
300
|
+
if (is_numeric($value)) {
|
|
301
|
+
return TRUE;
|
|
302
|
+
}
|
|
303
|
+
if (is_array($value)) {
|
|
304
|
+
foreach ($value as $item) {
|
|
305
|
+
if ($this->isMeaningfulValue($item)) {
|
|
306
|
+
return TRUE;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return FALSE;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return TRUE;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Checks if any block in the supplied list is populated.
|
|
317
|
+
*/
|
|
318
|
+
private function hasPopulatedBlock(array $blocks): bool {
|
|
319
|
+
foreach ($blocks as $block) {
|
|
320
|
+
if (is_array($block) && $this->isBlockPopulated($block)) {
|
|
321
|
+
return TRUE;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return FALSE;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private function isBlockPopulated(array $block): bool {
|
|
328
|
+
$evaluated = FALSE;
|
|
329
|
+
|
|
330
|
+
if (array_key_exists('innerHTML', $block) || array_key_exists('innerContent', $block)) {
|
|
331
|
+
$evaluated = TRUE;
|
|
332
|
+
if ($this->blockHasMeaningfulHtml($block)) {
|
|
333
|
+
return TRUE;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (array_key_exists('attrs', $block)) {
|
|
338
|
+
$evaluated = TRUE;
|
|
339
|
+
if ($this->blockHasMeaningfulAttributes($block)) {
|
|
340
|
+
return TRUE;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!empty($block['innerBlocks']) && is_array($block['innerBlocks'])) {
|
|
345
|
+
$evaluated = TRUE;
|
|
346
|
+
foreach ($block['innerBlocks'] as $innerBlock) {
|
|
347
|
+
if (is_array($innerBlock) && $this->isBlockPopulated($innerBlock)) {
|
|
348
|
+
return TRUE;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (!$evaluated) {
|
|
354
|
+
return TRUE;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return FALSE;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private function getMissingContentErrorMessage(?array $child_block): string|TranslatableMarkup {
|
|
361
|
+
$messageSuffix = t('content or attributes.');
|
|
362
|
+
|
|
363
|
+
if (!empty($child_block)) {
|
|
364
|
+
$messageParams = [
|
|
365
|
+
'%label' => $child_block['blockLabel'],
|
|
366
|
+
'@message_suffix' => $messageSuffix,
|
|
367
|
+
];
|
|
368
|
+
return t('%label: block must contain @message_suffix', $messageParams);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return t('Block must contain @message_suffix', [
|
|
372
|
+
'@message_suffix' => $messageSuffix,
|
|
373
|
+
]);
|
|
374
|
+
}
|
|
375
|
+
|
|
221
376
|
}
|
|
@@ -71,7 +71,7 @@ class LinkProcessor {
|
|
|
71
71
|
$this->cacheableMetadata->addCacheableDependency($other_object);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
public function processLinks(string $html, string $direction, LanguageInterface $language = NULL) {
|
|
74
|
+
public function processLinks(string $html, string $direction, ?LanguageInterface $language = NULL) {
|
|
75
75
|
if (!in_array($direction, ['inbound', 'outbound'], TRUE)) {
|
|
76
76
|
throw new \Exception('Unknown direction: "' . $direction . '".');
|
|
77
77
|
}
|
|
@@ -99,7 +99,7 @@ class LinkProcessor {
|
|
|
99
99
|
return $processed;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
protected function processBlocks(&$blocks, string $direction, LanguageInterface $language = NULL): void {
|
|
102
|
+
protected function processBlocks(&$blocks, string $direction, ?LanguageInterface $language = NULL): void {
|
|
103
103
|
$processUrlCallback = fn(string $url) => $this->processUrl($url, $direction, $language);
|
|
104
104
|
$processLinksCallback = fn(string $html) => $this->processLinks($html, $direction, $language);
|
|
105
105
|
foreach ($blocks as &$block) {
|
|
@@ -203,7 +203,7 @@ class LinkProcessor {
|
|
|
203
203
|
return $this->buildUrl($parts);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
protected function processLink(\DOMElement $link, string $direction, LanguageInterface $language = NULL) {
|
|
206
|
+
protected function processLink(\DOMElement $link, string $direction, ?LanguageInterface $language = NULL) {
|
|
207
207
|
if ($direction === 'outbound' && !$language) {
|
|
208
208
|
throw new \Exception('$language is required for "outbound" direction.');
|
|
209
209
|
}
|
|
@@ -229,7 +229,7 @@ class LinkProcessor {
|
|
|
229
229
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
public function processUrl(string $url, string $direction, LanguageInterface $language = NULL, array &$metadata = NULL): string {
|
|
232
|
+
public function processUrl(string $url, string $direction, ?LanguageInterface $language = NULL, ?array &$metadata = NULL): string {
|
|
233
233
|
$metadata = [];
|
|
234
234
|
|
|
235
235
|
if ($direction === 'outbound' && !$language) {
|
|
@@ -7,7 +7,7 @@ use Drupal\media_library\MediaLibraryState;
|
|
|
7
7
|
|
|
8
8
|
class MediaService extends Original {
|
|
9
9
|
|
|
10
|
-
public function renderDialog(array $media_types, array $media_bundles = NULL) {
|
|
10
|
+
public function renderDialog(array $media_types, ?array $media_bundles = NULL) {
|
|
11
11
|
// Instead of guessing media types as the parent method does, use given
|
|
12
12
|
// media types as media type IDs. So the blocks have full control over
|
|
13
13
|
// allowed media types.
|
|
@@ -1534,4 +1534,289 @@ class BlockValidatorCardinalityTest extends UnitTestCase {
|
|
|
1534
1534
|
);
|
|
1535
1535
|
}
|
|
1536
1536
|
|
|
1537
|
+
public function testRequirePopulatedBlocksWithHtml() {
|
|
1538
|
+
$expectedChildren = [
|
|
1539
|
+
[
|
|
1540
|
+
'blockName' => 'core/paragraph',
|
|
1541
|
+
'blockLabel' => t('Paragraph'),
|
|
1542
|
+
'min' => 1,
|
|
1543
|
+
'max' => GutenbergCardinalityValidatorInterface::CARDINALITY_UNLIMITED,
|
|
1544
|
+
],
|
|
1545
|
+
];
|
|
1546
|
+
|
|
1547
|
+
$validBlock = [
|
|
1548
|
+
'blockName' => 'core/column',
|
|
1549
|
+
'innerBlocks' => [
|
|
1550
|
+
[
|
|
1551
|
+
'blockName' => 'core/paragraph',
|
|
1552
|
+
'innerHTML' => '<p>Content</p>',
|
|
1553
|
+
'innerContent' => ['<p>Content</p>'],
|
|
1554
|
+
],
|
|
1555
|
+
],
|
|
1556
|
+
];
|
|
1557
|
+
$invalidBlock = [
|
|
1558
|
+
'blockName' => 'core/column',
|
|
1559
|
+
'innerBlocks' => [
|
|
1560
|
+
[
|
|
1561
|
+
'blockName' => 'core/paragraph',
|
|
1562
|
+
'innerHTML' => '<p></p>',
|
|
1563
|
+
'innerContent' => ['<p></p>'],
|
|
1564
|
+
],
|
|
1565
|
+
],
|
|
1566
|
+
];
|
|
1567
|
+
|
|
1568
|
+
$this->assertEquals(
|
|
1569
|
+
[
|
|
1570
|
+
'is_valid' => TRUE,
|
|
1571
|
+
'message' => '',
|
|
1572
|
+
],
|
|
1573
|
+
$this->validateCardinality($validBlock, $expectedChildren),
|
|
1574
|
+
);
|
|
1575
|
+
$this->assertEquals(
|
|
1576
|
+
[
|
|
1577
|
+
'is_valid' => FALSE,
|
|
1578
|
+
'message' => '<em class="placeholder">Paragraph</em>: block must contain content or attributes.',
|
|
1579
|
+
],
|
|
1580
|
+
$this->validateCardinality($invalidBlock, $expectedChildren),
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
public function testMixedContentBlocksAreValid() {
|
|
1585
|
+
$expectedChildren = [
|
|
1586
|
+
[
|
|
1587
|
+
'blockName' => 'core/paragraph',
|
|
1588
|
+
'blockLabel' => t('Paragraph'),
|
|
1589
|
+
'min' => 1,
|
|
1590
|
+
'max' => 3,
|
|
1591
|
+
],
|
|
1592
|
+
];
|
|
1593
|
+
|
|
1594
|
+
$block = [
|
|
1595
|
+
'blockName' => 'core/column',
|
|
1596
|
+
'innerBlocks' => [
|
|
1597
|
+
[
|
|
1598
|
+
'blockName' => 'core/paragraph',
|
|
1599
|
+
'innerHTML' => '<p></p>',
|
|
1600
|
+
'innerContent' => ['<p></p>'],
|
|
1601
|
+
],
|
|
1602
|
+
[
|
|
1603
|
+
'blockName' => 'core/paragraph',
|
|
1604
|
+
'innerHTML' => '<p>Content</p>',
|
|
1605
|
+
'innerContent' => ['<p>Content</p>'],
|
|
1606
|
+
],
|
|
1607
|
+
],
|
|
1608
|
+
];
|
|
1609
|
+
|
|
1610
|
+
$this->assertEquals(
|
|
1611
|
+
[
|
|
1612
|
+
'is_valid' => TRUE,
|
|
1613
|
+
'message' => '',
|
|
1614
|
+
],
|
|
1615
|
+
$this->validateCardinality($block, $expectedChildren),
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
public function testRequirePopulatedBlocksWithAttributes() {
|
|
1620
|
+
$expectedChildren = [
|
|
1621
|
+
[
|
|
1622
|
+
'blockName' => 'core/embed',
|
|
1623
|
+
'blockLabel' => t('Embed'),
|
|
1624
|
+
'min' => 1,
|
|
1625
|
+
'max' => 1,
|
|
1626
|
+
],
|
|
1627
|
+
];
|
|
1628
|
+
|
|
1629
|
+
$validBlock = [
|
|
1630
|
+
'blockName' => 'core/column',
|
|
1631
|
+
'innerBlocks' => [
|
|
1632
|
+
[
|
|
1633
|
+
'blockName' => 'core/embed',
|
|
1634
|
+
'innerHTML' => '',
|
|
1635
|
+
'innerContent' => [],
|
|
1636
|
+
'attrs' => [
|
|
1637
|
+
'url' => 'https://example.com/video',
|
|
1638
|
+
],
|
|
1639
|
+
],
|
|
1640
|
+
],
|
|
1641
|
+
];
|
|
1642
|
+
$invalidBlock = [
|
|
1643
|
+
'blockName' => 'core/column',
|
|
1644
|
+
'innerBlocks' => [
|
|
1645
|
+
[
|
|
1646
|
+
'blockName' => 'core/embed',
|
|
1647
|
+
'innerHTML' => '',
|
|
1648
|
+
'innerContent' => [],
|
|
1649
|
+
'attrs' => [],
|
|
1650
|
+
],
|
|
1651
|
+
],
|
|
1652
|
+
];
|
|
1653
|
+
|
|
1654
|
+
$this->assertEquals(
|
|
1655
|
+
[
|
|
1656
|
+
'is_valid' => TRUE,
|
|
1657
|
+
'message' => '',
|
|
1658
|
+
],
|
|
1659
|
+
$this->validateCardinality($validBlock, $expectedChildren),
|
|
1660
|
+
);
|
|
1661
|
+
$this->assertEquals(
|
|
1662
|
+
[
|
|
1663
|
+
'is_valid' => FALSE,
|
|
1664
|
+
'message' => '<em class="placeholder">Embed</em>: block must contain content or attributes.',
|
|
1665
|
+
],
|
|
1666
|
+
$this->validateCardinality($invalidBlock, $expectedChildren),
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
public function testAnyBlockTypeRequiresContent() {
|
|
1671
|
+
$expectedChildren = [
|
|
1672
|
+
'validationType' => GutenbergCardinalityValidatorInterface::CARDINALITY_ANY,
|
|
1673
|
+
'min' => 1,
|
|
1674
|
+
'max' => 2,
|
|
1675
|
+
];
|
|
1676
|
+
|
|
1677
|
+
$validBlock = [
|
|
1678
|
+
'blockName' => 'core/group',
|
|
1679
|
+
'innerBlocks' => [
|
|
1680
|
+
[
|
|
1681
|
+
'blockName' => 'core/paragraph',
|
|
1682
|
+
'innerHTML' => '<p>Content</p>',
|
|
1683
|
+
'innerContent' => ['<p>Content</p>'],
|
|
1684
|
+
],
|
|
1685
|
+
],
|
|
1686
|
+
];
|
|
1687
|
+
$invalidBlock = [
|
|
1688
|
+
'blockName' => 'core/group',
|
|
1689
|
+
'innerBlocks' => [
|
|
1690
|
+
[
|
|
1691
|
+
'blockName' => 'core/paragraph',
|
|
1692
|
+
'innerHTML' => '<p></p>',
|
|
1693
|
+
'innerContent' => ['<p></p>'],
|
|
1694
|
+
],
|
|
1695
|
+
],
|
|
1696
|
+
];
|
|
1697
|
+
|
|
1698
|
+
$this->assertEquals(
|
|
1699
|
+
[
|
|
1700
|
+
'is_valid' => TRUE,
|
|
1701
|
+
'message' => '',
|
|
1702
|
+
],
|
|
1703
|
+
$this->validateCardinality($validBlock, $expectedChildren),
|
|
1704
|
+
);
|
|
1705
|
+
$this->assertEquals(
|
|
1706
|
+
[
|
|
1707
|
+
'is_valid' => FALSE,
|
|
1708
|
+
'message' => 'Block must contain content or attributes.',
|
|
1709
|
+
],
|
|
1710
|
+
$this->validateCardinality($invalidBlock, $expectedChildren),
|
|
1711
|
+
);
|
|
1712
|
+
$this->assertEquals(
|
|
1713
|
+
[
|
|
1714
|
+
'is_valid' => FALSE,
|
|
1715
|
+
'message' => 'At least 1 block is required.',
|
|
1716
|
+
],
|
|
1717
|
+
$this->validateCardinality([
|
|
1718
|
+
'blockName' => 'core/group',
|
|
1719
|
+
'innerBlocks' => [],
|
|
1720
|
+
], $expectedChildren),
|
|
1721
|
+
);
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
public function testAnyBlockTypeAcceptsNestedContent() {
|
|
1725
|
+
$expectedChildren = [
|
|
1726
|
+
'validationType' => GutenbergCardinalityValidatorInterface::CARDINALITY_ANY,
|
|
1727
|
+
'min' => 1,
|
|
1728
|
+
'max' => 2,
|
|
1729
|
+
];
|
|
1730
|
+
|
|
1731
|
+
$block = [
|
|
1732
|
+
'blockName' => 'core/group',
|
|
1733
|
+
'innerBlocks' => [
|
|
1734
|
+
[
|
|
1735
|
+
'blockName' => 'custom/accordion-item-text',
|
|
1736
|
+
'innerHTML' => '',
|
|
1737
|
+
'innerContent' => [],
|
|
1738
|
+
'innerBlocks' => [
|
|
1739
|
+
[
|
|
1740
|
+
'blockName' => 'core/paragraph',
|
|
1741
|
+
'innerHTML' => '<p>Content</p>',
|
|
1742
|
+
'innerContent' => ['<p>Content</p>'],
|
|
1743
|
+
],
|
|
1744
|
+
],
|
|
1745
|
+
],
|
|
1746
|
+
],
|
|
1747
|
+
];
|
|
1748
|
+
|
|
1749
|
+
$this->assertEquals(
|
|
1750
|
+
[
|
|
1751
|
+
'is_valid' => TRUE,
|
|
1752
|
+
'message' => '',
|
|
1753
|
+
],
|
|
1754
|
+
$this->validateCardinality($block, $expectedChildren),
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
public function testBlockWithOnlyNestedContentSucceedsWhenDescendantHasContent() {
|
|
1759
|
+
$expectedChildren = [
|
|
1760
|
+
[
|
|
1761
|
+
'blockName' => 'custom/accordion-item-text',
|
|
1762
|
+
'blockLabel' => t('Accordion item'),
|
|
1763
|
+
'min' => 1,
|
|
1764
|
+
'max' => 1,
|
|
1765
|
+
],
|
|
1766
|
+
];
|
|
1767
|
+
|
|
1768
|
+
$validBlock = [
|
|
1769
|
+
'blockName' => 'custom/accordion',
|
|
1770
|
+
'innerBlocks' => [
|
|
1771
|
+
[
|
|
1772
|
+
'blockName' => 'custom/accordion-item-text',
|
|
1773
|
+
'innerHTML' => '',
|
|
1774
|
+
'innerContent' => [],
|
|
1775
|
+
'attrs' => [],
|
|
1776
|
+
'innerBlocks' => [
|
|
1777
|
+
[
|
|
1778
|
+
'blockName' => 'core/paragraph',
|
|
1779
|
+
'innerHTML' => '<p>Some content</p>',
|
|
1780
|
+
'innerContent' => ['<p>Some content</p>'],
|
|
1781
|
+
],
|
|
1782
|
+
],
|
|
1783
|
+
],
|
|
1784
|
+
],
|
|
1785
|
+
];
|
|
1786
|
+
|
|
1787
|
+
$invalidBlock = [
|
|
1788
|
+
'blockName' => 'custom/accordion',
|
|
1789
|
+
'innerBlocks' => [
|
|
1790
|
+
[
|
|
1791
|
+
'blockName' => 'custom/accordion-item-text',
|
|
1792
|
+
'innerHTML' => '',
|
|
1793
|
+
'innerContent' => [],
|
|
1794
|
+
'attrs' => [],
|
|
1795
|
+
'innerBlocks' => [
|
|
1796
|
+
[
|
|
1797
|
+
'blockName' => 'core/paragraph',
|
|
1798
|
+
'innerHTML' => '<p></p>',
|
|
1799
|
+
'innerContent' => ['<p></p>'],
|
|
1800
|
+
],
|
|
1801
|
+
],
|
|
1802
|
+
],
|
|
1803
|
+
],
|
|
1804
|
+
];
|
|
1805
|
+
|
|
1806
|
+
$this->assertEquals(
|
|
1807
|
+
[
|
|
1808
|
+
'is_valid' => TRUE,
|
|
1809
|
+
'message' => '',
|
|
1810
|
+
],
|
|
1811
|
+
$this->validateCardinality($validBlock, $expectedChildren),
|
|
1812
|
+
);
|
|
1813
|
+
$this->assertEquals(
|
|
1814
|
+
[
|
|
1815
|
+
'is_valid' => FALSE,
|
|
1816
|
+
'message' => '<em class="placeholder">Accordion item</em>: block must contain content or attributes.',
|
|
1817
|
+
],
|
|
1818
|
+
$this->validateCardinality($invalidBlock, $expectedChildren),
|
|
1819
|
+
);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1537
1822
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amazeelabs/silverback-gutenberg",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"precommit": "pnpm precommit:fix && pnpm precommit:check && pnpm test:unit",
|
|
6
6
|
"precommit:fix": "pnpm --filter @custom/cms precommit:fix packages/@amazeelabs/silverback-gutenberg/drupal",
|
|
@@ -12,5 +12,5 @@
|
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
|
-
"gitHead": "
|
|
15
|
+
"gitHead": "8e0638f5452e77742f99aebe4a65606aac751103"
|
|
16
16
|
}
|