diff --git a/composer.json b/composer.json index 7dc6f09c..1a293a60 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "ipl/stdlib": ">=0.12.0", "ipl/validator": ">=0.5.0", "psr/http-message": "^1.1", - "guzzlehttp/psr7": "^2.5" + "guzzlehttp/psr7": "^2.8" }, "require-dev": { "ext-dom": "*", diff --git a/src/Attributes.php b/src/Attributes.php index ed7e6684..90444281 100644 --- a/src/Attributes.php +++ b/src/Attributes.php @@ -38,9 +38,9 @@ class Attributes implements ArrayAccess, IteratorAggregate /** * Create new HTML attributes * - * @param AttributesType $attributes + * @param ?AttributesType $attributes */ - public function __construct(array $attributes = null) + public function __construct(?array $attributes = null) { if (empty($attributes)) { return; @@ -60,11 +60,11 @@ public function __construct(array $attributes = null) /** * Create new HTML attributes * - * @param AttributesType $attributes + * @param ?AttributesType $attributes * * @return static */ - public static function create(array $attributes = null) + public static function create(?array $attributes = null) { return new static($attributes); } diff --git a/src/FormElement/BaseFormElement.php b/src/FormElement/BaseFormElement.php index 13082c43..5b3ff7dd 100644 --- a/src/FormElement/BaseFormElement.php +++ b/src/FormElement/BaseFormElement.php @@ -408,7 +408,6 @@ protected function getValueOfNameAttribute() $attributes = $this->getAttributes(); $callbacksProperty = new ReflectionProperty(get_class($attributes), 'callbacks'); - $callbacksProperty->setAccessible(true); $callbacks = $callbacksProperty->getValue($attributes); if (isset($callbacks['name'])) { diff --git a/src/FormElement/RadioElement.php b/src/FormElement/RadioElement.php index 39ce438d..b3edf3ae 100644 --- a/src/FormElement/RadioElement.php +++ b/src/FormElement/RadioElement.php @@ -66,7 +66,7 @@ public function setOptions(array $options): self * * @throws InvalidArgumentException If no option with the specified value exists */ - public function getOption($value): RadioOption + public function getOption(string|int $value): RadioOption { if (! isset($this->options[$value])) { throw new InvalidArgumentException(sprintf('There is no such option "%s"', $value)); @@ -127,10 +127,15 @@ protected function assemble() 'checked', function () use ($option) { $optionValue = $option->getValue(); + $value = $this->getValue(); + + if ($optionValue === '' && $value === null) { + return true; + } return ! is_int($optionValue) - ? $this->getValue() === $optionValue - : $this->getValue() == $optionValue; + ? $value === $optionValue + : $value == $optionValue; } ) ->registerAttributeCallback( diff --git a/src/FormElement/RadioOption.php b/src/FormElement/RadioOption.php index 4968c35a..316dbb4e 100644 --- a/src/FormElement/RadioOption.php +++ b/src/FormElement/RadioOption.php @@ -9,7 +9,7 @@ class RadioOption /** @var string The default label class */ public const LABEL_CLASS = 'radio-label'; - /** @var string|int|null Value of the option */ + /** @var string|int Value of the option */ protected $value; /** @var string Label of the option */ @@ -27,12 +27,12 @@ class RadioOption /** * RadioOption constructor. * - * @param string|int|null $value + * @param string|int $value * @param string $label */ - public function __construct($value, string $label) + public function __construct(string|int $value, string $label) { - $this->value = $value === '' ? null : $value; + $this->value = $value; $this->label = $label; } @@ -63,9 +63,9 @@ public function getLabel(): string /** * Get the value of the option * - * @return string|int|null + * @return string|int */ - public function getValue() + public function getValue(): string|int { return $this->value; } diff --git a/src/FormElement/SelectElement.php b/src/FormElement/SelectElement.php index cef7a8ed..588000f8 100644 --- a/src/FormElement/SelectElement.php +++ b/src/FormElement/SelectElement.php @@ -2,7 +2,6 @@ namespace ipl\Html\FormElement; -use InvalidArgumentException; use ipl\Html\Attributes; use ipl\Html\Common\MultipleAttribute; use ipl\Html\Html; @@ -32,11 +31,11 @@ class SelectElement extends BaseFormElement /** * Get the option with specified value * - * @param string|int|null $value + * @param string|int $value * * @return ?SelectOption */ - public function getOption($value): ?SelectOption + public function getOption(string|int $value): ?SelectOption { return $this->options[$value] ?? null; } @@ -75,7 +74,7 @@ public function setDisabledOptions(array $disabledOptions): self $option->setAttribute( 'disabled', in_array($optionValue, $disabledOptions, ! is_int($optionValue)) - || ($optionValue === null && in_array('', $disabledOptions, true)) + || ($optionValue === '' && in_array(null, $disabledOptions, true)) ); } @@ -119,12 +118,12 @@ public function getNameAttribute() /** * Make the selectOption for the specified value and the label * - * @param string|int|null $value Value of the option + * @param string|int $value Value of the option * @param string|array $label Label of the option * * @return SelectOption|HtmlElement */ - protected function makeOption($value, $label) + protected function makeOption(string|int $value, string|array $label) { if (is_array($label)) { $grp = Html::tag('optgroup', ['label' => $value]); @@ -150,18 +149,14 @@ protected function makeOption($value, $label) /** * Get whether the given option is selected * - * @param int|string|null $optionValue + * @param string|int $optionValue * * @return bool */ - protected function isSelectedOption($optionValue): bool + protected function isSelectedOption(string|int $optionValue): bool { $value = $this->getValue(); - if ($optionValue === '') { - $optionValue = null; - } - if ($this->isMultiple()) { if (! is_array($value)) { throw new UnexpectedValueException( @@ -169,8 +164,8 @@ protected function isSelectedOption($optionValue): bool ); } - return in_array($optionValue, $this->getValue(), ! is_int($optionValue)) - || ($optionValue === null && in_array('', $this->getValue(), true)); + return in_array($optionValue, $value, ! is_int($optionValue)) + || ($optionValue === '' && in_array(null, $value, true)); } if (is_array($value)) { @@ -179,6 +174,10 @@ protected function isSelectedOption($optionValue): bool ); } + if ($optionValue === '' && $value === null) { + return true; + } + return is_int($optionValue) // The loose comparison is required because PHP casts // numeric strings to integers if used as array keys diff --git a/src/FormElement/SelectOption.php b/src/FormElement/SelectOption.php index 3d799a2c..b281a2ca 100644 --- a/src/FormElement/SelectOption.php +++ b/src/FormElement/SelectOption.php @@ -8,7 +8,7 @@ class SelectOption extends BaseHtmlElement { protected $tag = 'option'; - /** @var string|int|null Value of the option */ + /** @var string|int Value of the option */ protected $value; /** @var string Label of the option */ @@ -17,12 +17,12 @@ class SelectOption extends BaseHtmlElement /** * SelectOption constructor. * - * @param string|int|null $value + * @param string|int $value * @param string $label */ - public function __construct($value, string $label) + public function __construct(string|int $value, string $label) { - $this->value = $value === '' ? null : $value; + $this->value = $value; $this->label = $label; $this->getAttributes()->registerAttributeCallback('value', [$this, 'getValueAttribute']); @@ -55,7 +55,7 @@ public function getLabel(): string /** * Get the value of the option * - * @return string|int|null + * @return string|int */ public function getValue() { diff --git a/src/HtmlElement.php b/src/HtmlElement.php index 4f5d1628..aa5f4d11 100644 --- a/src/HtmlElement.php +++ b/src/HtmlElement.php @@ -13,10 +13,10 @@ class HtmlElement extends BaseHtmlElement * Create a new HTML element from the given tag, attributes and content * * @param string $tag The tag for the element - * @param Attributes $attributes The HTML attributes for the element + * @param ?Attributes $attributes The HTML attributes for the element * @param ValidHtml ...$content The content of the element */ - public function __construct($tag, Attributes $attributes = null, ValidHtml ...$content) + public function __construct($tag, ?Attributes $attributes = null, ValidHtml ...$content) { $this->tag = $tag; diff --git a/tests/DocumentationFormsTest.php b/tests/DocumentationFormsTest.php index 3f85299c..371bbbab 100644 --- a/tests/DocumentationFormsTest.php +++ b/tests/DocumentationFormsTest.php @@ -25,7 +25,7 @@ public function testSelectElement() $form->addElement('select', 'customer', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -57,7 +57,7 @@ public function testSetValues() $form->addElement('select', 'customer', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', diff --git a/tests/FormElement/FieldsetElementTest.php b/tests/FormElement/FieldsetElementTest.php index 44f538e8..1b33c155 100644 --- a/tests/FormElement/FieldsetElementTest.php +++ b/tests/FormElement/FieldsetElementTest.php @@ -15,7 +15,7 @@ class FieldsetElementTest extends TestCase { private const SELECT_OPTIONS_TO_TEST = [ - null => 'Nothing', + '' => 'Nothing', 1 => 'One', 'two' => 2, ]; diff --git a/tests/FormElement/RadioElementTest.php b/tests/FormElement/RadioElementTest.php index d9367b9d..5d5c90dc 100644 --- a/tests/FormElement/RadioElementTest.php +++ b/tests/FormElement/RadioElementTest.php @@ -282,7 +282,7 @@ public function testRadioNotValidIfCheckedValueIsDisabled() $this->assertFalse($radio->isValid()); } - public function testNullAndTheEmptyStringAreEquallyHandled() + public function testNullAndTheEmptyStringValuesAreEquallyHandled() { $form = new Form(); $form->addElement('radio', 'radio', [ @@ -290,7 +290,7 @@ public function testNullAndTheEmptyStringAreEquallyHandled() 'value' => '' ]); $form->addElement('radio', 'radio2', [ - 'options' => [null => 'Please choose'], + 'options' => ['' => 'Please choose'], 'value' => null ]); @@ -303,8 +303,6 @@ public function testNullAndTheEmptyStringAreEquallyHandled() $this->assertNull($radio2->getValue()); $this->assertInstanceOf(RadioOption::class, $radio->getOption('')); - $this->assertInstanceOf(RadioOption::class, $radio2->getOption(null)); - $this->assertInstanceOf(RadioOption::class, $radio->getOption(null)); $this->assertInstanceOf(RadioOption::class, $radio2->getOption('')); $this->assertTrue($radio->isValid()); @@ -341,6 +339,12 @@ public function testNullAndTheEmptyStringAreEquallyHandled() HTML; $this->assertHtml($html, $radio2); + + $radio->setValue(null); + $this->assertNull($radio->getValue()); + + $radio2->setValue(''); + $this->assertNull($radio2->getValue()); } public function testSetOptionsResetsOptions() @@ -395,7 +399,7 @@ public function testGetOptionReturnsPreviouslySetOption() $radio = new RadioElement('radio'); $radio->setOptions(['' => 'Empty String', 'foo' => 'Foo', 'bar' => 'Bar']); - $this->assertNull($radio->getOption('')->getValue()); + $this->assertSame('', $radio->getOption('')->getValue()); $this->assertSame('Empty String', $radio->getOption('')->getLabel()); $this->assertSame('foo', $radio->getOption('foo')->getValue()); @@ -403,55 +407,28 @@ public function testGetOptionReturnsPreviouslySetOption() $radio->setOptions(['' => 'Please Choose', 'car' => 'Car', 'train' => 'Train']); - $this->assertNull($radio->getOption('')->getValue()); + $this->assertSame('', $radio->getOption('')->getValue()); $this->assertSame('Please Choose', $radio->getOption('')->getLabel()); $this->assertSame('car', $radio->getOption('car')->getValue()); $this->assertSame('Car', $radio->getOption('car')->getLabel()); } - public function testNullAndTheEmptyStringAreAlsoEquallyHandledWhileDisablingOptions() + public function testNullAndTheEmptyStringAreEquallyHandledWhileDisablingOptions() { - $radio = new RadioElement('radio'); - $radio->setOptions([null => 'Foo', 'bar' => 'Bar']); - $radio->setDisabledOptions([null]); - - $this->assertTrue($radio->getOption(null)->isDisabled()); - $radio = new RadioElement('radio'); $radio->setOptions(['' => 'Foo', 'bar' => 'Bar']); - $radio->setDisabledOptions(['']); + $radio->setDisabledOptions([null]); $this->assertTrue($radio->getOption('')->isDisabled()); - $radio = new RadioElement('radio'); - $radio->setOptions([null => 'Foo', 'bar' => 'Bar']); - $radio->setDisabledOptions(['']); - - $this->assertTrue($radio->getOption(null)->isDisabled()); $radio = new RadioElement('radio'); $radio->setOptions(['' => 'Foo', 'bar' => 'Bar']); - $radio->setDisabledOptions([null]); + $radio->setDisabledOptions(['']); $this->assertTrue($radio->getOption('')->isDisabled()); } - public function testGetOptionGetValueAndElementGetValueHandleNullAndTheEmptyStringEqually() - { - $radio = new RadioElement('radio'); - $radio->setOptions(['' => 'Foo']); - $radio->setValue(''); - - $this->assertNull($radio->getValue()); - $this->assertNull($radio->getOption('')->getValue()); - - $radio = new RadioElement('radio'); - $radio->setOptions([null => 'Foo']); - - $this->assertNull($radio->getValue()); - $this->assertNull($radio->getOption(null)->getValue()); - } - public function testIsDecoratedWithLabelAndDescription(): void { $radio = new RadioElement('radio', ['id' => 'radio']); @@ -470,4 +447,111 @@ public function testIsDecoratedWithLabelAndDescription(): void $radio->applyDecoration(); $this->assertHtml($html, $radio); } + + public function testSetDisabledOptionsResetsDisabledOptions() + { + $radio = new RadioElement('test', [ + 'options' => [ + '1' => 'Foo', + 2 => 'Bar', + 3 => 'Yes' + ] + ]); + + $radio->setDisabledOptions(['1']); + $this->assertTrue($radio->getOption('1')->isDisabled()); + $this->assertFalse($radio->getOption(2)->isDisabled()); + $this->assertFalse($radio->getOption(3)->isDisabled()); + + $radio->setDisabledOptions([3]); + $this->assertFalse($radio->getOption('1')->isDisabled()); + $this->assertFalse($radio->getOption(2)->isDisabled()); + $this->assertTrue($radio->getOption(3)->isDisabled()); + + $radio->setDisabledOptions([]); + $this->assertFalse($radio->getOption('1')->isDisabled()); + $this->assertFalse($radio->getOption(2)->isDisabled()); + $this->assertFalse($radio->getOption(3)->isDisabled()); + } + + + public function testEmptyStringOptionCannotBeDisabledByFalsyValuesExceptNullOrEmptyString() + { + $radio = new RadioElement('test', [ + 'options' => [ + '' => 'Option to disable', + 'foo' => 'Foo' + ] + ]); + + $radio->setDisabledOptions([false, 0, [], 0.0]); + $this->assertFalse($radio->getOption('')->isDisabled()); + + $radio->setDisabledOptions([null]); + $this->assertTrue($radio->getOption('')->isDisabled()); + + $radio->setDisabledOptions(['']); + $this->assertTrue($radio->getOption('')->isDisabled()); + } + + public function testEmptyStringOptionCannotBeCheckedByFalsyValuesExceptNullOrEmptyString() + { + $radio = new RadioElement('test', [ + 'options' => [ + '' => 'Option to check', + 'foo' => 'Foo' + ] + ]); + + $radio->setValue(false); + $this->assertStringNotContainsString('checked', $radio->render()); + + $radio->setValue(0); + $this->assertStringNotContainsString('checked', $radio->render()); + + $radio->setValue([]); + $this->assertStringNotContainsString('checked', $radio->render()); + + $radio->setValue(0.0); + $this->assertStringNotContainsString('checked', $radio->render()); + + $radio->setValue(''); + $this->assertStringContainsString('checked', $radio->render()); + + $radio->setValue(null); + $this->assertStringContainsString('checked', $radio->render()); + } + + public function testDisabledOptionsCanBePreSet() + { + $radio = new RadioElement('test', [ + 'disabledOptions' => ['foo'] + ]); + $radio->setOptions(['foo' => 'Foo', 'bar' => 'Bar']); + + $this->assertTrue($radio->getOption('foo')->isDisabled()); + $this->assertFalse($radio->getOption('bar')->isDisabled()); + } + + public function testGetOptionThrowsExceptionIfOptionDoesNotExist() + { + $radio = new RadioElement('test'); + $radio->setOptions(['foo' => 'Foo']); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('There is no such option "bar"'); + $radio->getOption('bar'); + } + + public function testAttributesAreAppliedToAllOptions() + { + $radio = new RadioElement('test', [ + 'options' => ['foo' => 'Foo', 'bar' => 'Bar'], + 'class' => 'my-radio' + ]); + + $html = $radio->render(); + $this->assertStringContainsString('class="my-radio"', $html); + $this->assertSame(2, substr_count($html, 'class="my-radio"')); + } } diff --git a/tests/FormElement/SelectElementTest.php b/tests/FormElement/SelectElementTest.php index 59180707..cf44581f 100644 --- a/tests/FormElement/SelectElementTest.php +++ b/tests/FormElement/SelectElementTest.php @@ -17,7 +17,7 @@ public function testFlatOptions() $select = new SelectElement('elname', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -43,7 +43,7 @@ public function testOptionValidity() 'label' => 'Customer', 'value' => '3', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -72,7 +72,7 @@ public function testSelectingDisabledOptionIsNotPossible() 'label' => 'Customer', 'value' => '4', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -93,7 +93,7 @@ public function testNestedOptions() $select = new SelectElement('elname', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', 'Some Options' => [ '1' => 'The one', '4' => 'Four', @@ -125,7 +125,7 @@ public function testDisabledNestedOptions() $select = new SelectElement('elname', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', 'Some options' => [ '1' => 'The one', '4' => 'Four', @@ -160,7 +160,7 @@ public function testDeeplyDisabledNestedOptions() $select = new SelectElement('elname', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', 'Some options' => [ '1' => 'The one', '4' => [ @@ -204,7 +204,7 @@ public function testDefaultValueIsSelected() 'label' => 'Customer', 'value' => '1', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -228,7 +228,7 @@ public function testSetValueSelectsAnOption() $select = new SelectElement('elname', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -280,7 +280,7 @@ public function testSetArrayAsValueWithoutMultipleAttributeThrowsException() $select = new SelectElement('elname', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -301,7 +301,7 @@ public function testSetNonArrayAsValueWithMultipleAttributeThrowsException() 'label' => 'Customer', 'multiple' => true, 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -323,7 +323,7 @@ public function testSetArrayAsValueWithMultipleAttributeSetTheOptions() $select = new SelectElement('elname', [ 'label' => 'Customer', 'options' => [ - null => 'Please choose', + '' => 'Please choose', '1' => 'The one', '4' => 'Four', '5' => 'Hi five', @@ -441,7 +441,7 @@ public function testGetValueReturnsAnArrayWhenMultipleAttributeIsSet() $this->assertSame([], $select->getValue()); } - public function testNullAndTheEmptyStringAreEquallyHandled() + public function testNullAndTheEmptyStringValuesAreEquallyHandled() { $form = new Form(); $form->addElement('select', 'select', [ @@ -449,7 +449,7 @@ public function testNullAndTheEmptyStringAreEquallyHandled() 'value' => '' ]); $form->addElement('select', 'select2', [ - 'options' => [null => 'Please choose'], + 'options' => ['' => 'Please choose'], 'value' => null ]); @@ -462,8 +462,6 @@ public function testNullAndTheEmptyStringAreEquallyHandled() $this->assertNull($select2->getValue()); $this->assertInstanceOf(SelectOption::class, $select->getOption('')); - $this->assertInstanceOf(SelectOption::class, $select2->getOption(null)); - $this->assertInstanceOf(SelectOption::class, $select->getOption(null)); $this->assertInstanceOf(SelectOption::class, $select2->getOption('')); $this->assertTrue($select->isValid()); @@ -488,27 +486,14 @@ public function testNullAndTheEmptyStringAreEquallyHandled() HTML; $this->assertHtml($html, $select2); - } - - public function testGetOptionGetValueAndElementGetValueHandleNullAndTheEmptyStringEqually() - { - $select = new SelectElement('select'); - $select->setOptions(['' => 'Foo']); - $select->setValue(''); + $select->setValue(null); $this->assertNull($select->getValue()); - $this->assertNull($select->getOption('')->getValue()); - $select = new SelectElement('select'); - $select->setOptions([null => 'Foo']); - - $this->assertNull($select->getValue()); - $this->assertNull($select->getOption(null)->getValue()); + $select2->setValue(''); + $this->assertNull($select2->getValue()); } - /** - * @depends testNullAndTheEmptyStringAreEquallyHandled - */ public function testDisablingOptionsIsWorking() { $form = new Form(); @@ -530,28 +515,17 @@ public function testDisablingOptionsIsWorking() $this->assertHtml($html, $form->getElement('select')); } - public function testNullAndTheEmptyStringAreAlsoEquallyHandledWhileDisablingOptions() + public function testNullAndTheEmptyStringAreEquallyHandledWhileDisablingOptions() { - $select = new SelectElement('select'); - $select->setOptions([null => 'Foo', 'bar' => 'Bar']); - $select->setDisabledOptions([null]); - - $this->assertTrue($select->getOption(null)->getAttributes()->get('disabled')->getValue()); - $select = new SelectElement('select'); $select->setOptions(['' => 'Foo', 'bar' => 'Bar']); - $select->setDisabledOptions(['']); + $select->setDisabledOptions([null]); $this->assertTrue($select->getOption('')->getAttributes()->get('disabled')->getValue()); - $select = new SelectElement('select'); - $select->setOptions([null => 'Foo', 'bar' => 'Bar']); - $select->setDisabledOptions(['']); - - $this->assertTrue($select->getOption(null)->getAttributes()->get('disabled')->getValue()); $select = new SelectElement('select'); $select->setOptions(['' => 'Foo', 'bar' => 'Bar']); - $select->setDisabledOptions([null]); + $select->setDisabledOptions(['']); $this->assertTrue($select->getOption('')->getAttributes()->get('disabled')->getValue()); } @@ -597,10 +571,10 @@ public function testSetOptionsResetsOptions() $select->setOptions(['car' => 'Car', 'train' => 'Train']); + $this->assertNull($select->getOption('foo')); + $this->assertNull($select->getOption('bar')); $this->assertInstanceOf(SelectOption::class, $select->getOption('car')); $this->assertInstanceOf(SelectOption::class, $select->getOption('train')); - - $this->assertNull($select->getOption('foo')); } public function testGetOptionReturnsPreviouslySetOption() @@ -608,12 +582,159 @@ public function testGetOptionReturnsPreviouslySetOption() $select = new SelectElement('select'); $select->setOptions(['' => 'Empty String', 'foo' => 'Foo', 'bar' => 'Bar']); - $this->assertNull($select->getOption('')->getValue()); + $this->assertSame('', $select->getOption('')->getValue()); + $this->assertSame('Empty String', $select->getOption('')->getLabel()); + $this->assertSame('foo', $select->getOption('foo')->getValue()); + $this->assertSame('Foo', $select->getOption('foo')->getLabel()); $select->setOptions(['' => 'Please Choose', 'car' => 'Car', 'train' => 'Train']); - $this->assertNull($select->getOption('')->getValue()); + $this->assertSame('', $select->getOption('')->getValue()); + $this->assertSame('Please Choose', $select->getOption('')->getLabel()); + $this->assertSame('car', $select->getOption('car')->getValue()); + $this->assertSame('Car', $select->getOption('car')->getLabel()); + } + + public function testSetDisabledOptionsResetsDisabledOptions() + { + $select = new SelectElement('test', [ + 'options' => [ + '1' => 'Foo', + 2 => 'Bar', + 3 => 'Yes' + ] + ]); + + $select->setDisabledOptions(['1']); + $this->assertTrue($select->getOption('1')->getAttributes()->get('disabled')->getValue()); + $this->assertFalse($select->getOption(2)->getAttributes()->get('disabled')->getValue()); + $this->assertFalse($select->getOption(3)->getAttributes()->get('disabled')->getValue()); + + $select->setDisabledOptions([3]); + $this->assertFalse($select->getOption('1')->getAttributes()->get('disabled')->getValue()); + $this->assertFalse($select->getOption(2)->getAttributes()->get('disabled')->getValue()); + $this->assertTrue($select->getOption(3)->getAttributes()->get('disabled')->getValue()); + + $select->setDisabledOptions([]); + $this->assertFalse($select->getOption('1')->getAttributes()->get('disabled')->getValue()); + $this->assertFalse($select->getOption(2)->getAttributes()->get('disabled')->getValue()); + $this->assertFalse($select->getOption(3)->getAttributes()->get('disabled')->getValue()); + } + + public function testEmptyStringOptionCannotBeDisabledByFalsyValuesExceptNullOrEmptyString() + { + $select = new SelectElement('test', [ + 'options' => [ + '' => 'Option to disable', + 'foo' => 'Foo' + ] + ]); + + $select->setDisabledOptions([false, 0, [], 0.0]); + $this->assertFalse($select->getOption('')->getAttributes()->get('disabled')->getValue()); + + $select->setDisabledOptions([null]); + $this->assertTrue($select->getOption('')->getAttributes()->get('disabled')->getValue()); + + $select->setDisabledOptions(['']); + $this->assertTrue($select->getOption('')->getAttributes()->get('disabled')->getValue()); + } + + public function testEmptyStringOptionCannotBeSelectedByFalsyValuesExceptNullOrEmptyString() + { + $select = new SelectElement('test', [ + 'options' => [ + '' => 'Option to select', + 'foo' => 'Foo' + ] + ]); + + $select->setValue(false); + $this->assertStringNotContainsString('selected', $select->render()); + + $select->setValue(0); + $this->assertStringNotContainsString('selected', $select->render()); + + $select->setValue(0.0); + $this->assertStringNotContainsString('selected', $select->render()); + + $select->setValue(''); + $this->assertStringContainsString('selected', $select->render()); + + $select->setValue(null); + $this->assertStringContainsString('selected', $select->render()); + } + + public function testNumbersOfTypeIntOrStringAsOptionKeysAreHandledEqually() + { + $select = new SelectElement('test', [ + 'label' => 'Test', + 'options' => [ + '1' => 'Foo', + 2 => 'Bar', + 3 => 'Yes' + ] + ]); + + $select->setValue(2); + + $html = <<<'HTML' + +HTML; + $this->assertHtml($html, $select); + + $select->setValue('2'); + $this->assertHtml($html, $select); + + $select->setDisabledOptions([2]); + $this->assertTrue($select->getOption(2)->getAttributes()->get('disabled')->getValue()); + + $select->setDisabledOptions(['2']); + $this->assertTrue($select->getOption(2)->getAttributes()->get('disabled')->getValue()); + + $select->setOptions([]); + + $select->setDisabledOptions(['5']); + + $select->setOptions(['5' => 'Five', 8 => 'Eight']); + $this->assertTrue($select->getOption(5)->getAttributes()->get('disabled')->getValue()); + + $select->getOption(5)->getAttributes()->get('disabled')->setValue(false); + $this->assertFalse($select->getOption(5)->getAttributes()->get('disabled')->getValue()); + } + + public function testDisabledOptionsCanBePreSet() + { + $select = new SelectElement('test', [ + 'disabledOptions' => ['foo'] + ]); + $select->setOptions(['foo' => 'Foo', 'bar' => 'Bar']); + + $this->assertTrue($select->getOption('foo')->getAttributes()->get('disabled')->getValue()); + $this->assertFalse($select->getOption('bar')->getAttributes()->get('disabled')->getValue()); + } + + public function testGetOptionReturnsNullIfOptionDoesNotExist() + { + $select = new SelectElement('test'); + $select->setOptions(['foo' => 'Foo']); + + $this->assertNull($select->getOption('bar')); + } + + public function testMultiOptionsAttributeIsSupportedForZf1Compatibility() + { + $select = new SelectElement('test', [ + 'multiOptions' => ['foo' => 'Foo', 'bar' => 'Bar'] + ]); + + $this->assertInstanceOf(SelectOption::class, $select->getOption('foo')); + $this->assertInstanceOf(SelectOption::class, $select->getOption('bar')); } }