diff --git a/CHANGELOG.md b/CHANGELOG.md index 334ad77..f3fbdf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - Enh #48: Add `InputEmail` class for HTML `` element with attributes and rendering capabilities (@terabytesoftw) - Enh #49: Add `InputDateTimeLocal` class for HTML `` element with attributes and rendering capabilities (@terabytesoftw) - Bug #50: Standardize `tests` for clarity and consistency across all test cases (@terabytesoftw) +- Enh #51: Add `InputDate` class for HTML `` element with attributes and rendering capabilities (@terabytesoftw) ## 0.3.0 March 31, 2024 diff --git a/src/Form/InputDate.php b/src/Form/InputDate.php new file mode 100644 index 0000000..a9b443b --- /dev/null +++ b/src/Form/InputDate.php @@ -0,0 +1,96 @@ +` element. + * + * The value uses the `yyyy-mm-dd` format (for example, `2017-06-01`). + * + * Usage example: + * ```php + * echo \UIAwesome\Html\Form\InputDate::tag() + * ->name('birthday') + * ->render(); + * echo InputDate::tag() + * ->max('2017-04-30') + * ->min('2017-04-01') + * ->name('party-date') + * ->required(true) + * ->render(); + * echo InputDate::tag() + * ->name('appointment') + * ->readonly(true) + * ->value('2017-06-01') + * ->render(); + * ``` + * + * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/date + * + * @copyright Copyright (C) 2026 Terabytesoftw. + * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License. + */ +final class InputDate extends BaseInput +{ + use CanBeAutofocus; + use CanBeReadonly; + use CanBeRequired; + use HasAutocomplete; + use HasForm; + use HasList; + use HasMax; + use HasMin; + use HasStep; + use HasTabindex; + use HasValue; + + /** + * Returns the tag enumeration for the `` element. + * + * @return VoidInterface Tag enumeration instance for ``. + */ + protected function getTag(): VoidInterface + { + return Voids::INPUT; + } + + /** + * Returns the default configuration for the input element. + * + * @return array Default configuration array with method calls as keys. + * + * @phpstan-return array + */ + protected function loadDefault(): array + { + return parent::loadDefault() + ['type' => [Type::DATE]]; + } + + /** + * Renders the `` element with its attributes. + * + * @return string Rendered HTML for the `` element. + */ + protected function run(): string + { + return $this->buildElement(); + } +} diff --git a/tests/Form/InputDateTest.php b/tests/Form/InputDateTest.php new file mode 100644 index 0000000..12aec17 --- /dev/null +++ b/tests/Form/InputDateTest.php @@ -0,0 +1,1022 @@ +getAttribute('class', 'value'), + "Failed asserting that 'getAttribute()' returns the default value when missing.", + ); + } + + public function testGetAttributesReturnsAssignedAttributes(): void + { + self::assertSame( + [ + 'id' => null, + 'type' => Type::DATE, + 'class' => 'value', + ], + InputDate::tag() + ->id(null) + ->setAttribute('class', 'value') + ->getAttributes(), + "Failed asserting that 'getAttributes()' returns the assigned attributes.", + ); + } + + public function testRenderWithAccesskey(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->accesskey('value') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'accesskey' attribute.", + ); + } + + public function testRenderWithAddAriaAttribute(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addAriaAttribute('label', 'value') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'addAriaAttribute()' method.", + ); + } + + public function testRenderWithAddAriaAttributeUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addAriaAttribute(Aria::LABEL, 'value') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'addAriaAttribute()' method.", + ); + } + + public function testRenderWithAddAriaDescribedByString(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addAriaAttribute('describedby', 'value') + ->id('inputdate') + ->render(), + "Failed asserting that an explicit 'aria-describedby' string value is preserved.", + ); + } + + public function testRenderWithAddAriaDescribedByTrueBooleanValue(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addAriaAttribute('describedby', true) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to " + . "'true'.", + ); + } + + public function testRenderWithAddAriaDescribedByTrueBooleanValueAndIdNull(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addAriaAttribute('describedby', true) + ->id(null) + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to 'true' and 'id'" + . " is 'null'.", + ); + } + + public function testRenderWithAddAriaDescribedByTrueBooleanValueAndPrefixSuffix(): void + { + self::assertSame( + <<Prefix + + Suffix + HTML, + InputDate::tag() + ->addAriaAttribute('describedby', true) + ->id('inputdate') + ->prefix('Prefix') + ->prefixTag(Inline::SPAN) + ->suffix('Suffix') + ->suffixTag(Inline::SPAN) + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to 'true' and " + . 'prefix/suffix.', + ); + } + + public function testRenderWithAddAriaDescribedByTrueBooleanValueString(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addAriaAttribute('describedby', 'true') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to 'true'.", + ); + } + + public function testRenderWithAddAriaDescribedByTrueStringValueAndPrefixSuffix(): void + { + self::assertSame( + <<Prefix + + Suffix + HTML, + InputDate::tag() + ->addAriaAttribute('describedby', 'true') + ->id('inputdate') + ->prefix('Prefix') + ->prefixTag(Inline::SPAN) + ->suffix('Suffix') + ->suffixTag(Inline::SPAN) + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to 'true' and " + . 'prefix/suffix.', + ); + } + + public function testRenderWithAddDataAttribute(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addDataAttribute('value', 'value') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'addDataAttribute()' method.", + ); + } + + public function testRenderWithAddDataAttributeUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addDataAttribute(Data::VALUE, 'value') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'addDataAttribute()' method.", + ); + } + + public function testRenderWithAddEvent(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addEvent('click', "alert('Clicked!')") + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'addEvent()' method.", + ); + } + + public function testRenderWithAriaAttributes(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->ariaAttributes( + [ + 'controls' => 'value', + 'label' => 'value', + ], + ) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'ariaAttributes()' method.", + ); + } + + public function testRenderWithAriaAttributesAndAriaDescribedByTrueBooleanValue(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->ariaAttributes(['describedby' => true]) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to 'true'.", + ); + } + + public function testRenderWithAriaAttributesAndAriaDescribedByTrueStringValue(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->ariaAttributes(['describedby' => 'true']) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to 'true'.", + ); + } + + public function testRenderWithAttributes(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->attributes(['class' => 'value']) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'attributes()' method.", + ); + } + + public function testRenderWithAttributesAndAriaDescribedByTrueBooleanValue(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->attributes(['aria-describedby' => true]) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to 'true'.", + ); + } + + public function testRenderWithAttributesAndAriaDescribedByTrueStringValue(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->attributes(['aria-describedby' => 'true']) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'aria-describedby' attribute set to 'true'.", + ); + } + + public function testRenderWithAutocomplete(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->autocomplete('on') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'autocomplete' attribute.", + ); + } + + public function testRenderWithAutocompleteUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->autocomplete(Autocomplete::ON) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'autocomplete' attribute.", + ); + } + + public function testRenderWithAutofocus(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->autofocus(true) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'autofocus' attribute.", + ); + } + + public function testRenderWithClass(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->class('value') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'class' attribute.", + ); + } + + public function testRenderWithClassUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->class(BackedString::VALUE) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'class' attribute.", + ); + } + + public function testRenderWithDataAttributes(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->dataAttributes(['value' => 'value']) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'dataAttributes()' method.", + ); + } + + public function testRenderWithDefaultConfigurationValues(): void + { + self::assertSame( + << + HTML, + InputDate::tag(['class' => 'default-class']) + ->id('inputdate') + ->render(), + 'Failed asserting that default configuration values are applied correctly.', + ); + } + + public function testRenderWithDefaultProvider(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addDefaultProvider(DefaultProvider::class) + ->id('inputdate') + ->render(), + 'Failed asserting that default provider is applied correctly.', + ); + } + + public function testRenderWithDefaultValues(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->render(), + 'Failed asserting that element renders correctly with default values.', + ); + } + + public function testRenderWithDir(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->dir('ltr') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'dir' attribute.", + ); + } + + public function testRenderWithDirUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->dir(Direction::LTR) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'dir' attribute.", + ); + } + + public function testRenderWithDisabled(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->disabled(true) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'disabled' attribute.", + ); + } + + public function testRenderWithEvents(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->events( + [ + 'focus' => 'handleFocus()', + 'blur' => 'handleBlur()', + ], + ) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'events()' method.", + ); + } + + public function testRenderWithForm(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->form('value') + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'form' attribute.", + ); + } + + public function testRenderWithGenerateId(): void + { + /** @phpstan-var string $id */ + $id = InputDate::tag()->getAttribute('id', ''); + + self::assertMatchesRegularExpression( + '/^inputdate-\w+$/', + $id, + 'Failed asserting that element generates an ID when not provided.', + ); + } + + public function testRenderWithGlobalDefaultsAreApplied(): void + { + SimpleFactory::setDefaults( + InputDate::class, + ['class' => 'default-class'], + ); + + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->render(), + 'Failed asserting that global defaults are applied correctly.', + ); + + SimpleFactory::setDefaults( + InputDate::class, + [], + ); + } + + public function testRenderWithHidden(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->hidden(true) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'hidden' attribute.", + ); + } + + public function testRenderWithId(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'id' attribute.", + ); + } + + public function testRenderWithLang(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->lang('en') + ->render(), + "Failed asserting that element renders correctly with 'lang' attribute.", + ); + } + + public function testRenderWithLangUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->lang(Language::ENGLISH) + ->render(), + "Failed asserting that element renders correctly with 'lang' attribute.", + ); + } + + public function testRenderWithList(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->list('value') + ->render(), + "Failed asserting that element renders correctly with 'list' attribute.", + ); + } + + public function testRenderWithMax(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->max('2017-04-30') + ->render(), + "Failed asserting that element renders correctly with 'max' attribute.", + ); + } + + public function testRenderWithMin(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->min('2017-04-01') + ->render(), + "Failed asserting that element renders correctly with 'min' attribute.", + ); + } + + public function testRenderWithMinAndMax(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->min('2017-04-01') + ->max('2017-04-30') + ->render(), + "Failed asserting that element renders correctly with both 'min' and 'max' attributes.", + ); + } + + public function testRenderWithName(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->name('value') + ->render(), + "Failed asserting that element renders correctly with 'name' attribute.", + ); + } + + public function testRenderWithReadonly(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->readonly(true) + ->render(), + "Failed asserting that element renders correctly with 'readonly' attribute.", + ); + } + + public function testRenderWithRemoveAriaAttribute(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addAriaAttribute('label', 'value') + ->id('inputdate') + ->removeAriaAttribute('label') + ->render(), + "Failed asserting that element renders correctly with 'removeAriaAttribute()' method.", + ); + } + + public function testRenderWithRemoveAttribute(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->setAttribute('class', 'value') + ->id('inputdate') + ->removeAttribute('class') + ->render(), + "Failed asserting that element renders correctly with 'removeAttribute()' method.", + ); + } + + public function testRenderWithRemoveDataAttribute(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addDataAttribute('value', 'value') + ->id('inputdate') + ->removeDataAttribute('value') + ->render(), + "Failed asserting that element renders correctly with 'removeDataAttribute()' method.", + ); + } + + public function testRenderWithRemoveEvent(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addEvent('click', "alert('Clicked!')") + ->id('inputdate') + ->removeEvent('click') + ->render(), + "Failed asserting that element renders correctly with 'removeEvent()' method.", + ); + } + + public function testRenderWithRequired(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->required(true) + ->render(), + "Failed asserting that element renders correctly with 'required' attribute.", + ); + } + + public function testRenderWithRole(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->role('textbox') + ->render(), + "Failed asserting that element renders correctly with 'role' attribute.", + ); + } + + public function testRenderWithRoleUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->role(Role::TEXTBOX) + ->render(), + "Failed asserting that element renders correctly with 'role' attribute.", + ); + } + + public function testRenderWithSetAttribute(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->setAttribute('class', 'value') + ->render(), + "Failed asserting that element renders correctly with 'setAttribute()' method.", + ); + } + + public function testRenderWithSetAttributeUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->setAttribute(GlobalAttribute::TITLE, 'value') + ->render(), + "Failed asserting that element renders correctly with 'setAttribute()' method.", + ); + } + + public function testRenderWithStep(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->step(2) + ->render(), + "Failed asserting that element renders correctly with 'step' attribute.", + ); + } + + public function testRenderWithStepAny(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->step('any') + ->render(), + "Failed asserting that element renders correctly with 'step' attribute set to 'any'.", + ); + } + + public function testRenderWithStyle(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->style('value') + ->render(), + "Failed asserting that element renders correctly with 'style' attribute.", + ); + } + + public function testRenderWithTabindex(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->tabIndex(1) + ->render(), + "Failed asserting that element renders correctly with 'tabindex' attribute.", + ); + } + + public function testRenderWithTemplate(): void + { + self::assertSame( + << + + + HTML, + InputDate::tag() + ->id('inputdate') + ->template('
' . PHP_EOL . '{tag}' . PHP_EOL . '
') + ->render(), + 'Failed asserting that element renders correctly with a custom template wrapper.', + ); + } + + public function testRenderWithThemeProvider(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->addThemeProvider('muted', DefaultThemeProvider::class) + ->id('inputdate') + ->render(), + "Failed asserting that element renders correctly with 'addThemeProvider()' method.", + ); + } + + public function testRenderWithTitle(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->title('value') + ->render(), + "Failed asserting that element renders correctly with 'title' attribute.", + ); + } + + public function testRenderWithToString(): void + { + self::assertSame( + << + HTML, + (string) InputDate::tag()->id(null), + "Failed asserting that '__toString()' method renders correctly.", + ); + } + + public function testRenderWithTranslate(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->translate(false) + ->render(), + "Failed asserting that element renders correctly with 'translate' attribute.", + ); + } + + public function testRenderWithTranslateUsingEnum(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->translate(Translate::NO) + ->render(), + "Failed asserting that element renders correctly with 'translate' attribute.", + ); + } + + public function testRenderWithUserOverridesGlobalDefaults(): void + { + SimpleFactory::setDefaults( + InputDate::class, + [ + 'class' => 'from-global', + 'id' => 'id-global', + ], + ); + + self::assertSame( + << + HTML, + InputDate::tag(['id' => 'value'])->render(), + 'Failed asserting that user-defined attributes override global defaults correctly.', + ); + + SimpleFactory::setDefaults( + InputDate::class, + [], + ); + } + + public function testRenderWithValue(): void + { + self::assertSame( + << + HTML, + InputDate::tag() + ->id('inputdate') + ->value('2017-06-01') + ->render(), + "Failed asserting that element renders correctly with 'value' attribute.", + ); + } +}