diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d3ed31e..9a568781 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,8 @@
# Yii Form Change Log
-## 1.1.1 under development
+## 1.2.0 under development
-- no changes in this release.
+- New #364: Add `Checkbox::labelPlacement()` method and mark `Checkbox::enclosedByLabel()` as deprecated (@vjik)
## 1.1.0 September 26, 2024
diff --git a/config/theme-bootstrap5-horizontal.php b/config/theme-bootstrap5-horizontal.php
index 6696f6ea..6a0de46a 100644
--- a/config/theme-bootstrap5-horizontal.php
+++ b/config/theme-bootstrap5-horizontal.php
@@ -5,6 +5,7 @@
use Yiisoft\Form\Field\Button;
use Yiisoft\Form\Field\ButtonGroup;
use Yiisoft\Form\Field\Checkbox;
+use Yiisoft\Form\Field\CheckboxLabelPlacement;
use Yiisoft\Form\Field\CheckboxList;
use Yiisoft\Form\Field\ErrorSummary;
use Yiisoft\Form\Field\RadioList;
@@ -24,8 +25,8 @@
'inputInvalidClass' => 'is-invalid',
'fieldConfigs' => [
Checkbox::class => [
- 'inputContainerTag()' => ['div'],
- 'addInputContainerClass()' => ['form-check'],
+ 'labelPlacement()' => [CheckboxLabelPlacement::SIDE],
+ 'addContainerClass()' => ['form-check'],
'inputClass()' => ['form-check-input'],
'inputLabelClass()' => ['form-check-label'],
],
diff --git a/config/theme-bootstrap5-vertical.php b/config/theme-bootstrap5-vertical.php
index 3829fb36..4bfc3019 100644
--- a/config/theme-bootstrap5-vertical.php
+++ b/config/theme-bootstrap5-vertical.php
@@ -5,6 +5,7 @@
use Yiisoft\Form\Field\Button;
use Yiisoft\Form\Field\ButtonGroup;
use Yiisoft\Form\Field\Checkbox;
+use Yiisoft\Form\Field\CheckboxLabelPlacement;
use Yiisoft\Form\Field\CheckboxList;
use Yiisoft\Form\Field\ErrorSummary;
use Yiisoft\Form\Field\RadioList;
@@ -24,8 +25,8 @@
'inputInvalidClass' => 'is-invalid',
'fieldConfigs' => [
Checkbox::class => [
- 'inputContainerTag()' => ['div'],
- 'addInputContainerClass()' => ['form-check'],
+ 'labelPlacement()' => [CheckboxLabelPlacement::SIDE],
+ 'addContainerClass()' => ['form-check'],
'inputClass()' => ['form-check-input'],
'inputLabelClass()' => ['form-check-label'],
],
diff --git a/src/Field/Checkbox.php b/src/Field/Checkbox.php
index 46f5ceef..1824bce7 100644
--- a/src/Field/Checkbox.php
+++ b/src/Field/Checkbox.php
@@ -26,6 +26,7 @@ final class Checkbox extends InputField implements ValidationClassInterface
private ?string $uncheckValue = '0';
private bool $enclosedByLabel = true;
+ private CheckboxLabelPlacement $labelPlacement = CheckboxLabelPlacement::WRAP;
private ?string $inputLabel = null;
private array $inputLabelAttributes = [];
private bool $inputLabelEncode = true;
@@ -82,6 +83,8 @@ public function disabled(bool $disabled = true): self
* If the input should be enclosed by label.
*
* @param bool $value If the input should be en closed by label.
+ *
+ * @deprecated Use {@see labelPLacement()} instead it.
*/
public function enclosedByLabel(bool $value): self
{
@@ -91,9 +94,19 @@ public function enclosedByLabel(bool $value): self
}
/**
- * Label displayed next to the checkbox.
+ * Set label placement relative to checkbox input.
*
- * When this option is specified, the checkbox will be enclosed by a label tag.
+ * @see CheckboxLabelPlacement
+ */
+ public function labelPlacement(CheckboxLabelPlacement $placement): self
+ {
+ $new = clone $this;
+ $new->labelPlacement = $placement;
+ return $new;
+ }
+
+ /**
+ * Label displayed next to the checkbox.
*
* @link https://www.w3.org/TR/html52/sec-forms.html#the-label-element
*/
@@ -124,7 +137,7 @@ public function addInputLabelAttributes(array $attributes): self
}
/**
- * Set enclosed label tag ID.
+ * Set checkbox label tag ID.
*
* @param string|null $id Label tag ID.
*/
@@ -136,7 +149,7 @@ public function inputLabelId(?string $id): self
}
/**
- * Replace enclosed label tag CSS classes with a new set of classes.
+ * Replace checkbox label tag CSS classes with a new set of classes.
*
* @param string|null ...$class One or many CSS classes.
*/
@@ -148,7 +161,7 @@ public function inputLabelClass(?string ...$class): self
}
/**
- * Add one or more CSS classes to the enclosed label tag.
+ * Add one or more CSS classes to the checkbox label tag.
*
* @param string|null ...$class One or many CSS classes.
*/
@@ -233,11 +246,18 @@ protected function generateInput(): string
$checkbox = Html::checkbox($this->getName(), $inputValue, $inputAttributes);
- if ($this->enclosedByLabel) {
+ $labelPlacement = $this->getLabelPlacement();
+
+ if ($labelPlacement === CheckboxLabelPlacement::WRAP) {
$label = $this->inputLabel ?? $this->label ?? $this->getInputData()->getLabel();
$checkbox = $checkbox
->label($label, $this->inputLabelAttributes)
->labelEncode($this->inputLabelEncode);
+ } elseif ($labelPlacement === CheckboxLabelPlacement::SIDE) {
+ $label = $this->inputLabel ?? $this->label ?? $this->getInputData()->getLabel();
+ $checkbox = $checkbox
+ ->sideLabel($label, $this->inputLabelAttributes)
+ ->labelEncode($this->inputLabelEncode);
}
$html = $checkbox
@@ -245,7 +265,7 @@ protected function generateInput(): string
->uncheckValue($this->uncheckValue)
->render();
- if (!$this->enclosedByLabel && $this->inputLabel !== null) {
+ if ($labelPlacement === CheckboxLabelPlacement::DEFAULT && $this->inputLabel !== null) {
$html .= ' ' . ($this->inputLabelEncode ? Html::encode($this->inputLabel) : $this->inputLabel);
}
@@ -254,7 +274,7 @@ protected function generateInput(): string
protected function shouldHideLabel(): bool
{
- return $this->enclosedByLabel;
+ return $this->getLabelPlacement() !== CheckboxLabelPlacement::DEFAULT;
}
private function prepareCheckboxValue(mixed $value): ?string
@@ -287,4 +307,14 @@ protected function prepareInputAttributes(array &$attributes): void
$this->hasCustomError() ? true : null,
);
}
+
+ private function getLabelPlacement(): CheckboxLabelPlacement
+ {
+ // If default value, use deprecated `enclosedByLabel` property
+ if ($this->labelPlacement === CheckboxLabelPlacement::WRAP) {
+ return $this->enclosedByLabel ? CheckboxLabelPlacement::WRAP : CheckboxLabelPlacement::DEFAULT;
+ }
+
+ return $this->labelPlacement;
+ }
}
diff --git a/src/Field/CheckboxLabelPlacement.php b/src/Field/CheckboxLabelPlacement.php
new file mode 100644
index 00000000..01b3a98e
--- /dev/null
+++ b/src/Field/CheckboxLabelPlacement.php
@@ -0,0 +1,26 @@
+assertSame($expected, $result);
}
+ public static function dataLabelPlacement(): iterable
+ {
+ yield 'default' => [
+ <<
+
+
+
+ HTML,
+ CheckboxLabelPlacement::DEFAULT,
+ ];
+ yield 'wrap' => [
+ <<
+
+
+ HTML,
+ CheckboxLabelPlacement::WRAP,
+ ];
+ yield 'side' => [
+ <<
+
+
+ HTML,
+ CheckboxLabelPlacement::SIDE,
+ ];
+ }
+
+ #[DataProvider('dataLabelPlacement')]
+ public function testLabelPlacement(string $expected, CheckboxLabelPlacement $placement): void
+ {
+ $inputData = new InputData('city', label: 'Voronezh');
+
+ $result = Checkbox::widget()
+ ->inputData($inputData)
+ ->inputId('UID')
+ ->uncheckValue(null)
+ ->labelPlacement($placement)
+ ->render();
+
+ $this->assertSame($expected, $result);
+ }
+
+ public static function dataLabelPlacementWithInputLabel(): iterable
+ {
+ yield 'default' => [
+ <<
+
+ Moscow
+
+ HTML,
+ CheckboxLabelPlacement::DEFAULT,
+ ];
+ yield 'wrap' => [
+ <<
+
+
+ HTML,
+ CheckboxLabelPlacement::WRAP,
+ ];
+ yield 'side' => [
+ <<
+
+
+ HTML,
+ CheckboxLabelPlacement::SIDE,
+ ];
+ }
+
+ #[DataProvider('dataLabelPlacementWithInputLabel')]
+ public function testLabelPlacementWithInputLabel(string $expected, CheckboxLabelPlacement $placement): void
+ {
+ $inputData = new InputData('city', label: 'Voronezh');
+
+ $result = Checkbox::widget()
+ ->inputData($inputData)
+ ->inputLabel('Moscow')
+ ->inputId('UID')
+ ->uncheckValue(null)
+ ->labelPlacement($placement)
+ ->render();
+
+ $this->assertSame($expected, $result);
+ }
+
+ public static function dataLabelPlacementWithLabel(): iterable
+ {
+ yield 'default' => [
+ <<
+
+
+
+ HTML,
+ CheckboxLabelPlacement::DEFAULT,
+ ];
+ yield 'wrap' => [
+ <<
+
+
+ HTML,
+ CheckboxLabelPlacement::WRAP,
+ ];
+ yield 'side' => [
+ <<
+
+
+ HTML,
+ CheckboxLabelPlacement::SIDE,
+ ];
+ }
+
+ #[DataProvider('dataLabelPlacementWithLabel')]
+ public function testLabelPlacementWithLabel(string $expected, CheckboxLabelPlacement $placement): void
+ {
+ $inputData = new InputData('city', label: 'Voronezh');
+
+ $result = Checkbox::widget()
+ ->inputData($inputData)
+ ->label('Moscow')
+ ->inputId('UID')
+ ->uncheckValue(null)
+ ->labelPlacement($placement)
+ ->render();
+
+ $this->assertSame($expected, $result);
+ }
+
+ public static function dataLabelPlacementWithLabelAndInputLabel(): iterable
+ {
+ yield 'default' => [
+ <<
+
+ Moscow
+
+ HTML,
+ CheckboxLabelPlacement::DEFAULT,
+ ];
+ yield 'wrap' => [
+ <<
+
+
+ HTML,
+ CheckboxLabelPlacement::WRAP,
+ ];
+ yield 'side' => [
+ <<
+
+
+ HTML,
+ CheckboxLabelPlacement::SIDE,
+ ];
+ }
+
+ #[DataProvider('dataLabelPlacementWithLabelAndInputLabel')]
+ public function testLabelPlacementWithLabelAndInputLabel(string $expected, CheckboxLabelPlacement $placement): void
+ {
+ $inputData = new InputData('city', label: 'Voronezh');
+
+ $result = Checkbox::widget()
+ ->inputData($inputData)
+ ->inputLabel('Moscow')
+ ->label('Vladivostok')
+ ->inputId('UID')
+ ->uncheckValue(null)
+ ->labelPlacement($placement)
+ ->render();
+
+ $this->assertSame($expected, $result);
+ }
+
public function testImmutability(): void
{
$widget = Checkbox::widget();
$this->assertNotSame($widget, $widget->uncheckValue(null));
$this->assertNotSame($widget, $widget->enclosedByLabel(true));
+ $this->assertNotSame($widget, $widget->labelPlacement(CheckboxLabelPlacement::DEFAULT));
$this->assertNotSame($widget, $widget->inputLabel(null));
$this->assertNotSame($widget, $widget->inputLabelAttributes([]));
$this->assertNotSame($widget, $widget->addInputLabelAttributes([]));