Skip to content

Commit

Permalink
Remove unnecessary attributes Error widget (#145)
Browse files Browse the repository at this point in the history
  • Loading branch information
terabytesoftw committed Nov 10, 2021
1 parent 4d936e7 commit 3b5ce61
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 30 deletions.
16 changes: 2 additions & 14 deletions docs/error.md
Expand Up @@ -145,21 +145,9 @@ That would generate the following code after validation:

Method | Description | Default
-------|-------------|---------
`config(FormModelInterface $formModel, string $attribute)` | Configure the widget. |
`encode(bool $value)` | Whether to encode the error message. | `true`
`message(string $value)` | Error message to display. | `''`
`messageCallback(array $value)` | Callback that will be called to obtain an error message. | `[]`
`tag(string $value)` | Tag to use to display the error. | `'div'`

### `Common` methods:

Method | Description | Default
-------|-------------|---------
`autofocus(bool $value = true)` | Sets the autofocus attribute | `false`
`charset(string $value)` | Sets the charset attribute | `UTF-8`
`config(FormModelInterface $formModel, string $attribute, array $attributes = [])` | Configures the widget. |
`disabled(bool $value = true)` | Sets the disabled attribute | `false`
`form(string $value)` | Sets the form attribute | `''`
`id(string $value)` | Sets the id attribute | `''`
`readonly()` | Sets the readonly attribute | `false`
`required(bool $value = true)` | Sets the required attribute | `false`
`tabIndex(int $value = 0)` | Sets the tabindex attribute | `0`
`tagAttributes(array $value)` | Attributes to use to display the error. | `[]`
55 changes: 49 additions & 6 deletions src/Widget/Error.php
Expand Up @@ -4,27 +4,50 @@

namespace Yiisoft\Form\Widget;

use Yiisoft\Form\FormModelInterface;
use Yiisoft\Form\Helper\HtmlFormErrors;
use Yiisoft\Form\Widget\Attribute\ModelAttributes;
use Yiisoft\Html\Tag\CustomTag;
use Yiisoft\Widget\Widget;

/**
* The Error widget displays an error message.
*
* @psalm-suppress MissingConstructor
*/
final class Error extends Widget
{
use ModelAttributes;

private string $attribute = '';
private bool $encode = true;
private FormModelInterface $formModel;
private string $message = '';
private array $messageCallback = [];
private string $tag = 'div';
private array $tagAttributes = [];

/**
* Specify a form, its attribute.
*
* @param FormModelInterface $formModel Form instance.
* @param string $attribute Form model's property name this widget is rendered for.
*
* @return static
*
* {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
*/
public function config(FormModelInterface $formModel, string $attribute): self
{
$new = clone $this;
$new->formModel = $formModel;
$new->attribute = $attribute;
return $new;
}

/**
* Whether content should be HTML-encoded.
*
* @param bool $value
*
* @return static
*/
public function encode(bool $value): self
{
Expand Down Expand Up @@ -81,6 +104,22 @@ public function tag(string $value): self
return $new;
}

/**
* HTML attributes for the widget container tag.
*
* @param array $value
*
* @return static
*
* See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
*/
public function tagAttributes(array $value): self
{
$new = clone $this;
$new->tagAttributes = $value;
return $new;
}

/**
* Generates a tag that contains the first validation error of the specified form attribute.
*
Expand All @@ -89,19 +128,23 @@ public function tag(string $value): self
protected function run(): string
{
$new = clone $this;
$error = HtmlFormErrors::getFirstError($new->getFormModel(), $new->attribute);
$error = HtmlFormErrors::getFirstError($new->formModel, $new->attribute);

if ($error !== '' && $new->message !== '') {
$error = $new->message;
}

if ($error !== '' && $new->messageCallback !== []) {
/** @var string */
$error = call_user_func($new->messageCallback, $new->getFormModel(), $new->attribute);
$error = call_user_func($new->messageCallback, $new->formModel, $new->attribute);
}

$html = $new->tag !== ''
? CustomTag::name($new->tag)->attributes($new->attributes)->content($error)->encode($new->encode)->render()
? CustomTag::name($new->tag)
->attributes($new->tagAttributes)
->content($error)
->encode($new->encode)
->render()
: $error;

return $error !== '' ? $html : '';
Expand Down
26 changes: 18 additions & 8 deletions src/Widget/Field.php
Expand Up @@ -293,28 +293,38 @@ public function email(array $attributes = []): self
* - `messageCallback`: callback, a PHP callback that returns the error message to be displayed.
* - `tag`: string, the tag name of the container. if not set, `div` will be used. if `null`, no container tag will
* be rendered.
* @param string $messageError the error message to be displayed.
* @param array $messageCallback the callback that returns the error message. The signature of the callback
* should be: `[$FormModel, function()]` where `$FormModel` is the model object being validated, and `function()`
* returns the error message.
* @param bool $encode Whether content should be HTML-encoded.
*
* @return static the field object itself.
*/
public function error(array $attributes = [], array $messageCallback = [], bool $encode = true): self
{
public function error(
array $attributes = [],
string $messageError = '',
array $messageCallback = [],
bool $encode = true
): self {
$new = clone $this;
/** @var string */
$errorMessage = $attributes['errorMessage'] ?? '';
$error = Error::widget();

if (isset($attributes['tag']) && is_string($attributes['tag'])) {
$error = $error->tag($attributes['tag']);
unset($attributes['tag']);
}

if ($new->errorClass !== '') {
Html::addCssClass($attributes, $new->errorClass);
}

$new->parts['{error}'] = Error::widget()
->config($new->getFormModel(), $new->attribute, $attributes)
->message($errorMessage)
->messageCallback($messageCallback)
$new->parts['{error}'] = $error
->config($new->getFormModel(), $new->attribute)
->encode($encode)
->message($messageError)
->messageCallback($messageCallback)
->tagAttributes($attributes)
->render();

return $new;
Expand Down
9 changes: 9 additions & 0 deletions tests/Widget/ErrorTest.php
Expand Up @@ -26,6 +26,7 @@ public function testImmutability(): void
$this->assertNotSame($error, $error->message(''));
$this->assertNotSame($error, $error->messageCallback([]));
$this->assertNotSame($error, $error->tag('div'));
$this->assertNotSame($error, $error->tagAttributes([]));
}

public function testMessage(): void
Expand Down Expand Up @@ -73,6 +74,14 @@ public function testTag(): void
);
}

public function testTagAttributes(): void
{
$this->assertSame(
'<div class="testClass">Value cannot be blank.</div>',
Error::widget()->config($this->formModel, 'name')->tagAttributes(['class' => 'testClass'])->render(),
);
}

protected function setUp(): void
{
parent::setUp();
Expand Down
90 changes: 88 additions & 2 deletions tests/Widget/FieldErrorTest.php
Expand Up @@ -19,6 +19,29 @@ final class FieldErrorTest extends TestCase

private PersonalForm $formModel;

public function testMessageCustomText(): void
{
$validator = $this->createValidatorMock();
$this->formModel->setAttribute('name', 'sam');
$validator->validate($this->formModel);

$expected = <<<'HTML'
<div>
<label for="personalform-name">Name</label>
<input type="text" id="personalform-name" name="PersonalForm[name]" value="sam" minlength="4" required>
<div>Write your first name.</div>
<div>This is custom error message.</div>
</div>
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()
->config($this->formModel, 'name')
->error([], 'This is custom error message.')
->render(),
);
}

public function testMessageCallback(): void
{
$validator = $this->createValidatorMock();
Expand All @@ -35,7 +58,10 @@ public function testMessageCallback(): void
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()->config($this->formModel, 'name')->error([], [$this->formModel, 'customError'])->render(),
Field::widget()
->config($this->formModel, 'name')
->error([], '', [$this->formModel, 'customError'])
->render(),
);
}

Expand All @@ -57,7 +83,7 @@ public function testMessageCallbackWithNoEncode(): void
$expected,
Field::widget()
->config($this->formModel, 'name')
->error([], [$this->formModel, 'customErrorWithIcon'], false)
->error([], '', [$this->formModel, 'customErrorWithIcon'], false)
->render(),
);
}
Expand All @@ -79,6 +105,66 @@ public function testTabularErrors(): void
$this->assertEqualsWithoutLE($expected, Field::widget()->config($this->formModel, '[0]name')->render());
}

public function testRender(): void
{
$validator = $this->createValidatorMock();
$this->formModel->setAttribute('name', 'sam');
$validator->validate($this->formModel);

$expected = <<<'HTML'
<div>
<label for="personalform-name">Name</label>
<input type="text" id="personalform-name" name="PersonalForm[name]" value="sam" minlength="4" required>
<div>Write your first name.</div>
<div>Is too short.</div>
</div>
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()->config($this->formModel, 'name')->render(),
);
}

public function testTag(): void
{
$validator = $this->createValidatorMock();
$this->formModel->setAttribute('name', 'sam');
$validator->validate($this->formModel);

$expected = <<<'HTML'
<div>
<label for="personalform-name">Name</label>
<input type="text" id="personalform-name" name="PersonalForm[name]" value="sam" minlength="4" required>
<div>Write your first name.</div>
<span>Is too short.</span>
</div>
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()->config($this->formModel, 'name')->error(['tag' => 'span'])->render(),
);
}

public function testTagAttributes(): void
{
$validator = $this->createValidatorMock();
$this->formModel->setAttribute('name', 'sam');
$validator->validate($this->formModel);

$expected = <<<'HTML'
<div>
<label for="personalform-name">Name</label>
<input type="text" id="personalform-name" name="PersonalForm[name]" value="sam" minlength="4" required>
<div>Write your first name.</div>
<div class="testClass">Is too short.</div>
</div>
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()->config($this->formModel, 'name')->error(['class' => 'testClass'])->render(),
);
}

protected function setUp(): void
{
parent::setUp();
Expand Down

0 comments on commit 3b5ce61

Please sign in to comment.