From 0892ccd36a1ce351362bab564395e34e84534d33 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Sun, 16 Feb 2020 09:30:59 +0100 Subject: [PATCH 1/4] Allow registering custom item styles --- examples/basic.php | 30 ++++++++++++++++++++++++++ src/Builder/CliMenuBuilder.php | 32 ++++++++++++++++++++++++++++ src/Builder/SplitItemBuilder.php | 26 ++++++++++++++++++++++ src/CliMenu.php | 5 +++++ src/Style/Exception/InvalidStyle.php | 5 +++++ src/Style/Locator.php | 10 +++++++++ 6 files changed, 108 insertions(+) diff --git a/examples/basic.php b/examples/basic.php index cf59c932..c7a90ef9 100644 --- a/examples/basic.php +++ b/examples/basic.php @@ -1,7 +1,10 @@ getSelectedItem()->getText(); }; +class MyItem extends SelectableItem { + +}; + +class MySelectableStyle extends SelectableStyle { + +} + +$myItem = new MyItem('YO', $itemCallable); +$myItem2 = new MyItem('YO2', $itemCallable); + $menu = (new CliMenuBuilder) + ->registerItemStyle(MyItem::class, new MySelectableStyle()) +// ->modifyStyle(MySelectableStyle::class, function (MySelectableStyle $style) { +// $style->setUnselectedMarker('[#]'); +// }) ->setTitle('Basic CLI Menu') ->addItem('First Item', $itemCallable) ->addItem('Second Item', $itemCallable) ->addItem('Third Item', $itemCallable) ->addLineBreak('-') + ->addMenuItem($myItem) + ->addMenuItem($myItem2) + ->addSubMenu('OPT', function (CliMenuBuilder $b) use ($myItem) { + $b->addMenuItem($myItem); + }) + ->addSplitItem(function (SplitItemBuilder $b) use ($itemCallable, $myItem) { + $b->addItem('FIRST SPLIT', $itemCallable); + $b->addSubMenu('SUB', function (CliMenuBuilder $b) use ($myItem) { + $b->addMenuItem($myItem); + }); + $b->addMenuItem($myItem); + }) ->build(); $menu->open(); diff --git a/src/Builder/CliMenuBuilder.php b/src/Builder/CliMenuBuilder.php index 867f7859..7a68de79 100644 --- a/src/Builder/CliMenuBuilder.php +++ b/src/Builder/CliMenuBuilder.php @@ -18,10 +18,12 @@ use PhpSchool\CliMenu\MenuStyle; use PhpSchool\CliMenu\Style\CheckboxStyle; use PhpSchool\CliMenu\Style\DefaultStyle; +use PhpSchool\CliMenu\Style\ItemStyle; use PhpSchool\CliMenu\Style\RadioStyle; use PhpSchool\CliMenu\Style\SelectableStyle; use PhpSchool\CliMenu\Terminal\TerminalFactory; use PhpSchool\Terminal\Terminal; +use function PhpSchool\CliMenu\Util\each; /** * @author Michael Woodward @@ -80,6 +82,11 @@ class CliMenuBuilder */ private $autoShortcutsRegex = '/\[(.)\]/'; + /** + * @var array + */ + private $extraItemStyles = []; + /** * @var bool */ @@ -187,6 +194,10 @@ public function addSubMenu(string $text, \Closure $callback) : self $builder->enableAutoShortcuts($this->autoShortcutsRegex); } + each($this->extraItemStyles, function (int $i, array $extraItemStyle) use ($builder) { + $builder->registerItemStyle($extraItemStyle['class'], $extraItemStyle['style']); + }); + $callback($builder); $menu = $builder->build(); @@ -293,6 +304,10 @@ public function addSplitItem(\Closure $callback) : self $builder->enableAutoShortcuts($this->autoShortcutsRegex); } + foreach ($this->extraItemStyles as $extraItemStyle) { + $builder->registerItemStyle($extraItemStyle['class'], $extraItemStyle['style']); + } + $callback($builder); $this->menu->addItem($splitItem = $builder->build()); @@ -603,4 +618,21 @@ public function modifyRadioStyle(callable $itemCallable) : self return $this; } + + public function modifyStyle(string $styleClass, callable $itemCallable) : self + { + $itemCallable($this->menu->getItemStyle($styleClass)); + + return $this; + } + + public function registerItemStyle(string $itemClass, ItemStyle $itemStyle) : self + { + $this->menu->getStyleLocator() + ->registerItemStyle($itemClass, $itemStyle); + + $this->extraItemStyles[] = ['class' => $itemClass, 'style' => $itemStyle]; + + return $this; + } } diff --git a/src/Builder/SplitItemBuilder.php b/src/Builder/SplitItemBuilder.php index dfca5189..72b88bd0 100644 --- a/src/Builder/SplitItemBuilder.php +++ b/src/Builder/SplitItemBuilder.php @@ -5,11 +5,14 @@ use PhpSchool\CliMenu\CliMenu; use PhpSchool\CliMenu\MenuItem\CheckboxItem; use PhpSchool\CliMenu\MenuItem\LineBreakItem; +use PhpSchool\CliMenu\MenuItem\MenuItemInterface; use PhpSchool\CliMenu\MenuItem\MenuMenuItem; use PhpSchool\CliMenu\MenuItem\RadioItem; use PhpSchool\CliMenu\MenuItem\SelectableItem; use PhpSchool\CliMenu\MenuItem\SplitItem; use PhpSchool\CliMenu\MenuItem\StaticItem; +use PhpSchool\CliMenu\Style\ItemStyle; +use function \PhpSchool\CliMenu\Util\each; /** * @author Aydin Hassan @@ -42,6 +45,11 @@ class SplitItemBuilder */ private $autoShortcutsRegex = '/\[(.)\]/'; + /** + * @var array + */ + private $extraItemStyles = []; + public function __construct(CliMenu $menu) { $this->menu = $menu; @@ -103,6 +111,10 @@ public function addSubMenu(string $text, \Closure $callback) : self $builder->enableAutoShortcuts($this->autoShortcutsRegex); } + each($this->extraItemStyles, function (int $i, array $extraItemStyle) use ($builder) { + $builder->registerItemStyle($extraItemStyle['class'], $extraItemStyle['style']); + }); + $callback($builder); $menu = $builder->build(); @@ -117,6 +129,13 @@ public function addSubMenu(string $text, \Closure $callback) : self return $this; } + public function addMenuItem(MenuItemInterface $item) : self + { + $this->splitItem->addItem($item); + + return $this; + } + public function setGutter(int $gutter) : self { $this->splitItem->setGutter($gutter); @@ -135,6 +154,13 @@ public function enableAutoShortcuts(string $regex = null) : self return $this; } + public function registerItemStyle(string $itemClass, ItemStyle $itemStyle) : self + { + $this->extraItemStyles[] = ['class' => $itemClass, 'style' => $itemStyle]; + + return $this; + } + public function build() : SplitItem { return $this->splitItem; diff --git a/src/CliMenu.php b/src/CliMenu.php index 1a0bea7a..c99139047 100644 --- a/src/CliMenu.php +++ b/src/CliMenu.php @@ -666,6 +666,11 @@ public function getItemStyleForItem(MenuItemInterface $item) : ItemStyle return $this->itemStyleLocator->getStyleForMenuItem($item); } + public function getStyleLocator() : Locator + { + return $this->itemStyleLocator; + } + public function importStyles(CliMenu $menu) : void { if (!$this->style->hasChangedFromDefaults()) { diff --git a/src/Style/Exception/InvalidStyle.php b/src/Style/Exception/InvalidStyle.php index e5f7c166..fde0a505 100644 --- a/src/Style/Exception/InvalidStyle.php +++ b/src/Style/Exception/InvalidStyle.php @@ -22,4 +22,9 @@ public static function unregisteredItem(string $itemClass) : self { return new self("Menu item: '$itemClass' does not have a registered style class"); } + + public static function itemAlreadyRegistered(string $itemClass) : self + { + return new self("Menu item: '$itemClass' already has a registered style class"); + } } diff --git a/src/Style/Locator.php b/src/Style/Locator.php index e3e82d6b..76f4fec2 100644 --- a/src/Style/Locator.php +++ b/src/Style/Locator.php @@ -97,4 +97,14 @@ public function getStyleForMenuItem(MenuItemInterface $item) : ItemStyle return $this->getStyle($styleClass); } + + public function registerItemStyle(string $itemClass, ItemStyle $itemStyle) : void + { + if (isset($this->itemStyleMap[$itemClass])) { + throw InvalidStyle::itemAlreadyRegistered($itemClass); + } + + $this->itemStyleMap[$itemClass] = get_class($itemStyle); + $this->styles[get_class($itemStyle)] = $itemStyle; + } } From 577eeb84143dc0f4112e9bd977067b7190e35f65 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Sun, 16 Feb 2020 10:34:08 +0100 Subject: [PATCH 2/4] Cleanup + add example --- examples/basic.php | 30 ------------------- examples/custom-item-register.php | 49 +++++++++++++++++++++++++++++++ src/Builder/CliMenuBuilder.php | 4 +-- 3 files changed, 51 insertions(+), 32 deletions(-) create mode 100644 examples/custom-item-register.php diff --git a/examples/basic.php b/examples/basic.php index c7a90ef9..cf59c932 100644 --- a/examples/basic.php +++ b/examples/basic.php @@ -1,10 +1,7 @@ getSelectedItem()->getText(); }; -class MyItem extends SelectableItem { - -}; - -class MySelectableStyle extends SelectableStyle { - -} - -$myItem = new MyItem('YO', $itemCallable); -$myItem2 = new MyItem('YO2', $itemCallable); - $menu = (new CliMenuBuilder) - ->registerItemStyle(MyItem::class, new MySelectableStyle()) -// ->modifyStyle(MySelectableStyle::class, function (MySelectableStyle $style) { -// $style->setUnselectedMarker('[#]'); -// }) ->setTitle('Basic CLI Menu') ->addItem('First Item', $itemCallable) ->addItem('Second Item', $itemCallable) ->addItem('Third Item', $itemCallable) ->addLineBreak('-') - ->addMenuItem($myItem) - ->addMenuItem($myItem2) - ->addSubMenu('OPT', function (CliMenuBuilder $b) use ($myItem) { - $b->addMenuItem($myItem); - }) - ->addSplitItem(function (SplitItemBuilder $b) use ($itemCallable, $myItem) { - $b->addItem('FIRST SPLIT', $itemCallable); - $b->addSubMenu('SUB', function (CliMenuBuilder $b) use ($myItem) { - $b->addMenuItem($myItem); - }); - $b->addMenuItem($myItem); - }) ->build(); $menu->open(); diff --git a/examples/custom-item-register.php b/examples/custom-item-register.php new file mode 100644 index 00000000..38c05bd8 --- /dev/null +++ b/examples/custom-item-register.php @@ -0,0 +1,49 @@ +getSelectedItem()->getText(); +}; + +class MyItem extends SelectableItem { + +}; + +class MySelectableStyle extends SelectableStyle { + +} + +$myItem = new MyItem('MY CUSTOM ITEM 1', $itemCallable); +$myItem2 = new MyItem('MY CUSTOM ITEM 2', $itemCallable); + +$menu = (new CliMenuBuilder) + ->registerItemStyle(MyItem::class, new MySelectableStyle()) + ->modifyStyle(MySelectableStyle::class, function (MySelectableStyle $style) { + $style->setUnselectedMarker('--- '); + $style->setSelectedMarker('*** '); + }) + ->setTitle('Showcasing Custom Items & Styles') + ->addMenuItem($myItem) + ->addMenuItem($myItem2) + ->addLineBreak() + ->addSplitItem(function (SplitItemBuilder $b) use ($itemCallable, $myItem) { + $b->addItem('Split Item', $itemCallable); + $b->addSubMenu('Split Item Submenu', function (CliMenuBuilder $b) use ($myItem) { + $b->addMenuItem($myItem); + }); + $b->addMenuItem($myItem); + }) + ->addLineBreak() + ->addSubMenu('Options', function (CliMenuBuilder $b) use ($myItem) { + $b->addMenuItem($myItem); + }) + ->build(); + +$menu->open(); diff --git a/src/Builder/CliMenuBuilder.php b/src/Builder/CliMenuBuilder.php index 7a68de79..f3943aad 100644 --- a/src/Builder/CliMenuBuilder.php +++ b/src/Builder/CliMenuBuilder.php @@ -304,9 +304,9 @@ public function addSplitItem(\Closure $callback) : self $builder->enableAutoShortcuts($this->autoShortcutsRegex); } - foreach ($this->extraItemStyles as $extraItemStyle) { + each($this->extraItemStyles, function (int $i, array $extraItemStyle) use ($builder) { $builder->registerItemStyle($extraItemStyle['class'], $extraItemStyle['style']); - } + }); $callback($builder); From 8f62d13dcc86ce7ea1635a002eebc13d24e0f666 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Sun, 16 Feb 2020 11:03:40 +0100 Subject: [PATCH 3/4] Array collection --- src/Util/ArrayUtils.php | 12 ++++++++++ src/Util/Collection.php | 45 ++++++++++++++++++++++++++++++++++++ test/Util/ArrayUtilTest.php | 16 +++++++++++++ test/Util/CollectionTest.php | 26 +++++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 src/Util/Collection.php create mode 100644 test/Util/CollectionTest.php diff --git a/src/Util/ArrayUtils.php b/src/Util/ArrayUtils.php index f51cc90c..2e31c05a 100644 --- a/src/Util/ArrayUtils.php +++ b/src/Util/ArrayUtils.php @@ -16,6 +16,13 @@ function mapWithKeys(array $array, callable $callback) : array return $arr; } +function filter(array $array, callable $callback) : array +{ + return array_filter($array, function ($v, $k) use ($callback) { + return $callback($k, $v); + }, ARRAY_FILTER_USE_BOTH); +} + function each(array $array, callable $callback) : void { foreach ($array as $k => $v) { @@ -27,3 +34,8 @@ function max(array $items) : int { return count($items) > 0 ? \max($items) : 0; } + +function collect(array $items) : Collection +{ + return new Collection($items); +} diff --git a/src/Util/Collection.php b/src/Util/Collection.php new file mode 100644 index 00000000..2e854c0d --- /dev/null +++ b/src/Util/Collection.php @@ -0,0 +1,45 @@ +items = $items; + } + + public function map(callable $cb) : self + { + return new self(mapWithKeys($this->items, $cb)); + } + + public function filter(callable $cb) : self + { + return new self(filter($this->items, $cb)); + } + + public function values() : self + { + return new self(array_values($this->items)); + } + + public function each(callable $cb) : self + { + each($this->items, $cb); + + return $this; + } + + public function all() : array + { + return $this->items; + } +} diff --git a/test/Util/ArrayUtilTest.php b/test/Util/ArrayUtilTest.php index 34e349d1..4936f759 100644 --- a/test/Util/ArrayUtilTest.php +++ b/test/Util/ArrayUtilTest.php @@ -6,7 +6,9 @@ use PhpSchool\CliMenu\Util\ArrayUtil; use PHPUnit\Framework\TestCase; +use function PhpSchool\CliMenu\Util\collect; use function PhpSchool\CliMenu\Util\each; +use function PhpSchool\CliMenu\Util\filter; use function PhpSchool\CliMenu\Util\mapWithKeys; use function PhpSchool\CliMenu\Util\max; @@ -55,4 +57,18 @@ public function testMax() : void self::assertEquals(3, max([1, 2, 3])); self::assertEquals(6, max([1, 6, 3])); } + + public function testFilter() : void + { + $cb = function (int $k, int $v) { + return $v > 3; + }; + + self::assertEquals([3 => 4, 4 => 5, 5 => 6], filter([1, 2, 3, 4, 5, 6], $cb)); + } + + public function testCollect() : void + { + self::assertEquals([1, 2, 3], collect([1, 2, 3])->all()); + } } diff --git a/test/Util/CollectionTest.php b/test/Util/CollectionTest.php new file mode 100644 index 00000000..66e69e50 --- /dev/null +++ b/test/Util/CollectionTest.php @@ -0,0 +1,26 @@ +filter(function ($k, $v) { + return $v > 3; + }) + ->values() + ->map(function ($k, $v) { + return $v * 2; + }) + ->all(); + + self::assertEquals([8, 10, 12], $result); + } +} From 8cd1cab2cff507690d1f2a0f5a2c499fb0346f03 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Sun, 16 Feb 2020 12:17:30 +0100 Subject: [PATCH 4/4] Support adding menu items without registered styles + add tests for propagating custom styles --- src/CliMenu.php | 27 +++++++++-------- src/MenuItem/SplitItem.php | 26 +++++++++-------- src/Style/Locator.php | 5 ++++ test/Builder/CliMenuBuilderTest.php | 42 ++++++++++++++++++++++++++- test/Builder/SplitItemBuilderTest.php | 29 ++++++++++++++++++ test/Style/LocatorTest.php | 32 ++++++++++++++++++++ 6 files changed, 136 insertions(+), 25 deletions(-) diff --git a/src/CliMenu.php b/src/CliMenu.php index c99139047..daecb1bf 100644 --- a/src/CliMenu.php +++ b/src/CliMenu.php @@ -22,6 +22,7 @@ use PhpSchool\Terminal\InputCharacter; use PhpSchool\Terminal\NonCanonicalReader; use PhpSchool\Terminal\Terminal; +use function PhpSchool\CliMenu\Util\collect; use function PhpSchool\CliMenu\Util\each; /** @@ -749,22 +750,24 @@ private function guardSingleLine(string $text) : void public function propagateStyles() : void { - each( - array_filter($this->items, function (MenuItemInterface $item) { + collect($this->items) + ->filter(function (int $k, MenuItemInterface $item) { + return $this->itemStyleLocator->hasStyleForMenuItem($item); + }) + ->filter(function (int $k, MenuItemInterface $item) { return !$item->getStyle()->hasChangedFromDefaults(); - }), - function (int $index, $item) { + }) + ->each(function (int $k, $item) { $item->setStyle(clone $this->getItemStyleForItem($item)); - } - ); + }); + - each( - array_filter($this->items, function (MenuItemInterface $item) { + collect($this->items) + ->filter(function (int $k, MenuItemInterface $item) { return $item instanceof PropagatesStyles; - }), - function (int $index, PropagatesStyles $item) { + }) + ->each(function (int $k, $item) { $item->propagateStyles($this); - } - ); + }); } } diff --git a/src/MenuItem/SplitItem.php b/src/MenuItem/SplitItem.php index 001d15a1..f727d86b 100644 --- a/src/MenuItem/SplitItem.php +++ b/src/MenuItem/SplitItem.php @@ -9,6 +9,7 @@ use PhpSchool\CliMenu\Style\ItemStyle; use PhpSchool\CliMenu\Style\Selectable; use PhpSchool\CliMenu\Util\StringUtil; +use function PhpSchool\CliMenu\Util\collect; use function PhpSchool\CliMenu\Util\each; use function PhpSchool\CliMenu\Util\mapWithKeys; use function PhpSchool\CliMenu\Util\max; @@ -364,22 +365,23 @@ public function setStyle(DefaultStyle $style): void */ public function propagateStyles(CliMenu $parent): void { - each( - array_filter($this->getItems(), function (MenuItemInterface $item) { + collect($this->items) + ->filter(function (int $k, MenuItemInterface $item) use ($parent) { + return $parent->getStyleLocator()->hasStyleForMenuItem($item); + }) + ->filter(function (int $k, MenuItemInterface $item) { return !$item->getStyle()->hasChangedFromDefaults(); - }), - function ($index, $item) use ($parent) { + }) + ->each(function (int $k, $item) use ($parent) { $item->setStyle(clone $parent->getItemStyleForItem($item)); - } - ); + }); - each( - array_filter($this->getItems(), function (MenuItemInterface $item) { + collect($this->items) + ->filter(function (int $k, MenuItemInterface $item) { return $item instanceof PropagatesStyles; - }), - function ($index, PropagatesStyles $item) use ($parent) { + }) + ->each(function (int $k, $item) use ($parent) { $item->propagateStyles($parent); - } - ); + }); } } diff --git a/src/Style/Locator.php b/src/Style/Locator.php index 76f4fec2..47ffa791 100644 --- a/src/Style/Locator.php +++ b/src/Style/Locator.php @@ -87,6 +87,11 @@ public function setStyle(ItemStyle $itemStyle, string $styleClass) : void $this->styles[$styleClass] = $itemStyle; } + public function hasStyleForMenuItem(MenuItemInterface $item) : bool + { + return isset($this->itemStyleMap[get_class($item)]); + } + public function getStyleForMenuItem(MenuItemInterface $item) : ItemStyle { if (!isset($this->itemStyleMap[get_class($item)])) { diff --git a/test/Builder/CliMenuBuilderTest.php b/test/Builder/CliMenuBuilderTest.php index 2891215b..c3e0eb3d 100644 --- a/test/Builder/CliMenuBuilderTest.php +++ b/test/Builder/CliMenuBuilderTest.php @@ -13,6 +13,7 @@ use PhpSchool\CliMenu\MenuItem\SelectableItem; use PhpSchool\CliMenu\MenuItem\SplitItem; use PhpSchool\CliMenu\MenuItem\StaticItem; +use PhpSchool\CliMenu\Style\DefaultStyle; use PhpSchool\Terminal\Terminal; use PHPUnit\Framework\TestCase; @@ -956,7 +957,6 @@ public function testModifyingItemExtraForcesExtraToBeDisplayedWhenNoItemsDisplay self::assertTrue($menu->getStyle()->getDisplaysExtra()); } - private function checkMenuItems(CliMenu $menu, array $expected) : void { $this->checkItems($menu->getItems(), $expected); @@ -987,4 +987,44 @@ private function checkItems(array $actualItems, array $expected) : void } } } + + public function testRegisterItemStylePropagatesToSubmenus() : void + { + $myItem = new class extends LineBreakItem { + }; + + $myStyle = new class extends DefaultStyle { + }; + + $builder = new CliMenuBuilder; + $builder->registerItemStyle(get_class($myItem), $myStyle); + $builder + ->addSubMenu('My SubMenu', function (CliMenuBuilder $b) use ($myItem) { + $b->disableDefaultItems(); + $b->addMenuItem($myItem); + }) + ->addSplitItem(function (SplitItemBuilder $b) use ($myItem) { + $b->addSubMenu('My Split SubMenu', function (CliMenuBuilder $b) use ($myItem) { + $b->addMenuItem($myItem); + }); + }); + + $menu = $builder->build(); + $subMenuStyleLocator = $menu + ->getItems()[0] + ->getSubMenu() + ->getStyleLocator(); + + self::assertTrue($subMenuStyleLocator->hasStyleForMenuItem($myItem)); + self::assertSame($myStyle, $subMenuStyleLocator->getStyleForMenuItem($myItem)); + + $nestedSubMenuStyleLocator = $menu + ->getItems()[1] + ->getItems()[0] + ->getSubMenu() + ->getStyleLocator(); + + self::assertTrue($nestedSubMenuStyleLocator->hasStyleForMenuItem($myItem)); + self::assertSame($myStyle, $nestedSubMenuStyleLocator->getStyleForMenuItem($myItem)); + } } diff --git a/test/Builder/SplitItemBuilderTest.php b/test/Builder/SplitItemBuilderTest.php index a57a5027..9601b484 100644 --- a/test/Builder/SplitItemBuilderTest.php +++ b/test/Builder/SplitItemBuilderTest.php @@ -6,11 +6,13 @@ use PhpSchool\CliMenu\Builder\SplitItemBuilder; use PhpSchool\CliMenu\CliMenu; use PhpSchool\CliMenu\MenuItem\CheckboxItem; +use PhpSchool\CliMenu\MenuItem\LineBreakItem; use PhpSchool\CliMenu\MenuItem\MenuMenuItem; use PhpSchool\CliMenu\MenuItem\RadioItem; use PhpSchool\CliMenu\MenuItem\SelectableItem; use PhpSchool\CliMenu\MenuItem\SplitItem; use PhpSchool\CliMenu\MenuItem\StaticItem; +use PhpSchool\CliMenu\Style\DefaultStyle; use PHPUnit\Framework\TestCase; class SplitItemBuilderTest extends TestCase @@ -191,4 +193,31 @@ private function checkItems(array $actualItems, array $expected) : void } } } + + public function testRegisterItemStylePropagatesToSubmenus() : void + { + $myItem = new class extends LineBreakItem { + }; + + $myStyle = new class extends DefaultStyle { + }; + + $menu = new CliMenu(null, []); + $builder = new SplitItemBuilder($menu); + $builder->registerItemStyle(get_class($myItem), $myStyle); + $builder->addSubMenu('My SubMenu', function (CliMenuBuilder $b) { + $b->disableDefaultItems(); + $b->addItem('My Item', function () { + }); + }); + + $item = $builder->build(); + $styleLocator = $item + ->getItems()[0] + ->getSubMenu() + ->getStyleLocator(); + + self::assertTrue($styleLocator->hasStyleForMenuItem($myItem)); + self::assertSame($myStyle, $styleLocator->getStyleForMenuItem($myItem)); + } } diff --git a/test/Style/LocatorTest.php b/test/Style/LocatorTest.php index 8d4d957a..5d79e312 100644 --- a/test/Style/LocatorTest.php +++ b/test/Style/LocatorTest.php @@ -186,4 +186,36 @@ public function testSetStyle() : void self::assertSame($new, $locator->getStyle(DefaultStyle::class)); } + + public function testHasStyleForMenuItem() : void + { + $locator = new Locator(); + + $customClass = new class extends LineBreakItem { + }; + + self::assertTrue($locator->hasStyleForMenuItem(new LineBreakItem())); + self::assertFalse($locator->hasStyleForMenuItem($customClass)); + } + + public function testRegisterItemStyleThrowsExceptionIfItemAlreadyRegistered() : void + { + self::expectException(InvalidStyle::class); + + (new Locator())->registerItemStyle(LineBreakItem::class, new DefaultStyle()); + } + + public function testRegisterItemStyle() : void + { + $locator = new Locator(); + + $customClass = new class extends LineBreakItem { + }; + + self::assertFalse($locator->hasStyleForMenuItem($customClass)); + + $locator->registerItemStyle(get_class($customClass), new DefaultStyle()); + + self::assertTrue($locator->hasStyleForMenuItem($customClass)); + } }