diff --git a/src/Twig/Functions/IconExtension.php b/src/Twig/Functions/IconExtension.php index cca3498..154a819 100644 --- a/src/Twig/Functions/IconExtension.php +++ b/src/Twig/Functions/IconExtension.php @@ -12,11 +12,12 @@ final class IconExtension extends AbstractExtension public const DEFAULT_SIZE = 32; private const DEFAULT_OPTIONS = [ - 'icon' => null, - 'title' => null, - 'size' => self::DEFAULT_SIZE, - 'colour' => 'primary', - 'hover' => null + 'icon' => null, + 'title' => null, + 'size' => self::DEFAULT_SIZE, + 'colour' => 'primary', + 'hover' => null, + 'classes' => [], ]; public function __construct(private array $directories, private array $palletes) @@ -33,10 +34,10 @@ final class IconExtension extends AbstractExtension if (empty($this->palletes)) throw new \InvalidArgumentException('Palletes array must contain at least one pallet!'); - $pelletesContainNonarray = array_reduce($this->palletes, + $palletesContainNonarray = array_reduce($this->palletes, fn($notArray, $path) => $notArray || !is_array($path)); - if ($pelletesContainNonarray) + if ($palletesContainNonarray) throw new \TypeError('Palletes array must only contain arrays!'); foreach ($this->palletes as $pallete) { @@ -69,11 +70,14 @@ final class IconExtension extends AbstractExtension * @param array $options * ``` * $options = [ - * 'icon' => (string) Which icon to use - * 'title' => (?string) Text to appear on mouse hover - * 'size' => (int) Height and width in px - * 'colour' => (string) Main colour pallete - * 'hover' => (?string) Hover colour pallete + * 'icon' => (string) REQUIRED Which icon to use + * 'title' => (?string) Text to appear on mouse hover + * 'size' => (int) Height and width in px + * 'colour' => (string) Main colour pallete + * 'hover' => (?string) Hover colour pallete + * 'classes' => (array) Additional classes to add to the icon. + * Use with caution as this can potentially + * cause Tailwind class conflicts! * ] * ``` */ @@ -81,14 +85,22 @@ final class IconExtension extends AbstractExtension { $options = $this->getMergedOptions($userOptions); $svg = $this->getSanitisedIconSvg($options['icon']); + $colourClasses = $this->getColourClasses($options['colour'], $options['hover']); - $svg = $this->addClassesToSvg($svg, $colourClasses); + $extraClasses = $this->getExtraClasses($options['classes']); + + $svg = $this->addClassesToSvg($svg, trim($colourClasses.' '.$extraClasses)); $svg = $this->addTitleToSvgIfNotNull($svg, $options['title']); $svg = $this->setSvgHeightAndWidth($svg, $options['size']); return $svg; } + private function getExtraClasses(array $extraClasses): string + { + return implode(' ', $extraClasses); + } + private function getMergedOptions(array $userOptions): array { $this->throwIfUnrecognisedOptionExists($userOptions); diff --git a/tests/Twig/Functions/IconExtensionTest.php b/tests/Twig/Functions/IconExtensionTest.php index 37d785a..9e571d6 100644 --- a/tests/Twig/Functions/IconExtensionTest.php +++ b/tests/Twig/Functions/IconExtensionTest.php @@ -300,6 +300,26 @@ class IconExtensionTest extends TestCase $this->assertMatchesRegularExpression('//', $contents); } + public function testExtraClassesThrowsIfNotAnArray(): void + { + $this->expectException(\TypeError::class); + $this->icon->renderIcon(['icon' => self::ICON, 'classes' => 'string_value']); + } + + public function testExtraClassesGetAdded(): void + { + $contents = $this->icon->renderIcon(['icon' => self::ICON, 'classes' => ['abc', 'def']]); + $this->assertMatchesRegularExpression('//', $contents); + $this->assertMatchesRegularExpression('//', $contents); + } + + public function testAddingExtraClassesDoesntStripAwayColourClasses(): void + { + $contents = $this->icon->renderIcon(['icon' => self::ICON, 'classes' => ['abc']]); + $this->assertMatchesRegularExpression('//', $contents); + $this->assertMatchesRegularExpression('//', $contents); + } + public function testThrowsIfInvalidOptionPassed(): void { $this->expectException(\InvalidArgumentException::class);