Skip to content

Commit

Permalink
Extract ValidationClassFilter
Browse files Browse the repository at this point in the history
  • Loading branch information
xificurk committed May 2, 2020
1 parent 3e39c6f commit 2c7bdd1
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 10 deletions.
2 changes: 2 additions & 0 deletions src/FormRenderer/Bootstrap3Renderer.php
Expand Up @@ -3,6 +3,7 @@

namespace Nepada\FormRenderer;

use Nepada\FormRenderer\Filters\ValidationClassFilter;
use Nette;
use Nette\Forms\Controls;
use Nette\Forms\Form;
Expand Down Expand Up @@ -64,6 +65,7 @@ public function render(Form $form): string

$templateRenderer = $this->getTemplateRenderer();
$template = $templateRenderer->getTemplate();
$template->addFilter('validationClass', new ValidationClassFilter('has-error', null));
$template->mode = $this->mode;
$template->gridOffsetClass = sprintf('col-sm-offset-%d', $this->labelCols);
$template->gridLabelClass = sprintf('col-sm-%d', $this->labelCols);
Expand Down
11 changes: 2 additions & 9 deletions src/FormRenderer/Bootstrap4Renderer.php
Expand Up @@ -3,6 +3,7 @@

namespace Nepada\FormRenderer;

use Nepada\FormRenderer\Filters\ValidationClassFilter;
use Nette;
use Nette\Forms\Controls;
use Nette\Forms\Form;
Expand Down Expand Up @@ -71,15 +72,7 @@ public function render(Form $form): string

$templateRenderer = $this->getTemplateRenderer();
$template = $templateRenderer->getTemplate();
$template->addFilter(
'validationClass',
function (Nette\Forms\IControl $control): ?string {
if (count($control->getErrors()) > 0) {
return 'is-invalid';
}
return null;
},
);
$template->addFilter('validationClass', new ValidationClassFilter('is-invalid', null));
$template->useCustomControls = $this->useCustomControls;
$template->mode = $this->mode;
$template->gridOffsetClass = $this->mode === self::MODE_HORIZONTAL ? sprintf('offset-sm-%d', $this->labelCols) : null;
Expand Down
46 changes: 46 additions & 0 deletions src/FormRenderer/Filters/ValidationClassFilter.php
@@ -0,0 +1,46 @@
<?php
declare(strict_types = 1);

namespace Nepada\FormRenderer\Filters;

use Nette;

final class ValidationClassFilter
{

use Nette\SmartObject;

private ?string $invalidClass;

private ?string $validClass;

public function __construct(?string $invalidClass, ?string $validClass)
{
$this->invalidClass = $invalidClass;
$this->validClass = $validClass;
}

public function __invoke(Nette\Forms\IControl $control): ?string
{
if (count($control->getErrors()) > 0) {
return $this->invalidClass;
}

if ($this->isFilled($control)) {
return $this->validClass;
}

return null;
}

private function isFilled(Nette\Forms\IControl $control): bool
{
if ($control instanceof Nette\Forms\Controls\BaseControl || method_exists($control, 'isFilled')) {
return $control->isFilled();
}

$value = $control->getValue();
return $value !== null && $value !== [] && $value !== '';
}

}
2 changes: 1 addition & 1 deletion src/FormRenderer/templates/bootstrap3.latte
Expand Up @@ -52,7 +52,7 @@
{/define}

{define #pair-type-default}
<div n:class="form-group, $control->getOption('class'), $control->isRequired() ? required, $control->hasErrors() ? has-error" n:attr="id => $control->getOption('id')">
<div n:class="form-group, $control->getOption('class'), $control->isRequired() ? required, ($control|validationClass)" n:attr="id => $control->getOption('id')">
{include #label, control => $control}
<div n:tag-if="$mode === horizontal" n:class="!$control->getLabel() ? $gridOffsetClass, $gridControlClass">
{include #helpers-override-split, blockNamePrefix => control, control => $control}
Expand Down
64 changes: 64 additions & 0 deletions tests/FormRenderer/Filters/Fixtures/CustomControl.php
@@ -0,0 +1,64 @@
<?php
declare(strict_types = 1);

namespace NepadaTests\FormRenderer\Filters\Fixtures;

use Nette;

class CustomControl implements Nette\Forms\IControl
{

use Nette\SmartObject;

/** @var mixed */
private $value;

/** @var string[] */
private array $errors;

/**
* @param mixed $value
* @param string[] $errors
*/
public function __construct($value = null, array $errors = [])
{
$this->value = $value;
$this->errors = $errors;
}

/**
* @param mixed $value
* @return static
*/
public function setValue($value): self
{
$this->value = $value;
return $this;
}

/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}

public function validate(): void
{
}

/**
* @return string[]
*/
public function getErrors(): array
{
return $this->errors;
}

public function isOmitted(): bool
{
return false;
}

}
27 changes: 27 additions & 0 deletions tests/FormRenderer/Filters/Fixtures/FillableCustomControl.php
@@ -0,0 +1,27 @@
<?php
declare(strict_types = 1);

namespace NepadaTests\FormRenderer\Filters\Fixtures;

class FillableCustomControl extends CustomControl
{

private bool $isFilled;

/**
* @param bool $isFilled
* @param mixed $value
* @param string[] $errors
*/
public function __construct(bool $isFilled, $value = null, array $errors = [])
{
parent::__construct($value, $errors);
$this->isFilled = $isFilled;
}

public function isFilled(): bool
{
return $this->isFilled;
}

}
104 changes: 104 additions & 0 deletions tests/FormRenderer/Filters/ValidationClassFilterTest.phpt
@@ -0,0 +1,104 @@
<?php
declare(strict_types = 1);

namespace NepadaTests\FormRenderer\Filters;

use Nepada\FormRenderer\Filters\ValidationClassFilter;
use NepadaTests\FormRenderer\Filters\Fixtures\CustomControl;
use NepadaTests\FormRenderer\Filters\Fixtures\FillableCustomControl;
use NepadaTests\TestCase;
use Nette\Forms\IControl;
use Tester\Assert;

require_once __DIR__ . '/../../bootstrap.php';


/**
* @testCase
*/
class ValidationClassFilterTest extends TestCase
{

/**
* @dataProvider getFilterData
* @param string $description
* @param string|null $invalidClass
* @param string|null $validClass
* @param IControl $control
* @param string|null $expectedClass
*/
public function testFilter(string $description, ?string $invalidClass, ?string $validClass, IControl $control, ?string $expectedClass): void
{
$filter = new ValidationClassFilter($invalidClass, $validClass);
Assert::same($expectedClass, $filter->__invoke($control), $description);
}

/**
* @return mixed[]
*/
protected function getFilterData(): array
{
return [
[
'description' => 'filled control with error',
'invalidClass' => 'error',
'validClass' => 'filled',
'control' => new FillableCustomControl(true, 'foo', ['error']),
'expectedClass' => 'error',
],
[
'description' => 'filled control with error',
'invalidClass' => null,
'validClass' => 'filled',
'control' => new FillableCustomControl(true, 'foo', ['error']),
'expectedClass' => null,
],
[
'description' => 'isFilled() preference',
'invalidClass' => 'error',
'validClass' => 'filled',
'control' => new FillableCustomControl(false, 'foo'),
'expectedClass' => null,
],
[
'description' => 'isFilled() preference',
'invalidClass' => 'error',
'validClass' => 'filled',
'control' => new FillableCustomControl(true, null),
'expectedClass' => 'filled',
],
[
'description' => 'null is not filled by fallback logic',
'invalidClass' => 'error',
'validClass' => 'filled',
'control' => new CustomControl(null),
'expectedClass' => null,
],
[
'description' => 'empty array is not filled by fallback logic',
'invalidClass' => 'error',
'validClass' => 'filled',
'control' => new CustomControl([]),
'expectedClass' => null,
],
[
'description' => 'empty string is not filled by fallback logic',
'invalidClass' => 'error',
'validClass' => 'filled',
'control' => new CustomControl(''),
'expectedClass' => null,
],
[
'description' => 'filled by fallback logic',
'invalidClass' => 'error',
'validClass' => 'filled',
'control' => new CustomControl('foo'),
'expectedClass' => 'filled',
],
];
}

}


(new ValidationClassFilterTest())->run();

0 comments on commit 2c7bdd1

Please sign in to comment.