diff --git a/CHANGELOG.md b/CHANGELOG.md index 796b512c..7f7339c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## 4.1.1 under development +- New #277: Add `beforeCheckbox()` and `afterCheckbox()` methods to `CheckboxList`, and `beforeRadio()` and + `afterRadio()` methods to `RadioList` (@vjik) - New #276: Add `beforeInput()` and `afterInput()` methods to abstract `BooleanInputTag`, extended by `Radio` and `Checkbox` (@vjik) diff --git a/src/Widget/CheckboxList/CheckboxList.php b/src/Widget/CheckboxList/CheckboxList.php index 87a198f8..262dc6dc 100644 --- a/src/Widget/CheckboxList/CheckboxList.php +++ b/src/Widget/CheckboxList/CheckboxList.php @@ -28,6 +28,8 @@ final class CheckboxList implements NoEncodeStringableInterface private array $checkboxAttributes = []; private array $checkboxLabelAttributes = []; private bool $checkboxLabelWrap = true; + private string|Stringable $beforeCheckbox = ''; + private string|Stringable $afterCheckbox = ''; /** * @var array[] @@ -164,6 +166,26 @@ public function checkboxLabelWrap(bool $wrap): self return $new; } + /** + * @param string|Stringable $content Content to be rendered before each checkbox input. + */ + public function beforeCheckbox(string|Stringable $content): self + { + $new = clone $this; + $new->beforeCheckbox = $content; + return $new; + } + + /** + * @param string|Stringable $content Content to be rendered after each checkbox input. + */ + public function afterCheckbox(string|Stringable $content): self + { + $new = clone $this; + $new->afterCheckbox = $content; + return $new; + } + /** * @param array[] $attributes */ @@ -365,7 +387,9 @@ private function formatItem(CheckboxItem $item): string $checkbox = Html::checkbox($item->name, $item->value, $item->checkboxAttributes) ->checked($item->checked) ->label($item->label, $item->labelAttributes, $item->labelWrap) - ->labelEncode($item->encodeLabel); + ->labelEncode($item->encodeLabel) + ->beforeInput($this->beforeCheckbox) + ->afterInput($this->afterCheckbox); return $checkbox->render(); } diff --git a/src/Widget/RadioList/RadioList.php b/src/Widget/RadioList/RadioList.php index 6d5c6103..e091ab74 100644 --- a/src/Widget/RadioList/RadioList.php +++ b/src/Widget/RadioList/RadioList.php @@ -25,6 +25,8 @@ final class RadioList implements NoEncodeStringableInterface private array $radioAttributes = []; private array $radioLabelAttributes = []; private bool $radioLabelWrap = true; + private string|Stringable $beforeRadio = ''; + private string|Stringable $afterRadio = ''; /** * @var array[] @@ -155,6 +157,26 @@ public function radioLabelWrap(bool $wrap): self return $new; } + /** + * @param string|Stringable $content Content to be rendered before each radio input. + */ + public function beforeRadio(string|Stringable $content): self + { + $new = clone $this; + $new->beforeRadio = $content; + return $new; + } + + /** + * @param string|Stringable $content Content to be rendered after each radio input. + */ + public function afterRadio(string|Stringable $content): self + { + $new = clone $this; + $new->afterRadio = $content; + return $new; + } + /** * @param array[] $attributes */ @@ -344,7 +366,9 @@ private function formatItem(RadioItem $item): string $radio = Html::radio($item->name, $item->value, $item->radioAttributes) ->checked($item->checked) ->label($item->label, $item->labelAttributes, $item->labelWrap) - ->labelEncode($item->encodeLabel); + ->labelEncode($item->encodeLabel) + ->beforeInput($this->beforeRadio) + ->afterInput($this->afterRadio); return $radio->render(); } diff --git a/tests/Widget/CheckboxListTest.php b/tests/Widget/CheckboxListTest.php index 63130f47..be0730d1 100644 --- a/tests/Widget/CheckboxListTest.php +++ b/tests/Widget/CheckboxListTest.php @@ -7,8 +7,10 @@ use ArrayObject; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use Stringable; use Yiisoft\Html\Html; use Yiisoft\Html\Tests\Objects\IterableObject; +use Yiisoft\Html\Tests\Objects\StringableObject; use Yiisoft\Html\Tests\Support\IntegerEnum; use Yiisoft\Html\Tests\Support\StringEnum; use Yiisoft\Html\Widget\CheckboxList\CheckboxList; @@ -194,6 +196,46 @@ public function testCheckboxLabelAttributesMerge(): void ); } + public static function dataBeforeCheckbox(): iterable + { + yield ['*', '*']; + yield ['*', new StringableObject('*')]; + } + + #[DataProvider('dataBeforeCheckbox')] + public function testBeforeCheckbox(string $expected, string|Stringable $content): void + { + $this->assertSame( + '' . "\n" + . '', + (new CheckboxList('test')) + ->items([1 => 'One', 2 => 'Two']) + ->beforeCheckbox($content) + ->withoutContainer() + ->render(), + ); + } + + public static function dataAfterCheckbox(): iterable + { + yield ['!', '!']; + yield ['!', new StringableObject('!')]; + } + + #[DataProvider('dataAfterCheckbox')] + public function testAfterCheckbox(string $expected, string|Stringable $content): void + { + $this->assertSame( + '' . "\n" + . '', + (new CheckboxList('test')) + ->items([1 => 'One', 2 => 'Two']) + ->afterCheckbox($content) + ->withoutContainer() + ->render(), + ); + } + public function testAddIndividualInputAttributes(): void { $this->assertSame( @@ -903,5 +945,7 @@ public function testImmutability(): void $this->assertNotSame($widget, $widget->uncheckValue(null)); $this->assertNotSame($widget, $widget->separator('')); $this->assertNotSame($widget, $widget->itemFormatter(null)); + $this->assertNotSame($widget, $widget->beforeCheckbox('')); + $this->assertNotSame($widget, $widget->afterCheckbox('')); } } diff --git a/tests/Widget/RadioListTest.php b/tests/Widget/RadioListTest.php index 2ddfa938..dee4b313 100644 --- a/tests/Widget/RadioListTest.php +++ b/tests/Widget/RadioListTest.php @@ -6,7 +6,9 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; +use Stringable; use Yiisoft\Html\Html; +use Yiisoft\Html\Tests\Objects\StringableObject; use Yiisoft\Html\Tests\Support\IntegerEnum; use Yiisoft\Html\Tests\Support\StringEnum; use Yiisoft\Html\Widget\RadioList\RadioItem; @@ -832,6 +834,46 @@ public function testStringable(): void ); } + public static function dataBeforeRadio(): iterable + { + yield ['*', '*']; + yield ['*', new StringableObject('*')]; + } + + #[DataProvider('dataBeforeRadio')] + public function testBeforeRadio(string $expected, string|Stringable $content): void + { + $this->assertSame( + '' . "\n" + . '', + (new RadioList('test')) + ->items([1 => 'One', 2 => 'Two']) + ->beforeRadio($content) + ->withoutContainer() + ->render(), + ); + } + + public static function dataAfterRadio(): iterable + { + yield ['!', '!']; + yield ['!', new StringableObject('!')]; + } + + #[DataProvider('dataAfterRadio')] + public function testAfterRadio(string $expected, string|Stringable $content): void + { + $this->assertSame( + '' . "\n" + . '', + (new RadioList('test')) + ->items([1 => 'One', 2 => 'Two']) + ->afterRadio($content) + ->withoutContainer() + ->render(), + ); + } + public function testImmutability(): void { $widget = new RadioList('test'); @@ -859,5 +901,7 @@ public function testImmutability(): void $this->assertNotSame($widget, $widget->uncheckValue(null)); $this->assertNotSame($widget, $widget->separator('')); $this->assertNotSame($widget, $widget->itemFormatter(null)); + $this->assertNotSame($widget, $widget->beforeRadio('')); + $this->assertNotSame($widget, $widget->afterRadio('')); } }