feat: Outline und Knoten zeigen Gruppen-Presets mit Namen
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
ca3bbf5ad1
commit
c3f08e2d33
@@ -84,14 +84,37 @@ final class NodeSummary
|
|||||||
/** @return list<string> */
|
/** @return list<string> */
|
||||||
public static function presetIds(Block $block): array
|
public static function presetIds(Block $block): array
|
||||||
{
|
{
|
||||||
$presets = $block->attrs()['modulePreset'] ?? [];
|
return self::idList($block->attrs()['modulePreset'] ?? []);
|
||||||
if (is_string($presets)) {
|
|
||||||
$presets = [$presets];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return is_array($presets)
|
/**
|
||||||
? array_values(array_filter($presets, static fn (mixed $id): bool => is_string($id) && $id !== ''))
|
* Option group presets of the block (formats §3 "Verweise in Blöcken"): groupPreset.<groupId> holds
|
||||||
: [];
|
* {presetId: [id, …], groupName: "divi/<group>"}. Empty IDs are dropped; an entry without a valid
|
||||||
|
* groupName or without any ID is left out.
|
||||||
|
*
|
||||||
|
* @return array<string, array{groupName: string, presetIds: list<string>}> groupId => entry
|
||||||
|
*/
|
||||||
|
public static function groupPresets(Block $block): array
|
||||||
|
{
|
||||||
|
$entries = $block->attrs()['groupPreset'] ?? [];
|
||||||
|
if (! is_array($entries)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$groups = [];
|
||||||
|
foreach ($entries as $groupId => $entry) {
|
||||||
|
if (! is_array($entry)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $entry['groupName'] ?? null;
|
||||||
|
$ids = self::idList($entry['presetId'] ?? []);
|
||||||
|
if (is_string($name) && $name !== '' && $ids !== []) {
|
||||||
|
$groups[(string) $groupId] = ['groupName' => $name, 'presetIds' => $ids];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Layout facts: section type, column widths of a row (e.g. "8_24+16_24"). */
|
/** Layout facts: section type, column widths of a row (e.g. "8_24+16_24"). */
|
||||||
@@ -162,6 +185,22 @@ final class NodeSummary
|
|||||||
return trim((string) preg_replace('/\s+/u', ' ', $text));
|
return trim((string) preg_replace('/\s+/u', ' ', $text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preset IDs as Divi stores them: a list, or a single string (legacy).
|
||||||
|
*
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
private static function idList(mixed $ids): array
|
||||||
|
{
|
||||||
|
if (is_string($ids)) {
|
||||||
|
$ids = [$ids];
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_array($ids)
|
||||||
|
? array_values(array_filter($ids, static fn (mixed $id): bool => is_string($id) && $id !== ''))
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
private static function rank(string $path): int
|
private static function rank(string $path): int
|
||||||
{
|
{
|
||||||
$index = array_search(explode('.', $path)[0], self::PRIORITY, true);
|
$index = array_search(explode('.', $path)[0], self::PRIORITY, true);
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ final class Outline
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list<array{id: string, type: string, label: string, excerpt: string, info: string,
|
* @return list<array{id: string, type: string, label: string, excerpt: string, info: string,
|
||||||
* presets: list<string>, children: int, editable: bool, depth: int, truncated: bool,
|
* presets: list<string>, group_presets: list<string>, children: int, editable: bool, depth: int,
|
||||||
* global_id: ?string}>
|
* truncated: bool, global_id: ?string}>
|
||||||
*/
|
*/
|
||||||
public function rows(NodeIndex $index, ?string $rootId = null, ?int $maxDepth = null, ?int $limit = null): array
|
public function rows(NodeIndex $index, ?string $rootId = null, ?int $maxDepth = null, ?int $limit = null): array
|
||||||
{
|
{
|
||||||
@@ -121,6 +121,8 @@ final class Outline
|
|||||||
$line .= ($row['global_id'] ?? null) !== null ? ' [global ' . $row['global_id'] . ']' : '';
|
$line .= ($row['global_id'] ?? null) !== null ? ' [global ' . $row['global_id'] . ']' : '';
|
||||||
$line .= $row['info'] !== '' ? ' [' . $row['info'] . ']' : '';
|
$line .= $row['info'] !== '' ? ' [' . $row['info'] . ']' : '';
|
||||||
$line .= $row['presets'] !== [] ? ' {Preset: ' . implode(', ', $row['presets']) . '}' : '';
|
$line .= $row['presets'] !== [] ? ' {Preset: ' . implode(', ', $row['presets']) . '}' : '';
|
||||||
|
$groupPresets = $row['group_presets'] ?? [];
|
||||||
|
$line .= $groupPresets !== [] ? ' {Gruppen-Presets: ' . implode('; ', $groupPresets) . '}' : '';
|
||||||
$line .= $row['excerpt'] !== '' ? ' – ' . $row['excerpt'] : '';
|
$line .= $row['excerpt'] !== '' ? ' – ' . $row['excerpt'] : '';
|
||||||
$line .= $row['editable'] ? '' : ' (nur lesbar)';
|
$line .= $row['editable'] ? '' : ' (nur lesbar)';
|
||||||
$line .= ($row['truncated'] ?? false) ? ' (weitere Kinder abgeschnitten)' : '';
|
$line .= ($row['truncated'] ?? false) ? ' (weitere Kinder abgeschnitten)' : '';
|
||||||
@@ -132,8 +134,8 @@ final class Outline
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{id: string, type: string, label: string, excerpt: string, info: string,
|
* @return array{id: string, type: string, label: string, excerpt: string, info: string,
|
||||||
* presets: list<string>, children: int, editable: bool, depth: int, truncated: bool,
|
* presets: list<string>, group_presets: list<string>, children: int, editable: bool, depth: int,
|
||||||
* global_id: ?string}
|
* truncated: bool, global_id: ?string}
|
||||||
*/
|
*/
|
||||||
private function row(Block $block, string $id, int $depth): array
|
private function row(Block $block, string $id, int $depth): array
|
||||||
{
|
{
|
||||||
@@ -152,10 +154,8 @@ final class Outline
|
|||||||
'label' => NodeSummary::label($block),
|
'label' => NodeSummary::label($block),
|
||||||
'excerpt' => NodeSummary::excerpt($block),
|
'excerpt' => NodeSummary::excerpt($block),
|
||||||
'info' => $editable ? NodeSummary::info($block) : '',
|
'info' => $editable ? NodeSummary::info($block) : '',
|
||||||
'presets' => array_map(
|
'presets' => $this->namesOf(NodeSummary::presetIds($block)),
|
||||||
fn (string $presetId): string => $this->presetNames[$presetId] ?? $presetId,
|
'group_presets' => $this->groupPresetLines($block),
|
||||||
NodeSummary::presetIds($block)
|
|
||||||
),
|
|
||||||
'children' => $editable ? count($block->children()) : 0,
|
'children' => $editable ? count($block->children()) : 0,
|
||||||
'editable' => $editable,
|
'editable' => $editable,
|
||||||
'depth' => $depth,
|
'depth' => $depth,
|
||||||
@@ -163,4 +163,31 @@ final class Outline
|
|||||||
'global_id' => $this->globalIds ? ($global['id'] ?? null) : null,
|
'global_id' => $this->globalIds ? ($global['id'] ?? null) : null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One entry per option group: "<short group name>: <preset name>[, <preset name>]", e.g.
|
||||||
|
* "font-body: Fließtext" (the short name is the key "group_presets" of the write tools takes).
|
||||||
|
*
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
private function groupPresetLines(Block $block): array
|
||||||
|
{
|
||||||
|
$lines = [];
|
||||||
|
foreach (NodeSummary::groupPresets($block) as $group) {
|
||||||
|
$name = $group['groupName'];
|
||||||
|
$short = str_starts_with($name, 'divi/') ? substr($name, 5) : $name;
|
||||||
|
$lines[] = $short . ': ' . implode(', ', $this->namesOf($group['presetIds']));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<string> $presetIds
|
||||||
|
* @return list<string> The preset names, the ID where the name is unknown.
|
||||||
|
*/
|
||||||
|
private function namesOf(array $presetIds): array
|
||||||
|
{
|
||||||
|
return array_map(fn (string $presetId): string => $this->presetNames[$presetId] ?? $presetId, $presetIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use LIT\MCP\Abilities\Errors;
|
|||||||
use LIT\MCP\Abilities\JsonMap;
|
use LIT\MCP\Abilities\JsonMap;
|
||||||
use LIT\MCP\Abilities\Rev;
|
use LIT\MCP\Abilities\Rev;
|
||||||
use LIT\MCP\Integrations\Divi\BlockTree\AttrMap;
|
use LIT\MCP\Integrations\Divi\BlockTree\AttrMap;
|
||||||
|
use LIT\MCP\Integrations\Divi\BlockTree\Block;
|
||||||
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
|
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
|
||||||
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
|
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
|
||||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
||||||
@@ -42,9 +43,9 @@ final class DiviNodeGetAbility implements Ability
|
|||||||
{
|
{
|
||||||
return 'Liest einen Knoten der Divi-Seite vollständig: attrs als Attributpfad → Werte je Breakpoint '
|
return 'Liest einen Knoten der Divi-Seite vollständig: attrs als Attributpfad → Werte je Breakpoint '
|
||||||
. '(Vollform, z. B. "title.decoration.font.font": {"desktop": {"value": {…}}}), die Kurzform-Werte, '
|
. '(Vollform, z. B. "title.decoration.font.font": {"desktop": {"value": {…}}}), die Kurzform-Werte, '
|
||||||
. 'Presets und die IDs der Kinder. Pfad-IDs aus lit-divi-outline. Werte wie $variable(…)$ (globale '
|
. 'Presets, Gruppen-Presets und die IDs der Kinder. Pfad-IDs aus lit-divi-outline. Werte wie '
|
||||||
. 'Farben, Variablen, dynamische Inhalte) werden unverändert angezeigt, beim Schreiben aber erneut '
|
. '$variable(…)$ (globale Farben, Variablen, dynamische Inhalte) werden unverändert angezeigt, beim '
|
||||||
. 'geprüft, nie ungeprüft übernommen.';
|
. 'Schreiben aber erneut geprüft, nie ungeprüft übernommen.';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function inputSchema(): array
|
public function inputSchema(): array
|
||||||
@@ -83,6 +84,27 @@ final class DiviNodeGetAbility implements Ability
|
|||||||
'required' => ['id', 'name'],
|
'required' => ['id', 'name'],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
'group_presets' => [
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => 'Gruppen-Presets des Moduls: group_id = Attributpfad der Gruppe, group = '
|
||||||
|
. 'Kurzname (Schlüssel für group_presets).',
|
||||||
|
'items' => [
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => [
|
||||||
|
'group_id' => ['type' => 'string'],
|
||||||
|
'group' => ['type' => 'string'],
|
||||||
|
'presets' => [
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => [
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => ['id' => ['type' => 'string'], 'name' => ['type' => 'string']],
|
||||||
|
'required' => ['id', 'name'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'required' => ['group_id', 'group', 'presets'],
|
||||||
|
],
|
||||||
|
],
|
||||||
'builder_version' => ['type' => ['string', 'null']],
|
'builder_version' => ['type' => ['string', 'null']],
|
||||||
'children' => ['type' => 'array', 'items' => ['type' => 'string']],
|
'children' => ['type' => 'array', 'items' => ['type' => 'string']],
|
||||||
'global' => ['type' => ['object', 'null']],
|
'global' => ['type' => ['object', 'null']],
|
||||||
@@ -163,6 +185,7 @@ final class DiviNodeGetAbility implements Ability
|
|||||||
static fn (string $presetId): array => ['id' => $presetId, 'name' => $names[$presetId] ?? ''],
|
static fn (string $presetId): array => ['id' => $presetId, 'name' => $names[$presetId] ?? ''],
|
||||||
NodeSummary::presetIds($block)
|
NodeSummary::presetIds($block)
|
||||||
),
|
),
|
||||||
|
'group_presets' => $this->groupPresets($block, $names),
|
||||||
'builder_version' => is_string($version) ? $version : null,
|
'builder_version' => is_string($version) ? $version : null,
|
||||||
'children' => $editable
|
'children' => $editable
|
||||||
? array_values(array_filter(array_map([$page->index, 'idOf'], $block->children())))
|
? array_values(array_filter(array_map([$page->index, 'idOf'], $block->children())))
|
||||||
@@ -170,4 +193,26 @@ final class DiviNodeGetAbility implements Ability
|
|||||||
'global' => $global,
|
'global' => $global,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, string> $names Preset ID => name.
|
||||||
|
* @return list<array{group_id: string, group: string, presets: list<array{id: string, name: string}>}>
|
||||||
|
*/
|
||||||
|
private function groupPresets(Block $block, array $names): array
|
||||||
|
{
|
||||||
|
$groups = [];
|
||||||
|
foreach (NodeSummary::groupPresets($block) as $groupId => $group) {
|
||||||
|
$name = $group['groupName'];
|
||||||
|
$groups[] = [
|
||||||
|
'group_id' => $groupId,
|
||||||
|
'group' => str_starts_with($name, 'divi/') ? substr($name, 5) : $name,
|
||||||
|
'presets' => array_map(
|
||||||
|
static fn (string $presetId): array => ['id' => $presetId, 'name' => $names[$presetId] ?? ''],
|
||||||
|
$group['presetIds']
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ final class DiviOutlineAbility implements Ability
|
|||||||
public function description(): string
|
public function description(): string
|
||||||
{
|
{
|
||||||
return 'Liest den Aufbau einer Divi-Seite: Knoten mit Pfad-ID (s0, s0.r1, s0.r1.c0.m2), Modultyp, '
|
return 'Liest den Aufbau einer Divi-Seite: Knoten mit Pfad-ID (s0, s0.r1, s0.r1.c0.m2), Modultyp, '
|
||||||
. 'Admin-Label, Textauszug und Preset. Liefert tree_rev – Pflicht für jede Divi-Änderung. '
|
. 'Admin-Label, Textauszug, Preset und Gruppen-Presets. Liefert tree_rev – Pflicht für jede Divi-Änderung. '
|
||||||
. 'Optional nur ein Teilbaum (id), begrenzte Tiefe (depth) oder begrenzte Breite je Ebene (limit); '
|
. 'Optional nur ein Teilbaum (id), begrenzte Tiefe (depth) oder begrenzte Breite je Ebene (limit); '
|
||||||
. 'abgeschnittene Kinder zeigt "truncated".';
|
. 'abgeschnittene Kinder zeigt "truncated".';
|
||||||
}
|
}
|
||||||
@@ -91,6 +91,12 @@ final class DiviOutlineAbility implements Ability
|
|||||||
'excerpt' => ['type' => 'string'],
|
'excerpt' => ['type' => 'string'],
|
||||||
'info' => ['type' => 'string'],
|
'info' => ['type' => 'string'],
|
||||||
'presets' => ['type' => 'array', 'items' => ['type' => 'string']],
|
'presets' => ['type' => 'array', 'items' => ['type' => 'string']],
|
||||||
|
'group_presets' => [
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => ['type' => 'string'],
|
||||||
|
'description' => 'Gruppen-Presets des Moduls als "<Gruppe>: <Name>[, <Name>]", '
|
||||||
|
. 'z. B. "font-body: Fließtext". Die Gruppe ist der Schlüssel für group_presets.',
|
||||||
|
],
|
||||||
'children' => ['type' => 'integer'],
|
'children' => ['type' => 'integer'],
|
||||||
'editable' => ['type' => 'boolean'],
|
'editable' => ['type' => 'boolean'],
|
||||||
'depth' => ['type' => 'integer'],
|
'depth' => ['type' => 'integer'],
|
||||||
@@ -104,8 +110,8 @@ final class DiviOutlineAbility implements Ability
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
'required' => [
|
'required' => [
|
||||||
'id', 'type', 'label', 'excerpt', 'info', 'presets', 'children', 'editable', 'depth',
|
'id', 'type', 'label', 'excerpt', 'info', 'presets', 'group_presets', 'children',
|
||||||
'truncated', 'global_id',
|
'editable', 'depth', 'truncated', 'global_id',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Tests\Unit\Integrations\Divi\BlockTree;
|
||||||
|
|
||||||
|
use LIT\MCP\Integrations\Divi\BlockTree\Block;
|
||||||
|
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class NodeSummaryTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGroupPresetsByGroupId(): void
|
||||||
|
{
|
||||||
|
$block = Block::create('divi/blurb', ['groupPreset' => [
|
||||||
|
'content.decoration.bodyFont' => ['presetId' => ['ja6o54jtlc'], 'groupName' => 'divi/font-body'],
|
||||||
|
'module.decoration.spacing' => ['presetId' => ['apzj7e17ok', 'b2b2b2b2b2'], 'groupName' => 'divi/spacing'],
|
||||||
|
]]);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
[
|
||||||
|
'content.decoration.bodyFont' => ['groupName' => 'divi/font-body', 'presetIds' => ['ja6o54jtlc']],
|
||||||
|
'module.decoration.spacing' => [
|
||||||
|
'groupName' => 'divi/spacing',
|
||||||
|
'presetIds' => ['apzj7e17ok', 'b2b2b2b2b2'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
NodeSummary::groupPresets($block)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPresetIdAsStringIsAccepted(): void
|
||||||
|
{
|
||||||
|
$block = Block::create('divi/text', ['groupPreset' => [
|
||||||
|
'content.decoration.bodyFont' => ['presetId' => 'ja6o54jtlc', 'groupName' => 'divi/font-body'],
|
||||||
|
]]);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
['content.decoration.bodyFont' => ['groupName' => 'divi/font-body', 'presetIds' => ['ja6o54jtlc']]],
|
||||||
|
NodeSummary::groupPresets($block)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEmptyIdsAreFilteredAndBrokenEntriesLeftOut(): void
|
||||||
|
{
|
||||||
|
$block = Block::create('divi/text', ['groupPreset' => [
|
||||||
|
'a.one' => ['presetId' => ['', 'x1', 7, null], 'groupName' => 'divi/font'],
|
||||||
|
'a.two' => ['presetId' => [''], 'groupName' => 'divi/font'],
|
||||||
|
'a.three' => ['presetId' => ['x2']],
|
||||||
|
'a.four' => ['presetId' => ['x3'], 'groupName' => ''],
|
||||||
|
'a.five' => ['presetId' => ['x4'], 'groupName' => ['divi/font']],
|
||||||
|
'a.six' => 'kein Eintrag',
|
||||||
|
]]);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
['a.one' => ['groupName' => 'divi/font', 'presetIds' => ['x1']]],
|
||||||
|
NodeSummary::groupPresets($block)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBlockWithoutOrWithBrokenGroupPreset(): void
|
||||||
|
{
|
||||||
|
self::assertSame([], NodeSummary::groupPresets(Block::create('divi/text')));
|
||||||
|
self::assertSame([], NodeSummary::groupPresets(Block::create('divi/text', ['groupPreset' => 'x'])));
|
||||||
|
self::assertSame([], NodeSummary::groupPresets(Block::create('divi/text', ['groupPreset' => []])));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\BlockTree;
|
|||||||
|
|
||||||
use LIT\MCP\Integrations\Divi\BlockTree\AttrMap;
|
use LIT\MCP\Integrations\Divi\BlockTree\AttrMap;
|
||||||
use LIT\MCP\Integrations\Divi\BlockTree\Block;
|
use LIT\MCP\Integrations\Divi\BlockTree\Block;
|
||||||
|
use LIT\MCP\Integrations\Divi\BlockTree\Document;
|
||||||
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
|
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
|
||||||
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
|
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
|
||||||
use LIT\MCP\Integrations\Divi\BlockTree\Outline;
|
use LIT\MCP\Integrations\Divi\BlockTree\Outline;
|
||||||
@@ -26,7 +27,8 @@ final class OutlineTest extends TestCase
|
|||||||
|
|
||||||
self::assertSame(
|
self::assertSame(
|
||||||
['id' => 's0.r0.c1.m0', 'type' => 'heading', 'label' => '', 'excerpt' => 'Willkommen bei Müller & Söhne',
|
['id' => 's0.r0.c1.m0', 'type' => 'heading', 'label' => '', 'excerpt' => 'Willkommen bei Müller & Söhne',
|
||||||
'info' => '', 'presets' => ['Hero-Titel'], 'children' => 0, 'editable' => true, 'depth' => 3,
|
'info' => '', 'presets' => ['Hero-Titel'], 'group_presets' => [], 'children' => 0,
|
||||||
|
'editable' => true, 'depth' => 3,
|
||||||
'truncated' => false, 'global_id' => null],
|
'truncated' => false, 'global_id' => null],
|
||||||
$rows[5]
|
$rows[5]
|
||||||
);
|
);
|
||||||
@@ -41,6 +43,56 @@ final class OutlineTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGroupPresetsAreShownWithNamesInRowAndText(): void
|
||||||
|
{
|
||||||
|
$content = DiviFixtures::markup('stage4/page--designsystem.html');
|
||||||
|
$outline = new Outline([
|
||||||
|
'vdcl6msnuo' => '[Fixture] Blurb Leistung',
|
||||||
|
'ja6o54jtlc' => '[Fixture] Fließtext',
|
||||||
|
'apzj7e17ok' => '[Fixture] Abstand Karte',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$rows = $outline->rows(NodeIndex::build((new Parser())->parse($content)));
|
||||||
|
$byId = array_column($rows, null, 'id');
|
||||||
|
|
||||||
|
self::assertSame([], $byId['s0.r0.c0.m1']['group_presets']);
|
||||||
|
self::assertSame(['font-body: [Fixture] Fließtext'], $byId['s0.r0.c0.m2']['group_presets']);
|
||||||
|
self::assertSame(['spacing: [Fixture] Abstand Karte'], $byId['s0.r0.c0.m5']['group_presets']);
|
||||||
|
self::assertSame(['[Fixture] Blurb Leistung'], $byId['s0.r0.c0.m5']['presets']);
|
||||||
|
|
||||||
|
$lines = explode("\n", $outline->text($rows));
|
||||||
|
self::assertStringContainsString(
|
||||||
|
's0.r0.c0.m2 text "Text mit Gruppen-Preset" {Gruppen-Presets: font-body: [Fixture] Fließtext} – ',
|
||||||
|
$lines[5]
|
||||||
|
);
|
||||||
|
self::assertStringContainsString(
|
||||||
|
's0.r0.c0.m5 blurb "Blurb mit Modul- und Gruppen-Preset" {Preset: [Fixture] Blurb Leistung} '
|
||||||
|
. '{Gruppen-Presets: spacing: [Fixture] Abstand Karte} – Möbel nach Maß',
|
||||||
|
$lines[8]
|
||||||
|
);
|
||||||
|
self::assertStringNotContainsString('Gruppen-Presets', $lines[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUnknownGroupPresetFallsBackToTheIdAndStacksAreListed(): void
|
||||||
|
{
|
||||||
|
$block = Block::create('divi/blurb', ['groupPreset' => [
|
||||||
|
'module.decoration.spacing' => ['presetId' => ['a1', 'b2'], 'groupName' => 'divi/spacing'],
|
||||||
|
'title.decoration.font' => ['presetId' => ['c3'], 'groupName' => 'divi/font'],
|
||||||
|
]]);
|
||||||
|
$document = new Document();
|
||||||
|
$document->append($block);
|
||||||
|
$index = NodeIndex::build($document);
|
||||||
|
$outline = new Outline(['a1' => 'Abstand Karte']);
|
||||||
|
|
||||||
|
$rows = $outline->rows($index);
|
||||||
|
|
||||||
|
self::assertSame(['spacing: Abstand Karte, b2', 'font: c3'], $rows[0]['group_presets']);
|
||||||
|
self::assertStringContainsString(
|
||||||
|
'{Gruppen-Presets: spacing: Abstand Karte, b2; font: c3}',
|
||||||
|
$outline->text($rows)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGlobalInstancesShowTheirLibraryIdAndRootType(): void
|
public function testGlobalInstancesShowTheirLibraryIdAndRootType(): void
|
||||||
{
|
{
|
||||||
$content = DiviFixtures::markup('stage3/page--globale-instanzen.html');
|
$content = DiviFixtures::markup('stage3/page--globale-instanzen.html');
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages;
|
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages;
|
||||||
|
|
||||||
|
use LIT\MCP\Integrations\Divi\Design\DesignSystem;
|
||||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
||||||
use LIT\MCP\Integrations\Divi\Library\LibraryAccess;
|
use LIT\MCP\Integrations\Divi\Library\LibraryAccess;
|
||||||
use LIT\MCP\Integrations\Divi\Pages\DiviNodeGetAbility;
|
use LIT\MCP\Integrations\Divi\Pages\DiviNodeGetAbility;
|
||||||
@@ -25,6 +26,56 @@ final class DiviReadAbilitiesTest extends TestCase
|
|||||||
return $reader;
|
return $reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The stage 4 preset inventory (tests/Fixtures/divi/stage4/presets.json) as the design system reads it. */
|
||||||
|
private function designWithGroupPresets(): DesignSystemReader
|
||||||
|
{
|
||||||
|
$stored = json_decode(DiviFixtures::markup('stage4/presets.json'), true)['stored'];
|
||||||
|
$reader = Mockery::mock(DesignSystemReader::class);
|
||||||
|
$reader->allows('read')->andReturn(DesignSystem::fromDivi([], [], $stored, []));
|
||||||
|
|
||||||
|
return $reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOutlineShowsGroupPresetsWithNames(): void
|
||||||
|
{
|
||||||
|
DiviFixtures::givenPost(DiviFixtures::markup('stage4/page--designsystem.html'));
|
||||||
|
$ability = new DiviOutlineAbility(new DiviPageLoader(DiviFixtures::schema()), $this->designWithGroupPresets());
|
||||||
|
|
||||||
|
$result = $ability->execute(['post_id' => 7]);
|
||||||
|
$byId = array_column($result['nodes'], null, 'id');
|
||||||
|
|
||||||
|
self::assertSame([], $byId['s0.r0.c0.m1']['group_presets']);
|
||||||
|
self::assertSame(['[Fixture] Text Hinweis'], $byId['s0.r0.c0.m1']['presets']);
|
||||||
|
self::assertSame(['font-body: [Fixture] Fließtext'], $byId['s0.r0.c0.m2']['group_presets']);
|
||||||
|
self::assertSame(['spacing: [Fixture] Abstand Karte'], $byId['s0.r0.c0.m5']['group_presets']);
|
||||||
|
self::assertStringContainsString(
|
||||||
|
'{Preset: [Fixture] Blurb Leistung} {Gruppen-Presets: spacing: [Fixture] Abstand Karte}',
|
||||||
|
$result['outline']
|
||||||
|
);
|
||||||
|
self::assertContains('group_presets', $ability->outputSchema()['properties']['nodes']['items']['required']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNodeGetListsGroupPresetsWithGroupAndNames(): void
|
||||||
|
{
|
||||||
|
DiviFixtures::givenPost(DiviFixtures::markup('stage4/page--designsystem.html'));
|
||||||
|
$ability = new DiviNodeGetAbility(new DiviPageLoader(DiviFixtures::schema()), $this->designWithGroupPresets());
|
||||||
|
|
||||||
|
$blurb = $ability->execute(['post_id' => 7, 'id' => 's0.r0.c0.m5']);
|
||||||
|
$plain = $ability->execute(['post_id' => 7, 'id' => 's0.r0.c0.m3']);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
[[
|
||||||
|
'group_id' => 'module.decoration.spacing',
|
||||||
|
'group' => 'spacing',
|
||||||
|
'presets' => [['id' => 'apzj7e17ok', 'name' => '[Fixture] Abstand Karte']],
|
||||||
|
]],
|
||||||
|
$blurb['group_presets']
|
||||||
|
);
|
||||||
|
self::assertSame([['id' => 'vdcl6msnuo', 'name' => '[Fixture] Blurb Leistung']], $blurb['presets']);
|
||||||
|
self::assertSame([], $plain['group_presets']);
|
||||||
|
self::assertArrayNotHasKey('groupPreset', $blurb['attrs']);
|
||||||
|
}
|
||||||
|
|
||||||
public function testOutlineListsNodesWithTreeRevAndPostRev(): void
|
public function testOutlineListsNodesWithTreeRevAndPostRev(): void
|
||||||
{
|
{
|
||||||
$content = DiviFixtures::markup('synthetic-basic.html');
|
$content = DiviFixtures::markup('synthetic-basic.html');
|
||||||
|
|||||||
Reference in New Issue
Block a user