Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a753984
Initial plan
Copilot Aug 12, 2025
86efc7a
Implement HTML5 color input field with comprehensive test coverage
Copilot Aug 12, 2025
352629b
Apply fixes from StyleCI
StyleCIBot Aug 12, 2025
5330649
Add Color field integration to PureField classes with comprehensive t…
Copilot Aug 12, 2025
9768217
Fix Color field test data to use null instead of empty strings
Copilot Aug 12, 2025
e41e434
Fix Color field tests to hide error messages in validation class tests
Copilot Aug 12, 2025
3463d85
Fix Color field tests by removing non-existent hideError method calls
Copilot Aug 12, 2025
19377be
Add comprehensive mutation testing coverage with additional tests
Copilot Aug 12, 2025
88788ef
Apply fixes from StyleCI
StyleCIBot Aug 12, 2025
ad45316
Fix Color field validation class tests to hide error messages using e…
Copilot Aug 13, 2025
77ad4d2
Remove ignored files from tracking and fix Color field test validatio…
Copilot Aug 14, 2025
7d68aaa
Remove .phpunit.result.cache and infection.phar from tracking
Copilot Aug 14, 2025
c838834
Final cleanup - ensure ignored files are properly handled
Copilot Aug 14, 2025
cabb8d5
Update .gitignore and remove tracked files that should be ignored
Copilot Aug 14, 2025
c3e3763
Fix Color field test indentation to match actual output
Copilot Aug 14, 2025
7311905
Fix Color field test expectations for attribute ordering and empty st…
Copilot Aug 15, 2025
1db3919
Fix mutation tests: Add explicit immutability checks for CloneRemoval…
Copilot Aug 17, 2025
7c09d98
Update .gitignore
vjik Aug 18, 2025
8ffd4f3
Update .gitignore
samdark Aug 21, 2025
e9ea092
Complete Color field integration: Add to themes-preview, update docum…
Copilot Aug 21, 2025
8b7cf2b
Update CHANGELOG.md
samdark Aug 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 1.4.1 under development

- no changes in this release.
- New #377: Add `Color` field (@samdark)

## 1.4.0 March 27, 2025

Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
The package provides a set of widgets to help with dynamic server-side generation of HTML forms.
The following widgets are available out of the box:

- input fields: `Checkbox`, `CheckboxList`, `Date`, `DateTimeLocal`, `Email`, `File`, `Hidden`, `Image`,
- input fields: `Checkbox`, `CheckboxList`, `Color`, `Date`, `DateTimeLocal`, `Email`, `File`, `Hidden`, `Image`,
`Number`, `Password`, `RadioList`, `Range`, `Select`, `Telephone`, `Text`, `Textarea`, `Time`, `Url`;
- buttons: `Button`, `ResetButton`, `SubmitButton`;
- group widgets: `ButtonGroup`, `Fieldset`.
Expand Down Expand Up @@ -65,6 +65,7 @@ use Yiisoft\Form\PureField\Field;

echo Field::text('firstName', theme: 'horizontal')->label('First Name')->autofocus();
echo Field::text('lastName', theme: 'horizontal')->label('Last Name');
echo Field::color('favoriteColor')->label('Favorite Color')->value('#3498db');
echo Field::select('sex')->label('Sex')->optionsData(['m' => 'Male', 'f' => 'Female'])->prompt('—');
echo Field::number('age')->label('Age')->hint('Please enter your age.');
echo Field::submitButton('Submit')->buttonClass('primary');
Expand All @@ -85,6 +86,10 @@ The result of executing the code above will be:
<input type="text" class="form-control" name="lastName">
</div>
</div>
<div class="mb-3">
<label class="form-label">Favorite Color</label>
<input type="color" class="form-control" name="favoriteColor" value="#3498db">
</div>
<div class="mb-3">
<label class="form-label">Sex</label>
<select class="form-select" name="sex">
Expand Down
171 changes: 171 additions & 0 deletions src/Field/Color.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Form\Field;

use InvalidArgumentException;
use Yiisoft\Form\Field\Base\EnrichFromValidationRules\EnrichFromValidationRulesInterface;
use Yiisoft\Form\Field\Base\EnrichFromValidationRules\EnrichFromValidationRulesTrait;
use Yiisoft\Form\Field\Base\InputField;
use Yiisoft\Form\Field\Base\ValidationClass\ValidationClassInterface;
use Yiisoft\Form\Field\Base\ValidationClass\ValidationClassTrait;
use Yiisoft\Html\Html;

use function is_string;

/**
* Represents `<input>` element of type "color" that allows the user to select a color.
*
* @link https://html.spec.whatwg.org/multipage/input.html#color-state-(type=color)
* @link https://developer.mozilla.org/docs/Web/HTML/Element/input/color
*/
final class Color extends InputField implements EnrichFromValidationRulesInterface, ValidationClassInterface
{
use EnrichFromValidationRulesTrait;
use ValidationClassTrait;

/**
* @link https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled
*/
public function disabled(bool $disabled = true): self
{
$new = clone $this;
$new->inputAttributes['disabled'] = $disabled;
return $new;
}

/**
* A boolean attribute that controls whether or not the user can edit the form control.
*
* @param bool $value Whether to allow the value to be edited by the user.
*
* @link https://html.spec.whatwg.org/multipage/input.html#attr-input-readonly
*/
public function readonly(bool $value = true): self
{
$new = clone $this;
$new->inputAttributes['readonly'] = $value;
return $new;
}

/**
* A boolean attribute. When specified, the element is required.
*
* @param bool $value Whether the control is required for form submission.
*
* @link https://html.spec.whatwg.org/multipage/input.html#attr-input-required
*/
public function required(bool $value = true): self
{
$new = clone $this;
$new->inputAttributes['required'] = $value;
return $new;
}

/**
* Identifies the element (or elements) that describes the object.
*
* @link https://w3c.github.io/aria/#aria-describedby
*/
public function ariaDescribedBy(?string ...$value): self
{
$new = clone $this;
$new->inputAttributes['aria-describedby'] = array_filter($value, static fn (?string $v): bool => $v !== null);
return $new;
}

/**
* Defines a string value that labels the current element.
*
* @link https://w3c.github.io/aria/#aria-label
*/
public function ariaLabel(?string $value): self
{
$new = clone $this;
$new->inputAttributes['aria-label'] = $value;
return $new;
}

/**
* Focus on the control (put cursor into it) when the page loads. Only one form element could be in focus
* at the same time.
*
* @link https://html.spec.whatwg.org/multipage/interaction.html#attr-fe-autofocus
*/
public function autofocus(bool $value = true): self
{
$new = clone $this;
$new->inputAttributes['autofocus'] = $value;
return $new;
}

/**
* The `tabindex` attribute indicates that its element can be focused, and where it participates in sequential
* keyboard navigation (usually with the Tab key, hence the name).
*
* It accepts an integer as a value, with different results depending on the integer's value:
*
* - A negative value (usually `tabindex="-1"`) means that the element is not reachable via sequential keyboard
* navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
* with JavaScript.
* - `tabindex="0"` means that the element should be focusable in sequential keyboard navigation, but its order is
* defined by the document's source order.
* - A positive value means the element should be focusable in sequential keyboard navigation, with its order
* defined by the value of the number. That is, `tabindex="4"` is focused before `tabindex="5"`, but after
* `tabindex="3"`.
*
* @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
*/
public function tabIndex(?int $value): self
{
$new = clone $this;
$new->inputAttributes['tabindex'] = $value;
return $new;
}

protected function beforeRender(): void
{
if ($this->enrichFromValidationRules) {
$this->enrichment = $this
->validationRulesEnricher
?->process($this, $this->getInputData()->getValidationRules())
?? [];
}
}

protected function generateInput(): string
{
$value = $this->getValue();

if (!is_string($value) && $value !== null) {
throw new InvalidArgumentException('Color field requires a string or null value.');
}

/** @psalm-suppress MixedArgument We guess that enrichment contain correct values. */
$inputAttributes = array_merge(
$this->enrichment['inputAttributes'] ?? [],
$this->getInputAttributes()
);

return Html::input('color', $this->getName(), $value, $inputAttributes)->render();
}

protected function prepareContainerAttributes(array &$attributes): void
{
$this->addValidationClassToAttributes(
$attributes,
$this->getInputData(),
$this->hasCustomError() ? true : null,
);
}

protected function prepareInputAttributes(array &$attributes): void
{
$this->addInputValidationClassToAttributes(
$attributes,
$this->getInputData(),
$this->hasCustomError() ? true : null,
);
}
}
11 changes: 11 additions & 0 deletions src/PureField/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Yiisoft\Form\Field\ButtonGroup;
use Yiisoft\Form\Field\Checkbox;
use Yiisoft\Form\Field\CheckboxList;
use Yiisoft\Form\Field\Color;
use Yiisoft\Form\Field\Date;
use Yiisoft\Form\Field\DateTimeLocal;
use Yiisoft\Form\Field\Email;
Expand Down Expand Up @@ -81,6 +82,16 @@ final public static function checkboxList(
->inputData(new InputData($name, $value));
}

final public static function color(
?string $name = null,
mixed $value = null,
array $config = [],
?string $theme = null,
): Color {
return Color::widget(config: $config, theme: $theme ?? static::DEFAULT_THEME)
->inputData(new InputData($name, $value));
}

final public static function date(
?string $name = null,
mixed $value = null,
Expand Down
11 changes: 11 additions & 0 deletions src/PureField/FieldFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Yiisoft\Form\Field\ButtonGroup;
use Yiisoft\Form\Field\Checkbox;
use Yiisoft\Form\Field\CheckboxList;
use Yiisoft\Form\Field\Color;
use Yiisoft\Form\Field\Date;
use Yiisoft\Form\Field\DateTimeLocal;
use Yiisoft\Form\Field\Email;
Expand Down Expand Up @@ -80,6 +81,16 @@ final public function checkboxList(
->inputData(new InputData($name, $value));
}

final public function color(
?string $name = null,
mixed $value = null,
array $config = [],
?string $theme = null,
): Color {
return Color::widget(config: $config, theme: $theme ?? $this->defaultTheme)
->inputData(new InputData($name, $value));
}

final public function date(
?string $name = null,
mixed $value = null,
Expand Down
Loading
Loading