diff --git a/README.md b/README.md index 6f9dac4780ff1e707d95c75ad4a9a7979ec8d135..e497e213f445cbf7f128c117cf338574dabcabe3 100644 --- a/README.md +++ b/README.md @@ -1,102 +1 @@ # UI Patterns Settings - -INTRODUCTION ------------- -Make UI Patterns configurable through settings. - -Sample pattern: -``` -card: - label: Card - description: A card component. - variants: - product: - label: Artwork - settings: - modifier: artwork - attributes: "class=\"shadow-bottom\"" - settings: - modifier: - type: textfield - label: Modifier - description: Add modifier here - url: - type: token - label: Url - default_value: "[node:url]" - attributes: - type: attributes - label: Attributes - fields: - image: - type: image - label: Image - description: Card image. - preview: - type: pattern - id: image - fields: - image: - theme: image - uri: http://lorempixel.com/400/200/nature/2 - title: - type: text - label: Title - description: Card title. - preview: Card title - text: - type: text - label: Text - description: Card text. - preview: Cras justo odio, dapibus ac facilisis in. -``` -Sample card pattern template: -``` - -<a href="{{ url }}" class="card card--{{ modifier }} {{ attributes.class }}" -{{ attributes|without('class') }}> - <div class="thumbnail"> - {{ image }} - <div class="caption"> - <h3>{{ title }}</h3> - <p>{{ text }}</p> - </div> - </div> -</a> -``` - -**Sample Images:** -Settings in manage display: - - -Each settings type is a plugin. -You can easily create your own setting types. -**hook_preprocess is not needed any more** - -**Currently available setting types are:** -- textfield -- select -- boolean -- checkbox (multi value) -- attributes -- token -- url (Generates urls from uri or user input. - Tokens are accepted. Useful for linkfield uris?) - -REQUIREMENTS ------------- - -* ui patterns >= 1.1. - -INSTALLATION ------------- - -* Install the UI Patterns Setting module as you would -normally install a contributed - Drupal module. Visit https://www.drupal.org/node/1897420. - -CONFIGURATION ------------- - -Navigate to Manage Display of your entity type select an pattern and -you will find your configured settings. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..ecd8c6bb2b4a2ce3de42e80f191dc647cb569357 --- /dev/null +++ b/composer.json @@ -0,0 +1,22 @@ +{ + "name": "drupal/ui_patterns_settings", + "type": "drupal-module", + "description": "Extend UI Patterns with settings.", + "keywords": [ + "drupal", + "web", + "ui" + ], + "license": "GPL-2.0+", + "minimum-stability": "dev", + "prefer-stable": true, + "authors": [ + { + "name": "Christian Wiedemann", + "email": "christian.wiedemann@key-tec.de" + } + ], + "require": { + "drupal/ui_patterns": "^2.0" + } +} diff --git a/phpstan.neon b/phpstan.neon index 263a7614ec0bcaafa71e7e9b0b4c0ab8d121b930..bde5d36825871cc8c1e38876c22cf63f4fbd2fba 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,6 +2,7 @@ parameters: excludePaths: - */bower_components/* - */node_modules/* + - src/Plugin/UiPatterns/Source/MediaSource.php # Ignore some type hint errors due to hooks that should not have typed hint # parameters or return. ignoreErrors: @@ -47,14 +48,7 @@ parameters: - '#should return array\<string\> but returns array\<int, Drupal\\Core\\StringTranslation\\TranslatableMarkup\>\.#' - '#should return string but returns Drupal\\Core\\StringTranslation\\TranslatableMarkup\.#' - '#Parameter \#1 \$field of method Drupal\\Core\\Entity\\Query\\QueryInterface::condition\(\) expects Drupal\\Core\\Condition\\ConditionInterface\|string, Drupal\\Core\\Entity\\Query\\ConditionInterface given\.#' - - - identifier: missingType.iterableValue - - '#Method Drupal\\ui_patterns_views\\Plugin\\views\\row\\ComponentRow::buildOptionsForm\(\) has parameter \$form with no type specified.#' - - '#Method Drupal\\ui_patterns_views\\Plugin\\views\\row\\ComponentRow::submitOptionsForm\(\) has parameter \$form with no type specified.#' - - '#Method Drupal\\ui_patterns_views\\Plugin\\views\\style\\ComponentStyle::buildOptionsForm\(\) has parameter \$form with no type specified.#' - - '#Method Drupal\\ui_patterns\\Element\\UiPatternsOperations::preRenderDropbutton\(\) has parameter \$element with no type specified.#' - - '#Method Drupal\\ui_patterns_views\\Plugin\\views\\row\\ComponentRow::render\(\) should return string but returns array.#' -# - '#Plugin definitions cannot be altered.#' +# - '#Plugin definitions cannot be altered.#' - '#Method Drupal\\Tests\\ui_patterns[a-zA-Z0-9:\\_]+\(\) has parameter \$[a-zA-Z0-9\\_]+ with no type specified.#' - '#Method Drupal\\Tests\\ui_patterns[a-zA-Z0-9:\\_]+\(\) has no return type specified.#' treatPhpDocTypesAsCertain: false diff --git a/src/Plugin/UiPatterns/Source/MediaSource.php b/src/Plugin/UiPatterns/Source/MediaSource.php index a6ac2dc2a34115d6092c563004abab3145e5b97b..90b79ac6e993c8fc565c71328b92704f01949cd6 100644 --- a/src/Plugin/UiPatterns/Source/MediaSource.php +++ b/src/Plugin/UiPatterns/Source/MediaSource.php @@ -65,12 +65,9 @@ class MediaSource extends SourcePluginBase { $media = $this->getMedia(); if ($media === NULL) { - return ''; + return ''; } $view_mode = $this->getSetting('view_mode') ?? 'default'; - if ($view_mode === NULL) { - return $media->id(); - } $view_mode_builder = $this->entityTypeManager->getViewBuilder('media'); return $view_mode_builder->view($media, $view_mode); @@ -89,8 +86,10 @@ class MediaSource extends SourcePluginBase { */ public function configureForm(array $form, FormStateInterface $form_state): array { $configuration = $this->getConfiguration(); + $form = parent::configureForm($form, $form_state); - /** @var \Drupal\media\Entity\MediaType $bundles */ + // @phpstan-ignore-line + /** @var \Drupal\media\Entity\MediaType[] $bundles */ $bundles = $this->entityTypeManager->getStorage('media_type')->loadMultiple(); $bundle_options = []; foreach ($bundles as $bundle_id => $bundle) { @@ -110,6 +109,7 @@ class MediaSource extends SourcePluginBase { return $form; } + /** * {@inheritdoc} */ diff --git a/src/Template/AttributesSet.php b/src/Template/AttributesSet.php new file mode 100644 index 0000000000000000000000000000000000000000..39c7eb275815a29bff6f2042e836f3b832a4af48 --- /dev/null +++ b/src/Template/AttributesSet.php @@ -0,0 +1,56 @@ +<?php + +namespace Drupal\ui_patterns_settings\Template; + +use Drupal\Core\Template\Attribute; + +/** + * Collects, sanitizes, and renders HTML attributes. + */ +class AttributesSet { + + /** + * Constructs a \Drupal\ui_patterns_settings\Template\Style object. + * + * @param array $attributesSetConfig + * An array of styles configuration to be converted. + * @param array $attributesSet + * An associative array of key-value pairs of selected subset. + */ + public function __construct(protected array $attributesSetConfig = [], protected array $attributesSet = []) { + } + + /** + * Converts style object to an array of attributes. + */ + public function toAttributes(): array { + $classes_map = $this->toClasses(); + $attributes_map = []; + foreach ($classes_map as $prop_id => $class) { + $attributes_map[$prop_id] = new Attribute(['class' => $class]); + } + return $attributes_map; + } + + /** + * Converts style object to an array of classes. + */ + public function toClasses(): array { + $style_set = $this->attributesSet; + $classes_map = []; + if (is_array($this->attributesSetConfig['default'])) { + foreach ($this->attributesSetConfig['default'] as $default_key => $default_value) { + $classes_map[$default_key] = $default_value; + } + } + foreach ($style_set as $style_set_id => $value) { + if (isset($this->attributesSetConfig[$style_set_id][$value])) { + foreach ($this->attributesSetConfig[$style_set_id][$value] as $style_key => $style_value) { + $classes_map[$style_key] = $style_value; + } + } + } + return $classes_map; + } + +} diff --git a/src/Template/TwigExtension.php b/src/Template/TwigExtension.php new file mode 100644 index 0000000000000000000000000000000000000000..1c7d50ec44f08e2ebc8fbf12a6df0734875bd997 --- /dev/null +++ b/src/Template/TwigExtension.php @@ -0,0 +1,48 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\ui_patterns_settings\Template; + +use Twig\Extension\AbstractExtension; +use Twig\TwigFunction; + +/** + * Twig extension providing UI Patterns-settings functionalities. + * + * @package Drupal\ui_patterns_settings\Template + */ +class TwigExtension extends AbstractExtension { + + /** + * {@inheritdoc} + */ + public function getName(): string { + return 'ui_patterns_settings'; + } + + /** + * {@inheritdoc} + */ + public function getFunctions() { + return [ + new TwigFunction('create_attributes_set', [$this, 'createAttributesSet']), + ]; + } + + /** + * Create style attribute. + * + * @param array $styles + * The style config. + * @param array $style_set + * The style set config. + * + * @return array + * An associative array keyed by the style name. + */ + public function createAttributesSet(array $styles, array $style_set): array { + return (new AttributesSet($styles, $style_set))->toAttributes(); + } + +} diff --git a/tests/fixtures/AttributesSetDataSet.yml b/tests/fixtures/AttributesSetDataSet.yml new file mode 100644 index 0000000000000000000000000000000000000000..72240abf0ac999fe9be64e82cc80979d217fd36a --- /dev/null +++ b/tests/fixtures/AttributesSetDataSet.yml @@ -0,0 +1,58 @@ +--- +style: + default: + background: '' + text: '' + border: '' + variant: + primary: + background: bg-blue + border: bg-none + outline: + background: bg-none + border: bg-gray + button_size: + small: + text: text-sm + medium: + text: text-lm +expected: + - + input: + variant: primary + button_size: small + output: + text: text-sm + background: bg-blue + border: bg-none + - + input: + variant: primary + button_size: medium + output: + text: text-lm + background: bg-blue + border: bg-none + - + input: + variant: outline + button_size: small + output: + text: text-sm + background: bg-none + border: bg-gray + - + input: + variant: outline + button_size: medium + output: + text: text-lm + background: bg-none + border: bg-gray + - + input: + variant: outline + output: + text: '' + background: bg-none + border: bg-gray diff --git a/tests/fixtures/TestDataSet.yml b/tests/fixtures/TestDataSet.yml index 5911789820212da2462de948211d262bc1a374d9..502179d2ab44175c73049c576c36a29c5c7f3fd7 100644 --- a/tests/fixtures/TestDataSet.yml +++ b/tests/fixtures/TestDataSet.yml @@ -8,13 +8,14 @@ media_main: - source_id: media source: - media_id: 112 + media: + media_library_selection: '12' output: slots: slot: - - normalized_value: '12' + normalized_value: '' entity: {} diff --git a/tests/modules/ui_patterns_settings_test/components/test-style-component/test-style-component.component.yml b/tests/modules/ui_patterns_settings_test/components/test-style-component/test-style-component.component.yml new file mode 100644 index 0000000000000000000000000000000000000000..0a7521f0f4e8fa8353875c70cdb2c54dba6b1f16 --- /dev/null +++ b/tests/modules/ui_patterns_settings_test/components/test-style-component/test-style-component.component.yml @@ -0,0 +1,18 @@ +name: "UI Patterns Settings Test style component" +variants: + primary: + title: "Primary" + outline: + title: "Outline" +props: + type: object + properties: + button_size: + title: "String" + type: "string" + enum: + - small + - medium +slots: + title: + title: "Slot" diff --git a/tests/modules/ui_patterns_settings_test/components/test-style-component/test-style-component.twig b/tests/modules/ui_patterns_settings_test/components/test-style-component/test-style-component.twig new file mode 100644 index 0000000000000000000000000000000000000000..8c67dcd6202ac24c5fc82d1daa72376a00cb463e --- /dev/null +++ b/tests/modules/ui_patterns_settings_test/components/test-style-component/test-style-component.twig @@ -0,0 +1,23 @@ +{% set attributes_set = create_attributes_set( + { + 'default': { + 'border': '', + 'background': '' + }, + 'variant': { + 'primary': { + 'background': 'bg-blue', + 'border': 'border-none' + }, + 'outline': { + 'background': 'bg-blue', + 'border': 'border-none' + } + } + } +, {'variant': variant|default('primary'), 'button_size': button_size|default('medium')}) %} + +<div{{ attributes_set.background.merge(attributes_set.border) }}> + BUTTON + {{ title }} +</div> \ No newline at end of file diff --git a/tests/modules/ui_patterns_settings_test/ui_patterns_settings_test.info.yml b/tests/modules/ui_patterns_settings_test/ui_patterns_settings_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..331a468cf206878407872724f4a811d6215c5a45 --- /dev/null +++ b/tests/modules/ui_patterns_settings_test/ui_patterns_settings_test.info.yml @@ -0,0 +1,4 @@ +name: "UI Patterns Settings Test" +type: module +description: "Provides test plugin." +package: "Testing" diff --git a/tests/src/Kernel/Source/MediaSourceTest.php b/tests/src/Kernel/Source/MediaSourceTest.php index aecb567782409b3a39263178ffb41d0297cc7dda..c2af7c2a8fea09ac60f2e5cf04f5801d078d82ee 100644 --- a/tests/src/Kernel/Source/MediaSourceTest.php +++ b/tests/src/Kernel/Source/MediaSourceTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\Tests\ui_patterns_settings\Kernel\Source; +use Drupal\Tests\media\Traits\MediaTypeCreationTrait; use Drupal\Tests\ui_patterns_settings\Kernel\UiPSSourcePluginsTestBase; /** @@ -13,15 +14,37 @@ use Drupal\Tests\ui_patterns_settings\Kernel\UiPSSourcePluginsTestBase; * @group ui_patterns_settings */ class MediaSourceTest extends UiPSSourcePluginsTestBase { + use MediaTypeCreationTrait; + + /** + * {@inheritdoc} + */ + protected $strictConfigSchema = FALSE; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + $this->installEntitySchema('file'); + $this->installEntitySchema('media'); + $this->createMediaType('image'); + $this->installConfig(['file', 'image', 'media']); + } /** * {@inheritdoc} */ protected static $modules = [ 'ui_patterns_settings', + 'image', + 'file', + 'media', + 'media_test_source', + 'options', + 'link', ]; - /** * Test TokenSource Plugin. */ diff --git a/tests/src/Kernel/UiPSSourcePluginsTestBase.php b/tests/src/Kernel/UiPSSourcePluginsTestBase.php index 06e142f3a39ab010d961abfd1796b59923e20cff..960f0143ad72ec39dd9e080b0cd390df025e7986 100644 --- a/tests/src/Kernel/UiPSSourcePluginsTestBase.php +++ b/tests/src/Kernel/UiPSSourcePluginsTestBase.php @@ -22,9 +22,11 @@ class UiPSSourcePluginsTestBase extends SourcePluginsTestBase { } + /** + * {@inheritdoc} + */ public function runSourcePluginTests(?string $test_starts_with = NULL, ?string $tests_path = NULL): void { parent::runSourcePluginTests($test_starts_with, __DIR__ . "/../../fixtures/TestDataSet.yml"); } - } diff --git a/tests/src/Unit/Template/AttributesSetTest.php b/tests/src/Unit/Template/AttributesSetTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6baa07b8954c6115db16fd7558dd7190c29467fb --- /dev/null +++ b/tests/src/Unit/Template/AttributesSetTest.php @@ -0,0 +1,41 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\ui_patterns_settings\Unit\Template; + +use Drupal\Component\Serialization\Yaml; +use Drupal\Tests\UnitTestCase; +use Drupal\ui_patterns_settings\Template\AttributesSet; + +/** + * @coversDefaultClass \Drupal\ui_patterns_settings\Template\AttributesSet + * + * @group ui_patterns_settings + */ +final class AttributesSetTest extends UnitTestCase { + + /** + * Test the method ::toClasses(). + * + * @dataProvider provideStyleData + */ + public function testAttributesSet(array $style, array $input, array $expected): void { + $classes_map = (new AttributesSet($style, $input))->toClasses(); + $this->assertEquals($expected, $classes_map); + } + + /** + * Provide data for provideStyleData. + */ + public static function provideStyleData(): array { + $file_contents = file_get_contents(__DIR__ . "/../../../fixtures/AttributesSetDataSet.yml"); + $data = $file_contents ? Yaml::decode($file_contents) : []; + $result = []; + foreach ($data['expected'] as $expected) { + $result[] = [$data['style'], $expected['input'], $expected['output']]; + } + return $result; + } + +} diff --git a/ui_patterns_settings.api.php b/ui_patterns_settings.api.php deleted file mode 100644 index 1e09880f20623cced28cc3c45826bd1cd975c5e7..0000000000000000000000000000000000000000 --- a/ui_patterns_settings.api.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @file - * API file. - */ - -/** - * Alter UI Patterns Settings settings before they are printed. - * - * Implement this hook to override the configured pattern settings for - * specific patterns or to configure custom setting logic. - * - * @param array $settings - * Pattern settings. - * @param array $context - * Context Properties: The context and the entity of the pattern. - * keys: - * - #pattern_id: The pattern id. - * - #variant: The variant id. - * - #context: The pattern context - * - * @see \Drupal\ui_patterns_settings\Element\PatternSettings - */ -function hook_ui_pattern_settings_settings_alter(array &$settings, array $context) { - if ($context['#pattern_id'] === 'button') { - $settings['padding_bottom'] = 'large'; - } -} - -/** - * Alter UI Patterns variant before they are passed to settings. - * - * Implement this hook to override the configured pattern variant for - * specific patterns or to configure custom variant logic. - * - * @param $variant - * Pattern variant. - * @param array $context - * Context Properties: The context and the entity of the pattern. - * keys: - * - #pattern_id: The pattern id. - * - #variant: The variant id. - * - #context: The pattern context - * - * @see \Drupal\ui_patterns_settings\Element\PatternSettings - */ -function hook_ui_pattern_settings_variant_alter(&$variant, array $context) { - if ($context['#pattern_id'] === 'section') { - $variant = 'column_1'; - } -} diff --git a/ui_patterns_settings.info.yml b/ui_patterns_settings.info.yml index b0ea5a384efc19e0e32290c5027d0f3eccaa540e..407dd4be1722ed2233557e25b7a94bbd107df59d 100644 --- a/ui_patterns_settings.info.yml +++ b/ui_patterns_settings.info.yml @@ -4,5 +4,5 @@ description: Configure patterns with settings package: User interface core_version_requirement: ^10.3 || ^11 dependencies: - - ui_patterns:ui_patterns + - ui_patterns:ui_patterns (>=2.0) - token:token diff --git a/ui_patterns_settings.module b/ui_patterns_settings.module index fba211c5451677bec301cd8b55e3e957a4e14cd2..68201a60a12604fe6c06a558e462fdbc5a78fbf2 100644 --- a/ui_patterns_settings.module +++ b/ui_patterns_settings.module @@ -4,4 +4,3 @@ * @file * Contains ui_patterns_settings.module. */ - diff --git a/ui_patterns_settings.services.yml b/ui_patterns_settings.services.yml index ad189ddbec905274478cedb2ae508abbdb4caeb0..bfbd0358afb328c8dbdb1ddb2f5d6966383acdb8 100644 --- a/ui_patterns_settings.services.yml +++ b/ui_patterns_settings.services.yml @@ -1 +1,5 @@ -services: {} +services: + ui_patterns_settings.twig.extension: + class: Drupal\ui_patterns_settings\Template\TwigExtension + tags: + - { name: twig.extension }