diff --git a/src/Icons/src/DependencyInjection/UXIconsExtension.php b/src/Icons/src/DependencyInjection/UXIconsExtension.php index 2dd81007058..1bb61fb61f6 100644 --- a/src/Icons/src/DependencyInjection/UXIconsExtension.php +++ b/src/Icons/src/DependencyInjection/UXIconsExtension.php @@ -12,6 +12,7 @@ namespace Symfony\UX\Icons\DependencyInjection; use Symfony\Component\AssetMapper\Event\PreAssetsCompileEvent; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\FileLocator; @@ -38,10 +39,15 @@ public function getConfigTreeBuilder(): TreeBuilder ->info('The local directory where icons are stored.') ->defaultValue('%kernel.project_dir%/assets/icons') ->end() - ->variableNode('default_icon_attributes') + ->arrayNode('default_icon_attributes') ->info('Default attributes to add to all icons.') ->defaultValue(['fill' => 'currentColor']) ->example(['class' => 'icon']) + ->normalizeKeys(false) + ->useAttributeAsKey('key') + ->scalarPrototype() + ->cannotBeEmpty() + ->end() ->end() ->arrayNode('icon_sets') ->info('Icon sets configuration.') @@ -63,7 +69,9 @@ public function getConfigTreeBuilder(): TreeBuilder ->info('Override default icon attributes for icons in this set.') ->example(['class' => 'icon icon-acme', 'fill' => 'none']) ->normalizeKeys(false) - ->variablePrototype() + ->useAttributeAsKey('key') + ->scalarPrototype() + ->cannotBeEmpty() ->end() ->end() ->end() @@ -80,7 +88,8 @@ public function getConfigTreeBuilder(): TreeBuilder 'privacy' => 'bi:cookie', ]) ->normalizeKeys(false) - ->scalarPrototype() + ->useAttributeAsKey('key') + ->{method_exists(ArrayNodeDefinition::class, 'stringPrototype') ? 'stringPrototype' : 'scalarPrototype'}() ->cannotBeEmpty() ->end() ->end() diff --git a/src/Icons/tests/UXIconsBundleTest.php b/src/Icons/tests/UXIconsBundleTest.php new file mode 100644 index 00000000000..ddf1b10ce13 --- /dev/null +++ b/src/Icons/tests/UXIconsBundleTest.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\Icons\Tests; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Processor; +use Symfony\UX\Icons\DependencyInjection\UXIconsExtension; + +class UXIconsBundleTest extends TestCase +{ + public static function provideTestInvalidAliasConfiguration(): iterable + { + yield ['', 'Invalid type for path "ux_icons.aliases". Expected "array", but got "string"']; + yield [123, 'Invalid type for path "ux_icons.aliases". Expected "array", but got "int']; + yield [false, 'Invalid type for path "ux_icons.aliases". Expected "array", but got "bool"']; + + if (method_exists(ArrayNodeDefinition::class, 'stringPrototype')) { + yield [[1, 2, 3], 'Invalid type for path "ux_icons.aliases.0". Expected "string", but got "int"']; + yield [['foo' => 123], 'Invalid type for path "ux_icons.aliases.foo". Expected "string", but got "int"']; + } + } + + /** + * @dataProvider provideTestInvalidAliasConfiguration + */ + #[DataProvider('provideTestInvalidAliasConfiguration')] + public function testInvalidAliasConfiguration(mixed $value, string $expectedMessage) + { + self::expectException(InvalidConfigurationException::class); + self::expectExceptionMessage($expectedMessage); + + $processor = new Processor(); + $configurableExtension = new UXIconsExtension(); + $processor->processConfiguration($configurableExtension, [ + [ + 'aliases' => $value, + ], + ]); + } + + public static function provideTestValidAliasConfiguration(): iterable + { + yield [[]]; + yield [['foo' => 'bar']]; + } + + /** + * @dataProvider provideTestValidAliasConfiguration + */ + #[DataProvider('provideTestValidAliasConfiguration')] + public function testValidAliasConfiguration(array $value) + { + $processor = new Processor(); + $configurableExtension = new UXIconsExtension(); + $processedConfig = $processor->processConfiguration($configurableExtension, [ + [ + 'aliases' => $value, + ], + ]); + + $this->assertSame($value, $processedConfig['aliases']); + } + + public static function provideTestInvalidIconAttributesConfiguration(): iterable + { + yield ['', 'Invalid type for path "ux_icons.default_icon_attributes". Expected "array", but got "string"']; + yield [123, 'Invalid type for path "ux_icons.default_icon_attributes". Expected "array", but got "int"']; + yield [false, 'Invalid type for path "ux_icons.default_icon_attributes". Expected "array", but got "bool"']; + } + + /** + * @dataProvider provideTestInvalidIconAttributesConfiguration + */ + #[DataProvider('provideTestInvalidIconAttributesConfiguration')] + public function testInvalidIconAttributeConfiguration(mixed $value, string $expectedMessage) + { + self::expectException(InvalidConfigurationException::class); + self::expectExceptionMessage($expectedMessage); + + $processor = new Processor(); + $configurableExtension = new UXIconsExtension(); + $processor->processConfiguration($configurableExtension, [ + [ + 'default_icon_attributes' => $value, + ], + ]); + } + + public static function provideTestValidIconAttributesConfiguration(): iterable + { + yield [[]]; + yield [['class' => 'icon-large', 'aria-hidden' => 'true', 'data-test' => 123]]; + } + + /** + * @dataProvider provideTestValidIconAttributesConfiguration + */ + #[DataProvider('provideTestValidIconAttributesConfiguration')] + public function testValidIconAttributeConfiguration(array $value) + { + $processor = new Processor(); + $configurableExtension = new UXIconsExtension(); + $processedConfig = $processor->processConfiguration($configurableExtension, [ + [ + 'default_icon_attributes' => $value, + ], + ]); + $this->assertSame($value, $processedConfig['default_icon_attributes']); + } +}