Skip to content

Commit 8cd1cab

Browse files
committed
Support adding menu items without registered styles + add tests for propagating custom styles
1 parent 8f62d13 commit 8cd1cab

File tree

6 files changed

+136
-25
lines changed

6 files changed

+136
-25
lines changed

src/CliMenu.php

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use PhpSchool\Terminal\InputCharacter;
2323
use PhpSchool\Terminal\NonCanonicalReader;
2424
use PhpSchool\Terminal\Terminal;
25+
use function PhpSchool\CliMenu\Util\collect;
2526
use function PhpSchool\CliMenu\Util\each;
2627

2728
/**
@@ -749,22 +750,24 @@ private function guardSingleLine(string $text) : void
749750

750751
public function propagateStyles() : void
751752
{
752-
each(
753-
array_filter($this->items, function (MenuItemInterface $item) {
753+
collect($this->items)
754+
->filter(function (int $k, MenuItemInterface $item) {
755+
return $this->itemStyleLocator->hasStyleForMenuItem($item);
756+
})
757+
->filter(function (int $k, MenuItemInterface $item) {
754758
return !$item->getStyle()->hasChangedFromDefaults();
755-
}),
756-
function (int $index, $item) {
759+
})
760+
->each(function (int $k, $item) {
757761
$item->setStyle(clone $this->getItemStyleForItem($item));
758-
}
759-
);
762+
});
763+
760764

761-
each(
762-
array_filter($this->items, function (MenuItemInterface $item) {
765+
collect($this->items)
766+
->filter(function (int $k, MenuItemInterface $item) {
763767
return $item instanceof PropagatesStyles;
764-
}),
765-
function (int $index, PropagatesStyles $item) {
768+
})
769+
->each(function (int $k, $item) {
766770
$item->propagateStyles($this);
767-
}
768-
);
771+
});
769772
}
770773
}

src/MenuItem/SplitItem.php

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PhpSchool\CliMenu\Style\ItemStyle;
1010
use PhpSchool\CliMenu\Style\Selectable;
1111
use PhpSchool\CliMenu\Util\StringUtil;
12+
use function PhpSchool\CliMenu\Util\collect;
1213
use function PhpSchool\CliMenu\Util\each;
1314
use function PhpSchool\CliMenu\Util\mapWithKeys;
1415
use function PhpSchool\CliMenu\Util\max;
@@ -364,22 +365,23 @@ public function setStyle(DefaultStyle $style): void
364365
*/
365366
public function propagateStyles(CliMenu $parent): void
366367
{
367-
each(
368-
array_filter($this->getItems(), function (MenuItemInterface $item) {
368+
collect($this->items)
369+
->filter(function (int $k, MenuItemInterface $item) use ($parent) {
370+
return $parent->getStyleLocator()->hasStyleForMenuItem($item);
371+
})
372+
->filter(function (int $k, MenuItemInterface $item) {
369373
return !$item->getStyle()->hasChangedFromDefaults();
370-
}),
371-
function ($index, $item) use ($parent) {
374+
})
375+
->each(function (int $k, $item) use ($parent) {
372376
$item->setStyle(clone $parent->getItemStyleForItem($item));
373-
}
374-
);
377+
});
375378

376-
each(
377-
array_filter($this->getItems(), function (MenuItemInterface $item) {
379+
collect($this->items)
380+
->filter(function (int $k, MenuItemInterface $item) {
378381
return $item instanceof PropagatesStyles;
379-
}),
380-
function ($index, PropagatesStyles $item) use ($parent) {
382+
})
383+
->each(function (int $k, $item) use ($parent) {
381384
$item->propagateStyles($parent);
382-
}
383-
);
385+
});
384386
}
385387
}

src/Style/Locator.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ public function setStyle(ItemStyle $itemStyle, string $styleClass) : void
8787
$this->styles[$styleClass] = $itemStyle;
8888
}
8989

90+
public function hasStyleForMenuItem(MenuItemInterface $item) : bool
91+
{
92+
return isset($this->itemStyleMap[get_class($item)]);
93+
}
94+
9095
public function getStyleForMenuItem(MenuItemInterface $item) : ItemStyle
9196
{
9297
if (!isset($this->itemStyleMap[get_class($item)])) {

test/Builder/CliMenuBuilderTest.php

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PhpSchool\CliMenu\MenuItem\SelectableItem;
1414
use PhpSchool\CliMenu\MenuItem\SplitItem;
1515
use PhpSchool\CliMenu\MenuItem\StaticItem;
16+
use PhpSchool\CliMenu\Style\DefaultStyle;
1617
use PhpSchool\Terminal\Terminal;
1718
use PHPUnit\Framework\TestCase;
1819

@@ -956,7 +957,6 @@ public function testModifyingItemExtraForcesExtraToBeDisplayedWhenNoItemsDisplay
956957
self::assertTrue($menu->getStyle()->getDisplaysExtra());
957958
}
958959

959-
960960
private function checkMenuItems(CliMenu $menu, array $expected) : void
961961
{
962962
$this->checkItems($menu->getItems(), $expected);
@@ -987,4 +987,44 @@ private function checkItems(array $actualItems, array $expected) : void
987987
}
988988
}
989989
}
990+
991+
public function testRegisterItemStylePropagatesToSubmenus() : void
992+
{
993+
$myItem = new class extends LineBreakItem {
994+
};
995+
996+
$myStyle = new class extends DefaultStyle {
997+
};
998+
999+
$builder = new CliMenuBuilder;
1000+
$builder->registerItemStyle(get_class($myItem), $myStyle);
1001+
$builder
1002+
->addSubMenu('My SubMenu', function (CliMenuBuilder $b) use ($myItem) {
1003+
$b->disableDefaultItems();
1004+
$b->addMenuItem($myItem);
1005+
})
1006+
->addSplitItem(function (SplitItemBuilder $b) use ($myItem) {
1007+
$b->addSubMenu('My Split SubMenu', function (CliMenuBuilder $b) use ($myItem) {
1008+
$b->addMenuItem($myItem);
1009+
});
1010+
});
1011+
1012+
$menu = $builder->build();
1013+
$subMenuStyleLocator = $menu
1014+
->getItems()[0]
1015+
->getSubMenu()
1016+
->getStyleLocator();
1017+
1018+
self::assertTrue($subMenuStyleLocator->hasStyleForMenuItem($myItem));
1019+
self::assertSame($myStyle, $subMenuStyleLocator->getStyleForMenuItem($myItem));
1020+
1021+
$nestedSubMenuStyleLocator = $menu
1022+
->getItems()[1]
1023+
->getItems()[0]
1024+
->getSubMenu()
1025+
->getStyleLocator();
1026+
1027+
self::assertTrue($nestedSubMenuStyleLocator->hasStyleForMenuItem($myItem));
1028+
self::assertSame($myStyle, $nestedSubMenuStyleLocator->getStyleForMenuItem($myItem));
1029+
}
9901030
}

test/Builder/SplitItemBuilderTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
use PhpSchool\CliMenu\Builder\SplitItemBuilder;
77
use PhpSchool\CliMenu\CliMenu;
88
use PhpSchool\CliMenu\MenuItem\CheckboxItem;
9+
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
910
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
1011
use PhpSchool\CliMenu\MenuItem\RadioItem;
1112
use PhpSchool\CliMenu\MenuItem\SelectableItem;
1213
use PhpSchool\CliMenu\MenuItem\SplitItem;
1314
use PhpSchool\CliMenu\MenuItem\StaticItem;
15+
use PhpSchool\CliMenu\Style\DefaultStyle;
1416
use PHPUnit\Framework\TestCase;
1517

1618
class SplitItemBuilderTest extends TestCase
@@ -191,4 +193,31 @@ private function checkItems(array $actualItems, array $expected) : void
191193
}
192194
}
193195
}
196+
197+
public function testRegisterItemStylePropagatesToSubmenus() : void
198+
{
199+
$myItem = new class extends LineBreakItem {
200+
};
201+
202+
$myStyle = new class extends DefaultStyle {
203+
};
204+
205+
$menu = new CliMenu(null, []);
206+
$builder = new SplitItemBuilder($menu);
207+
$builder->registerItemStyle(get_class($myItem), $myStyle);
208+
$builder->addSubMenu('My SubMenu', function (CliMenuBuilder $b) {
209+
$b->disableDefaultItems();
210+
$b->addItem('My Item', function () {
211+
});
212+
});
213+
214+
$item = $builder->build();
215+
$styleLocator = $item
216+
->getItems()[0]
217+
->getSubMenu()
218+
->getStyleLocator();
219+
220+
self::assertTrue($styleLocator->hasStyleForMenuItem($myItem));
221+
self::assertSame($myStyle, $styleLocator->getStyleForMenuItem($myItem));
222+
}
194223
}

test/Style/LocatorTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,4 +186,36 @@ public function testSetStyle() : void
186186

187187
self::assertSame($new, $locator->getStyle(DefaultStyle::class));
188188
}
189+
190+
public function testHasStyleForMenuItem() : void
191+
{
192+
$locator = new Locator();
193+
194+
$customClass = new class extends LineBreakItem {
195+
};
196+
197+
self::assertTrue($locator->hasStyleForMenuItem(new LineBreakItem()));
198+
self::assertFalse($locator->hasStyleForMenuItem($customClass));
199+
}
200+
201+
public function testRegisterItemStyleThrowsExceptionIfItemAlreadyRegistered() : void
202+
{
203+
self::expectException(InvalidStyle::class);
204+
205+
(new Locator())->registerItemStyle(LineBreakItem::class, new DefaultStyle());
206+
}
207+
208+
public function testRegisterItemStyle() : void
209+
{
210+
$locator = new Locator();
211+
212+
$customClass = new class extends LineBreakItem {
213+
};
214+
215+
self::assertFalse($locator->hasStyleForMenuItem($customClass));
216+
217+
$locator->registerItemStyle(get_class($customClass), new DefaultStyle());
218+
219+
self::assertTrue($locator->hasStyleForMenuItem($customClass));
220+
}
189221
}

0 commit comments

Comments
 (0)