Skip to content

Commit

Permalink
Add support output value. add docs, add more tests Range widget (#124)
Browse files Browse the repository at this point in the history
  • Loading branch information
terabytesoftw committed Oct 27, 2021
1 parent 45ec32a commit b69c2bf
Show file tree
Hide file tree
Showing 7 changed files with 331 additions and 24 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -136,6 +136,7 @@ The following documentation describes how to use widgets with PHP:
- [File](docs/file.md)
- [Radio](docs/radio.md)
- [RadioList](docs/radiolist.md)
- [Range](docs/range.md)

### Unit testing

Expand Down
82 changes: 82 additions & 0 deletions docs/range.md
@@ -0,0 +1,82 @@
# Range widget

[Range](https://www.w3.org/TR/2012/WD-html-markup-20120329/input.range.html) is an imprecise control for setting the element’s value representing a number.

## Usage

```php
<?php

declare(strict_types=1);

namespace App\Form;

use Yiisoft\Form\FormModel;

final class TestForm extends FormModel
{
public int $number = 0;
}
```

Widget view:

```php
<?php

declare(strict_types=1);

use Yiisoft\Form\FormModelInterface;
use Yiisoft\Form\Widget\Field;
use Yiisoft\Form\Widget\Form;
use Yiisoft\Form\Widget\Range;

/**
* @var FormModelInterface $data
* @var object $csrf
*/
?>

<?= Form::widget()->action('widgets')->csrf($csrf)->begin(); ?>
<?= Range::widget()->config($data, 'number')->render(); ?>
<hr class="mt-3">
<?= Field::widget()->submitButton(['class' => 'button is-block is-info is-fullwidth', 'value' => 'Save']); ?>
<?= Form::end(); ?>
```

That would generate the following code:

```html
<form action="widgets" method="POST" _csrf="8UkZkPtSj6jhmwhbTCGTQ77oRrAfacF_46hDJyX3bWqcCF38jzPI46SvT2gmaMAF9osNg05Y9Ufb3ghPSME6Rw==">
<input type="hidden" name="_csrf" value="8UkZkPtSj6jhmwhbTCGTQ77oRrAfacF_46hDJyX3bWqcCF38jzPI46SvT2gmaMAF9osNg05Y9Ufb3ghPSME6Rw==">
<input type="range" id="testform-number" name="TestForm[number]" value="0" oninput="i304985617645001.value=this.value">
<output id="i304985617645001" name="i304985617645001" for="TestForm[number]">0</output>
<hr class="mt-3">
<div>
<input type="submit" id="submit-304985635933001" class="button is-block is-info is-fullwidth" name="submit-304985635933001" value="Save">
</div>
</form>
```

### `Range` methods:

Method | Description | Default
-------|-------------|---------
`max(int $value)` | Sets the maximum value of the range. | `100`
`min(int $value)` | Sets the minimum value of the range. | `0`
`outputAttributes(array $value)` | Sets the attributes of the output element. | `[]`
`outputTag(string $value)` | Sets the tag of the output element. | `'output'`

### `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`
11 changes: 10 additions & 1 deletion src/Widget/Field.php
Expand Up @@ -665,10 +665,19 @@ public function radioList(array $attributes = [], array $items = [], array $item
public function range(array $attributes = []): self
{
$new = clone $this;
$range = Range::widget();
$attributes['type'] = self::TYPE_NUMBER;
$attributes = $new->setInputAttributes($attributes);

$new->parts['{input}'] = Range::widget()->config($new->getFormModel(), $new->attribute, $attributes)->render();
if (isset($attributes['outputAttributes']) && is_array($attributes['outputAttributes'])) {
$range = $range->outputAttributes($attributes['outputAttributes']);
}

if (isset($attributes['outputTag']) && is_string($attributes['outputTag'])) {
$range = $range->outputTag($attributes['outputTag']);
}

$new->parts['{input}'] = $range->config($new->getFormModel(), $new->attribute, $attributes)->render();

return $new;
}
Expand Down
69 changes: 61 additions & 8 deletions src/Widget/Range.php
Expand Up @@ -8,20 +8,25 @@
use Yiisoft\Form\Helper\HtmlForm;
use Yiisoft\Form\Widget\Attribute\CommonAttributes;
use Yiisoft\Form\Widget\Attribute\ModelAttributes;
use Yiisoft\Html\Html;
use Yiisoft\Html\Tag\CustomTag;
use Yiisoft\Html\Tag\Input;
use Yiisoft\Widget\Widget;

/**
* The input element with a type attribute whose value is "range" represents an imprecise control for setting the
* element’s value to a string representing a number.
*
* @link https://www.w3.org/TR/2012/WD-html-markup-20120329/input.number.html
* @link https://www.w3.org/TR/2012/WD-html-markup-20120329/input.range.html
*/
final class Range extends Widget
{
use CommonAttributes;
use ModelAttributes;

private array $outputAttributes = [];
private string $outputTag = 'output';

/**
* The expected upper bound for the element’s value.
*
Expand Down Expand Up @@ -54,12 +59,50 @@ public function min(int $value): self
return $new;
}

/**
* The HTML attributes for output tag. The following special options are recognized.
*
* @param array $value
*
* @return static
*
* See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
*/
public function outputAttributes(array $value): self
{
$new = clone $this;
$new->outputAttributes = $value;
return $new;
}

/**
* The tag name of the output tag.
*
* Empty to render error messages without container {@see Html::tag()}.
*
* @param string $value
*
* @return static
*/
public function outputTag(string $value): self
{
$new = clone $this;
$new->outputTag = $value;
return $new;
}

/**
* @return string the generated input tag.
*/
protected function run(): string
{
$new = clone $this;
$name = HtmlForm::getInputName($new->getFormModel(), $new->attribute);
$nameOutput = Html::generateId();

if (empty($new->outputTag)) {
throw new InvalidArgumentException('The output tag name it cannot be empty value.');
}

/** @link https://www.w3.org/TR/2012/WD-html-markup-20120329/input.range.html#input.range.attrs.value */
$value = HtmlForm::getAttributeValue($new->getFormModel(), $new->attribute);
Expand All @@ -68,12 +111,22 @@ protected function run(): string
throw new InvalidArgumentException('Range widget must be a numeric value.');
}

return Input::tag()
->type('range')
->attributes($new->attributes)
->id($new->getId())
->name(HtmlForm::getInputName($new->getFormModel(), $new->attribute))
->value($value)
->render();
$new->outputAttributes['for'] = $name;
$new->outputAttributes['name'] = $nameOutput;
$new->attributes['oninput'] = "$nameOutput.value=this.value";

return
Input::tag()
->type('range')
->attributes($new->attributes)
->id($new->getId())
->name($name)
->value($value)
->render() . PHP_EOL .
CustomTag::name($new->outputTag)
->attributes($new->outputAttributes)
->content((string) $value)
->id($nameOutput)
->render();
}
}
92 changes: 89 additions & 3 deletions tests/Widget/FieldRangeTest.php
Expand Up @@ -9,6 +9,7 @@
use Yiisoft\Form\Tests\TestSupport\Form\TypeForm;
use Yiisoft\Form\Tests\TestSupport\TestTrait;
use Yiisoft\Form\Widget\Field;
use Yiisoft\Html\Html;
use Yiisoft\Test\Support\Container\SimpleContainer;
use Yiisoft\Widget\WidgetFactory;

Expand All @@ -20,10 +21,12 @@ final class FieldRangeTest extends TestCase

public function testMax(): void
{
$this->setInaccessibleProperty(new Html(), 'generateIdCounter', []);
$expected = <<<'HTML'
<div>
<label for="typeform-int">Int</label>
<input type="range" id="typeform-int" name="TypeForm[int]" value="0" max="8">
<input type="range" id="typeform-int" name="TypeForm[int]" value="0" max="8" oninput="i1.value=this.value">
<output id="i1" name="i1" for="TypeForm[int]">0</output>
</div>
HTML;
$this->assertEqualsWithoutLE(
Expand All @@ -34,10 +37,12 @@ public function testMax(): void

public function testMin(): void
{
$this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 1]);
$expected = <<<'HTML'
<div>
<label for="typeform-int">Int</label>
<input type="range" id="typeform-int" name="TypeForm[int]" value="0" min="4">
<input type="range" id="typeform-int" name="TypeForm[int]" value="0" min="4" oninput="i2.value=this.value">
<output id="i2" name="i2" for="TypeForm[int]">0</output>
</div>
HTML;
$this->assertEqualsWithoutLE(
Expand All @@ -46,12 +51,93 @@ public function testMin(): void
);
}

public function testOutputAttributes(): void
{
$this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 2]);
$expected = <<<'HTML'
<div>
<label for="typeform-int">Int</label>
<input type="range" id="typeform-int" name="TypeForm[int]" value="0" outputAttributes='{"class":"test-class"}' oninput="i3.value=this.value">
<output id="i3" class="test-class" name="i3" for="TypeForm[int]">0</output>
</div>
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()
->config($this->formModel, 'int')
->range(['outputAttributes' => ['class' => 'test-class']])
->render(),
);
}

public function testOutputTag(): void
{
$this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 3]);
$expected = <<<'HTML'
<div>
<label for="typeform-int">Int</label>
<input type="range" id="typeform-int" name="TypeForm[int]" value="0" outputTag="p" oninput="i4.value=this.value">
<p id="i4" name="i4" for="TypeForm[int]">0</p>
</div>
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()
->config($this->formModel, 'int')
->range(['outputTag' => 'p'])
->render(),
);
}

public function testOutputTagException(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('The output tag name it cannot be empty value.');
Field::widget()->config($this->formModel, 'int')->range(['outputTag' => ''])->render();
}

public function testRender(): void
{
$this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 5]);
$expected = <<<'HTML'
<div>
<label for="typeform-int">Int</label>
<input type="range" id="typeform-int" name="TypeForm[int]" value="0" oninput="i6.value=this.value">
<output id="i6" name="i6" for="TypeForm[int]">0</output>
</div>
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()->config($this->formModel, 'int')->range()->render(),
);
}

public function testValue(): void
{
// string value numeric `1`.
$this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 6]);
$this->formModel->setAttribute('string', '1');
$expected = <<<'HTML'
<div>
<label for="typeform-string">String</label>
<input type="range" id="typeform-string" name="TypeForm[string]" value="1" placeholder="Typed your text string." oninput="i7.value=this.value">
<output id="i7" name="i7" for="TypeForm[string]">1</output>
<div>Write your text string.</div>
</div>
HTML;
$this->assertEqualsWithoutLE(
$expected,
Field::widget()->config($this->formModel, 'string')->range()->render(),
);

// int value 1
$this->setInaccessibleProperty(new Html(), 'generateIdCounter', ['i' => 7]);
$this->formModel->setAttribute('int', '1');
$expected = <<<'HTML'
<div>
<label for="typeform-int">Int</label>
<input type="range" id="typeform-int" name="TypeForm[int]" value="0">
<input type="range" id="typeform-int" name="TypeForm[int]" value="1" oninput="i8.value=this.value">
<output id="i8" name="i8" for="TypeForm[int]">1</output>
</div>
HTML;
$this->assertEqualsWithoutLE(
Expand Down

0 comments on commit b69c2bf

Please sign in to comment.