diff --git a/src/View/Helper/FormHelper.php b/src/View/Helper/FormHelper.php index a7984087..891882df 100644 --- a/src/View/Helper/FormHelper.php +++ b/src/View/Helper/FormHelper.php @@ -89,10 +89,10 @@ class FormHelper extends Helper 'multicheckboxWrapper' => '
{{content}}
', 'multicheckboxTitle' => '{{text}}', 'customFileLabel' => '', - 'customFileFormGroup' => '
{{input}}{{label}}
', + 'customFileFormGroup' => '
{{input}}{{label}}
', 'customFileInputGroupFormGroup' => '{{input}}', - 'customFileInputGroupContainer' => '{{prepend}}
{{content}}{{label}}' . - '
{{append}}', + 'customFileInputGroupContainer' => + '{{prepend}}
{{content}}{{label}}
{{append}}', 'nestingLabel' => '{{hidden}}{{input}}{{text}}{{tooltip}}', 'nestingLabelNestedInput' => '{{hidden}}{{input}}{{text}}{{tooltip}}', ]; @@ -127,13 +127,13 @@ class FormHelper extends Helper 'label' => '', 'fileLabel' => '', 'formGroup' => '{{label}}
{{input}}{{error}}{{help}}
', - 'customFileFormGroup' => '
{{input}}{{label}}
' . - '{{error}}{{help}}
', + 'customFileFormGroup' => + '
{{input}}{{label}}
{{error}}{{help}}
', 'customFileInputGroupFormGroup' => '
{{input}}{{error}}{{help}}
', - 'checkboxFormGroup' => '
{{input}}{{label}}
' . - '{{error}}{{help}}
', + 'checkboxFormGroup' => + '
{{input}}{{label}}{{error}}{{help}}
', 'customCheckboxFormGroup' => '
' . - '{{input}}{{label}}
{{error}}{{help}}
', + '{{input}}{{label}}{{error}}{{help}}', 'datetimeContainer' => '
{{content}}
', 'datetimeContainerError' => '
_getContext()->hasError($fieldName); - $options['label']['templateVars']['groupId'] = $options['templateVars']['groupId'] = $this->_domId($fieldName . '-group-label'); @@ -452,6 +450,10 @@ public function control(string $fieldName, array $options = []): string $options['templates']['label'] = $this->templater()->get('customFileLabel'); $options['templates']['formGroup'] = $this->templater()->get('customFileFormGroup'); + if ($this->_getContext()->hasError($fieldName)) { + $options['templateVars']['invalid'] = $this->_config['errorClass']; + } + if ( $options['prepend'] || $options['append'] @@ -514,6 +516,13 @@ public function control(string $fieldName, array $options = []): string unset($options['tooltip']); } + if ( + isset($options['append']) || + isset($options['prepend']) + ) { + $options['injectErrorClass'] = $this->_config['errorClass']; + } + $result = parent::control($fieldName, $options); if ($newTemplates) { diff --git a/src/View/Widget/InputgroupTrait.php b/src/View/Widget/InputgroupTrait.php index 60e12c7b..16b2f1db 100644 --- a/src/View/Widget/InputgroupTrait.php +++ b/src/View/Widget/InputgroupTrait.php @@ -33,6 +33,7 @@ protected function _withInputGroup(array $data, ContextInterface $context): stri 'prepend' => null, 'append' => null, 'injectFormControl' => true, + 'injectErrorClass' => null, 'input' => null, ]; @@ -42,7 +43,8 @@ protected function _withInputGroup(array $data, ContextInterface $context): stri $prepend = $data['prepend']; $append = $data['append']; - unset($data['append'], $data['prepend'], $data['injectFormControl']); + $errorClass = $data['injectErrorClass']; + unset($data['append'], $data['prepend'], $data['injectFormControl'], $data['injectErrorClass']); if (isset($data['input'])) { $input = $data['input']; @@ -68,6 +70,13 @@ protected function _withInputGroup(array $data, ContextInterface $context): stri } if ($prepend || $append) { + if ( + $errorClass && + $context->hasError($data['fieldName']) + ) { + $attrs['class'][] = $errorClass; + } + $input = $this->_templates->format('inputGroupContainer', [ 'attrs' => $this->_templates->formatAttributes($attrs), 'append' => $append, diff --git a/tests/TestCase/View/Helper/FormHelperTest.php b/tests/TestCase/View/Helper/FormHelperTest.php index 62eb0d38..e839be62 100644 --- a/tests/TestCase/View/Helper/FormHelperTest.php +++ b/tests/TestCase/View/Helper/FormHelperTest.php @@ -514,10 +514,10 @@ public function testErroredControl() ['label' => ['class' => 'form-check-label', 'for' => 'published']], 'Published', '/label', - '/div', ['div' => ['class' => 'invalid-feedback']], 'error message', '/div', '/div', '/div', + '/div', ]; $this->assertHtml($expected, $result); } @@ -1586,12 +1586,12 @@ public function testHelpTextHorizontal() 'label' => ['for' => 'published', 'class' => 'form-check-label'], 'Published', '/label', - '/div', ['small' => ['class' => 'form-text text-muted']], 'help text', '/small', '/div', '/div', + '/div', ]; $this->assertHtml($expected, $result); @@ -1801,6 +1801,44 @@ public function testDefaultAlignDatetimeControlWithTooltip() $this->assertHtml($expected, $result); } + public function testDefaultAlignDatetimeControlWithError() + { + $this->article['errors'] = [ + 'created' => ['error message'], + ]; + $this->Form->create($this->article); + + $now = new \DateTime('now'); + + $result = $this->Form->control('created', [ + 'type' => 'datetime-local', + 'value' => $now->format('Y-m-d H:i:s'), + ]); + + $expected = [ + ['div' => [ + 'class' => 'form-group datetime-local is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'created-group-label', + ]], + ['label' => ['id' => 'created-group-label']], + 'Created', + '/label', + 'input' => [ + 'type' => 'datetime-local', + 'name' => 'created', + 'id' => 'created', + 'class' => 'is-invalid form-control', + 'value' => $now->format('Y-m-d H:i:s'), + ], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignDatetimeControlDate() { $this->Form->create($this->article); @@ -2025,6 +2063,52 @@ public function testHorizontalAlignDatetimeControlWithTooltip() $this->assertHtml($expected, $result); } + public function testHorizontalAlignDatetimeControlWithError() + { + $this->article['errors'] = [ + 'created' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $now = new \DateTime('now'); + + $result = $this->Form->control('created', [ + 'type' => 'datetime-local', + 'value' => $now->format('Y-m-d H:i:s'), + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group row datetime-local is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'created-group-label', + ]], + ['label' => ['id' => 'created-group-label', 'class' => 'col-form-label col-sm-5']], + 'Created', + '/label', + ['div' => ['class' => 'col-sm-7']], + 'input' => [ + 'type' => 'datetime-local', + 'name' => 'created', + 'id' => 'created', + 'class' => 'is-invalid form-control', + 'value' => $now->format('Y-m-d H:i:s'), + ], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignDatetimeControlDate() { $this->Form->create($this->article, [ @@ -2270,6 +2354,47 @@ public function testInlineAlignDatetimeControlWithTooltip() $this->assertHtml($expected, $result); } + /** + * Inline datetime controls currently do not render error messages. + */ + public function testInlineAlignDatetimeControlWithError() + { + $this->withErrorReporting(0, function () { + $this->article['errors'] = [ + 'created' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + }); + + $now = new \DateTime('now'); + + $result = $this->Form->control('created', [ + 'type' => 'datetime-local', + 'value' => $now->format('Y-m-d H:i:s'), + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group datetime-local is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'created-group-label', + ]], + ['span' => ['id' => 'created-group-label', 'class' => 'sr-only']], + 'Created', + '/span', + 'input' => [ + 'type' => 'datetime-local', + 'name' => 'created', + 'id' => 'created', + 'class' => 'is-invalid form-control', + 'value' => $now->format('Y-m-d H:i:s'), + ], + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testInlineAlignDatetimeControlDate() { $this->withErrorReporting(0, function () { @@ -2495,6 +2620,42 @@ public function testDefaultAlignCheckboxControlWithTooltip() $this->assertHtml($expected, $result); } + public function testDefaultAlignCheckboxControlWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'type' => 'checkbox', + ]); + $expected = [ + ['div' => ['class' => 'form-group form-check checkbox is-invalid']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => 0, + ]], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users', + 'id' => 'users', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users']], + 'Users', + '/label', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignCheckboxControlNestedInput() { $this->Form->create($this->article); @@ -2562,6 +2723,43 @@ public function testDefaultAlignCheckboxControlNestedInputWithTooltip() $this->assertHtml($expected, $result); } + public function testDefaultAlignCheckboxControlNestedInputWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'type' => 'checkbox', + 'nestedInput' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-group form-check checkbox is-invalid']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => 0, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users', + 'id' => 'users', + 'value' => 1, + ]], + 'Users', + '/label', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignCheckboxControlInline() { $this->Form->create($this->article); @@ -2629,6 +2827,43 @@ public function testDefaultAlignCheckboxControlInlineWithTooltip() $this->assertHtml($expected, $result); } + /** + * Inline checkbox controls currently do not render error messages. + */ + public function testDefaultAlignCheckboxControlInlineWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'type' => 'checkbox', + 'inline' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-check form-check-inline checkbox is-invalid']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => 0, + ]], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users', + 'id' => 'users', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users']], + 'Users', + '/label', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignCheckboxControlInlineNestedInput() { $this->Form->create($this->article); @@ -2785,6 +3020,53 @@ public function testHorizontalAlignCheckboxControlWithTooltip() $this->assertHtml($expected, $result); } + public function testHorizontalAlignCheckboxControlWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'type' => 'checkbox', + ]); + $expected = [ + ['div' => ['class' => 'form-group row checkbox is-invalid']], + ['div' => ['class' => 'offset-sm-5 col-sm-7']], + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => 0, + ]], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users', + 'id' => 'users', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users']], + 'Users', + '/label', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignCheckboxControlNestedInput() { $this->Form->create($this->article, [ @@ -2874,8 +3156,11 @@ public function testHorizontalAlignCheckboxControlNestedInputWithTooltip() $this->assertHtml($expected, $result); } - public function testHorizontalAlignCheckboxControlInline() + public function testHorizontalAlignCheckboxControlNestedInputWithError() { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; $this->Form->create($this->article, [ 'align' => [ 'sm' => [ @@ -2887,27 +3172,31 @@ public function testHorizontalAlignCheckboxControlInline() $result = $this->Form->control('users', [ 'type' => 'checkbox', - 'inline' => true, + 'nestedInput' => true, ]); $expected = [ - ['div' => ['class' => 'form-group row checkbox']], + ['div' => ['class' => 'form-group row checkbox is-invalid']], ['div' => ['class' => 'offset-sm-5 col-sm-7']], ['div' => ['class' => 'form-check']], ['input' => [ + 'class' => 'is-invalid', 'type' => 'hidden', 'name' => 'users', 'value' => 0, ]], - ['input' => [ - 'class' => 'form-check-input', - 'type' => 'checkbox', - 'name' => 'users', - 'id' => 'users', - 'value' => 1, - ]], ['label' => ['class' => 'form-check-label', 'for' => 'users']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users', + 'id' => 'users', + 'value' => 1, + ]], 'Users', '/label', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', '/div', '/div', '/div', @@ -2915,7 +3204,48 @@ public function testHorizontalAlignCheckboxControlInline() $this->assertHtml($expected, $result); } - public function testHorizontalAlignCheckboxControlInlineWithTooltip() + public function testHorizontalAlignCheckboxControlInline() + { + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'type' => 'checkbox', + 'inline' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-group row checkbox']], + ['div' => ['class' => 'offset-sm-5 col-sm-7']], + ['div' => ['class' => 'form-check']], + ['input' => [ + 'type' => 'hidden', + 'name' => 'users', + 'value' => 0, + ]], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'checkbox', + 'name' => 'users', + 'id' => 'users', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users']], + 'Users', + '/label', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testHorizontalAlignCheckboxControlInlineWithTooltip() { $this->Form->create($this->article, [ 'align' => [ @@ -2963,6 +3293,54 @@ public function testHorizontalAlignCheckboxControlInlineWithTooltip() $this->assertHtml($expected, $result); } + public function testHorizontalAlignCheckboxControlInlineWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'type' => 'checkbox', + 'inline' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-group row checkbox is-invalid']], + ['div' => ['class' => 'offset-sm-5 col-sm-7']], + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => 0, + ]], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users', + 'id' => 'users', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users']], + 'Users', + '/label', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignCheckboxControlInlineNestedInput() { $this->Form->create($this->article, [ @@ -3127,6 +3505,46 @@ public function testInlineAlignCheckboxControlWithTooltip() $this->assertHtml($expected, $result); } + /** + * Inline checkbox controls currently do not render error messages. + */ + public function testInlineAlignCheckboxControlWithError() + { + $this->withErrorReporting(0, function () { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + }); + + $result = $this->Form->control('users', [ + 'type' => 'checkbox', + ]); + $expected = [ + ['div' => ['class' => 'form-check form-check-inline checkbox is-invalid']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => 0, + ]], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users', + 'id' => 'users', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users']], + 'Users', + '/label', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testInlineAlignCheckboxControlNestedInput() { $this->withErrorReporting(0, function () { @@ -3433,6 +3851,67 @@ public function testDefaultAlignRadioControlWithTooltip() $this->assertHtml($expected, $result); } + public function testDefaultAlignRadioControlWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'type' => 'radio', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group radio is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'd-block']], + 'Users', + '/label', + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignRadioControlNestedInput() { $this->Form->create($this->article); @@ -3535,6 +4014,68 @@ public function testDefaultAlignRadioControlInline() $this->assertHtml($expected, $result); } + public function testDefaultAlignRadioControlInlineWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'type' => 'radio', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'inline' => true, + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group radio is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'd-block']], + 'Users', + '/label', + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignRadioControlInlineNestedInput() { $this->Form->create($this->article); @@ -3587,6 +4128,69 @@ public function testDefaultAlignRadioControlInlineNestedInput() $this->assertHtml($expected, $result); } + public function testDefaultAlignRadioControlInlineNestedInputWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'type' => 'radio', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'inline' => true, + 'nestedInput' => true, + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group radio is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'd-block']], + 'Users', + '/label', + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check form-check-inline']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check form-check-inline']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignRadioPerOptionConfiguration() { $this->Form->create($this->article); @@ -3897,7 +4501,208 @@ public function testHorizontalAlignRadioControlWithTooltip() $this->assertHtml($expected, $result); } - public function testHorizontalAlignRadioControlNestedInput() + public function testHorizontalAlignRadioControlWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'type' => 'radio', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group row radio is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + 'Users', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testHorizontalAlignRadioControlNestedInput() + { + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'type' => 'radio', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'nestedInput' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-group row radio', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + 'Users', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], + 'option 2', + '/label', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testHorizontalAlignRadioControlNestedInputWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'type' => 'radio', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'nestedInput' => true, + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group row radio is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + 'Users', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testHorizontalAlignRadioControlInline() { $this->Form->create($this->article, [ 'align' => [ @@ -3914,11 +4719,11 @@ public function testHorizontalAlignRadioControlNestedInput() 1 => 'option 1', 2 => 'option 2', ], - 'nestedInput' => true, + 'inline' => true, ]); $expected = [ ['div' => ['class' => 'form-group row radio', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], - ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], 'Users', '/label', ['div' => ['class' => 'col-sm-7']], @@ -3927,27 +4732,27 @@ public function testHorizontalAlignRadioControlNestedInput() 'name' => 'users', 'value' => '', ]], - ['div' => ['class' => 'form-check']], + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], - ['input' => [ - 'class' => 'form-check-input', - 'type' => 'radio', - 'name' => 'users', - 'id' => 'users-1', - 'value' => 1, - ]], 'option 1', '/label', '/div', - ['div' => ['class' => 'form-check']], + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], - ['input' => [ - 'class' => 'form-check-input', - 'type' => 'radio', - 'name' => 'users', - 'id' => 'users-2', - 'value' => 2, - ]], 'option 2', '/label', '/div', @@ -3957,8 +4762,11 @@ public function testHorizontalAlignRadioControlNestedInput() $this->assertHtml($expected, $result); } - public function testHorizontalAlignRadioControlInline() + public function testHorizontalAlignRadioControlInlineWithError() { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; $this->Form->create($this->article, [ 'align' => [ 'sm' => [ @@ -3977,19 +4785,24 @@ public function testHorizontalAlignRadioControlInline() 'inline' => true, ]); $expected = [ - ['div' => ['class' => 'form-group row radio', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['div' => [ + 'class' => 'form-group row radio is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], 'Users', '/label', ['div' => ['class' => 'col-sm-7']], ['input' => [ + 'class' => 'is-invalid', 'type' => 'hidden', 'name' => 'users', 'value' => '', ]], ['div' => ['class' => 'form-check form-check-inline']], ['input' => [ - 'class' => 'form-check-input', + 'class' => 'form-check-input is-invalid', 'type' => 'radio', 'name' => 'users', 'id' => 'users-1', @@ -4001,7 +4814,7 @@ public function testHorizontalAlignRadioControlInline() '/div', ['div' => ['class' => 'form-check form-check-inline']], ['input' => [ - 'class' => 'form-check-input', + 'class' => 'form-check-input is-invalid', 'type' => 'radio', 'name' => 'users', 'id' => 'users-2', @@ -4011,6 +4824,9 @@ public function testHorizontalAlignRadioControlInline() 'option 2', '/label', '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', '/div', '/div', ]; @@ -4078,6 +4894,78 @@ public function testHorizontalAlignRadioControlInlineNestedInput() $this->assertHtml($expected, $result); } + public function testHorizontalAlignRadioControlInlineNestedInputWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'type' => 'radio', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'inline' => true, + 'nestedInput' => true, + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group row radio is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + 'Users', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check form-check-inline']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check form-check-inline']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignRadioControlPerOptionConfiguration() { $this->Form->create($this->article, [ @@ -4402,6 +5290,67 @@ public function testInlineAlignRadioControlWithTooltip() $this->assertHtml($expected, $result); } + /** + * Inline radio controls currently do not render error messages. + */ + public function testInlineAlignRadioControlWithError() + { + $this->withErrorReporting(0, function () { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + }); + + $result = $this->Form->control('users', [ + 'type' => 'radio', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + ]); + $expected = [ + ['div' => ['class' => 'form-group radio is-invalid', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['span' => ['id' => 'users-group-label', 'class' => 'sr-only']], + 'Users', + '/span', + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'radio', + 'name' => 'users', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testInlineAlignRadioControlWithPerOptionConfiguration() { $this->withErrorReporting(0, function () { @@ -4686,8 +5635,123 @@ public function testDefaultAlignMultipleCheckboxControlWithTooltip() $this->assertHtml($expected, $result); } - public function testDefaultAlignMultipleCheckboxControlNestedInput() + public function testDefaultAlignMultipleCheckboxControlWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'd-block']], + 'Users', + '/label', + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testDefaultAlignMultipleCheckboxControlNestedInput() + { + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'nestedInput' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-group multicheckbox', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['label' => ['id' => 'users-group-label', 'class' => 'd-block']], + 'Users', + '/label', + ['input' => [ + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + 'option 2', + '/label', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testDefaultAlignMultipleCheckboxControlNestedInputWithError() { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; $this->Form->create($this->article); $result = $this->Form->control('users', [ @@ -4699,11 +5763,16 @@ public function testDefaultAlignMultipleCheckboxControlNestedInput() 'nestedInput' => true, ]); $expected = [ - ['div' => ['class' => 'form-group multicheckbox', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['div' => [ + 'class' => 'form-group multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], ['label' => ['id' => 'users-group-label', 'class' => 'd-block']], 'Users', '/label', ['input' => [ + 'class' => 'is-invalid', 'type' => 'hidden', 'name' => 'users', 'value' => '', @@ -4711,7 +5780,7 @@ public function testDefaultAlignMultipleCheckboxControlNestedInput() ['div' => ['class' => 'form-check']], ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], ['input' => [ - 'class' => 'form-check-input', + 'class' => 'form-check-input is-invalid', 'type' => 'checkbox', 'name' => 'users[]', 'id' => 'users-1', @@ -4723,7 +5792,7 @@ public function testDefaultAlignMultipleCheckboxControlNestedInput() ['div' => ['class' => 'form-check']], ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], ['input' => [ - 'class' => 'form-check-input', + 'class' => 'form-check-input is-invalid', 'type' => 'checkbox', 'name' => 'users[]', 'id' => 'users-2', @@ -4732,6 +5801,9 @@ public function testDefaultAlignMultipleCheckboxControlNestedInput() 'option 2', '/label', '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', '/div', ]; $this->assertHtml($expected, $result); @@ -4788,6 +5860,68 @@ public function testDefaultAlignMultipleCheckboxControlInline() $this->assertHtml($expected, $result); } + public function testDefaultAlignMultipleCheckboxControlInlineWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'inline' => true, + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'd-block']], + 'Users', + '/label', + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignMultipleCheckboxControlInlineNestedInput() { $this->Form->create($this->article); @@ -4930,6 +6064,107 @@ public function testDefaultAlignMultipleCheckboxControlOptionGroups() $this->assertHtml($expected, $result); } + public function testDefaultAlignMultipleCheckboxControlOptionGroupsWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [ + 'group 1' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'group 2' => [ + 3 => 'option 3', + 4 => 'option 4', + ], + ], + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'd-block']], + 'Users', + '/label', + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['fieldset' => ['class' => 'form-group']], + ['legend' => ['class' => 'col-form-label pt-0']], + 'group 1', + '/legend', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + '/fieldset', + ['fieldset' => ['class' => 'form-group']], + ['legend' => ['class' => 'col-form-label pt-0']], + 'group 2', + '/legend', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-3', + 'value' => 3, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-3']], + 'option 3', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-4', + 'value' => 4, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-4']], + 'option 4', + '/label', + '/div', + '/fieldset', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignMultipleCheckboxControlOptionGroupsNestedInput() { $this->Form->create($this->article); @@ -5652,8 +6887,109 @@ public function testDefaultAlignMultipleCheckboxControlOptionsGroupsAndSingleEnt $this->assertHtml($expected, $result); } - public function testHorizontalAlignMultipleCheckboxControl() + public function testHorizontalAlignMultipleCheckboxControl() + { + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + ]); + $expected = [ + ['div' => ['class' => 'form-group row multicheckbox', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + 'Users', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testHorizontalAlignMultipleCheckboxControlWithTooltip() + { + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [], + 'tooltip' => 'Tooltip text', + ]); + $expected = [ + ['div' => ['class' => 'form-group row multicheckbox', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + 'Users ', + 'span' => [ + 'data-toggle' => 'tooltip', + 'title' => 'Tooltip text', + 'class' => 'fas fa-info-circle', + ], + '/span', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testHorizontalAlignMultipleCheckboxControlWithError() { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; $this->Form->create($this->article, [ 'align' => [ 'sm' => [ @@ -5671,19 +7007,24 @@ public function testHorizontalAlignMultipleCheckboxControl() ], ]); $expected = [ - ['div' => ['class' => 'form-group row multicheckbox', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['div' => [ + 'class' => 'form-group row multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], 'Users', '/label', ['div' => ['class' => 'col-sm-7']], ['input' => [ + 'class' => 'is-invalid', 'type' => 'hidden', 'name' => 'users', 'value' => '', ]], ['div' => ['class' => 'form-check']], ['input' => [ - 'class' => 'form-check-input', + 'class' => 'form-check-input is-invalid', 'type' => 'checkbox', 'name' => 'users[]', 'id' => 'users-1', @@ -5695,7 +7036,7 @@ public function testHorizontalAlignMultipleCheckboxControl() '/div', ['div' => ['class' => 'form-check']], ['input' => [ - 'class' => 'form-check-input', + 'class' => 'form-check-input is-invalid', 'type' => 'checkbox', 'name' => 'users[]', 'id' => 'users-2', @@ -5705,13 +7046,16 @@ public function testHorizontalAlignMultipleCheckboxControl() 'option 2', '/label', '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', '/div', '/div', ]; $this->assertHtml($expected, $result); } - public function testHorizontalAlignMultipleCheckboxControlWithTooltip() + public function testHorizontalAlignMultipleCheckboxControlNestedInput() { $this->Form->create($this->article, [ 'align' => [ @@ -5724,19 +7068,16 @@ public function testHorizontalAlignMultipleCheckboxControlWithTooltip() $result = $this->Form->control('users', [ 'multiple' => 'checkbox', - 'options' => [], - 'tooltip' => 'Tooltip text', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'nestedInput' => true, ]); $expected = [ ['div' => ['class' => 'form-group row multicheckbox', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], - 'Users ', - 'span' => [ - 'data-toggle' => 'tooltip', - 'title' => 'Tooltip text', - 'class' => 'fas fa-info-circle', - ], - '/span', + 'Users', '/label', ['div' => ['class' => 'col-sm-7']], ['input' => [ @@ -5744,14 +7085,41 @@ public function testHorizontalAlignMultipleCheckboxControlWithTooltip() 'name' => 'users', 'value' => '', ]], + ['div' => ['class' => 'form-check']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + ['input' => [ + 'class' => 'form-check-input', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + 'option 2', + '/label', + '/div', '/div', '/div', ]; $this->assertHtml($expected, $result); } - public function testHorizontalAlignMultipleCheckboxControlNestedInput() + public function testHorizontalAlignMultipleCheckboxControlNestedInputWithError() { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; $this->Form->create($this->article, [ 'align' => [ 'sm' => [ @@ -5770,12 +7138,17 @@ public function testHorizontalAlignMultipleCheckboxControlNestedInput() 'nestedInput' => true, ]); $expected = [ - ['div' => ['class' => 'form-group row multicheckbox', 'role' => 'group', 'aria-labelledby' => 'users-group-label']], + ['div' => [ + 'class' => 'form-group row multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], 'Users', '/label', ['div' => ['class' => 'col-sm-7']], ['input' => [ + 'class' => 'is-invalid', 'type' => 'hidden', 'name' => 'users', 'value' => '', @@ -5783,7 +7156,7 @@ public function testHorizontalAlignMultipleCheckboxControlNestedInput() ['div' => ['class' => 'form-check']], ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], ['input' => [ - 'class' => 'form-check-input', + 'class' => 'form-check-input is-invalid', 'type' => 'checkbox', 'name' => 'users[]', 'id' => 'users-1', @@ -5795,7 +7168,7 @@ public function testHorizontalAlignMultipleCheckboxControlNestedInput() ['div' => ['class' => 'form-check']], ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], ['input' => [ - 'class' => 'form-check-input', + 'class' => 'form-check-input is-invalid', 'type' => 'checkbox', 'name' => 'users[]', 'id' => 'users-2', @@ -5804,6 +7177,9 @@ public function testHorizontalAlignMultipleCheckboxControlNestedInput() 'option 2', '/label', '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', '/div', '/div', ]; @@ -5870,6 +7246,77 @@ public function testHorizontalAlignMultipleCheckboxControlInline() $this->assertHtml($expected, $result); } + public function testHorizontalAlignMultipleCheckboxControlInlineWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'inline' => true, + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group row multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + 'Users', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignMultipleCheckboxControlInlineNestedInput() { $this->Form->create($this->article, [ @@ -6030,6 +7477,116 @@ public function testHorizontalAlignMultipleCheckboxControlOptionGroups() $this->assertHtml($expected, $result); } + public function testHorizontalAlignMultipleCheckboxControlOptionGroupsWithError() + { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [ + 'group 1' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + 'group 2' => [ + 3 => 'option 3', + 4 => 'option 4', + ], + ], + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group row multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['label' => ['id' => 'users-group-label', 'class' => 'col-form-label d-block pt-0 col-sm-5']], + 'Users', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['fieldset' => ['class' => 'form-group']], + ['legend' => ['class' => 'col-form-label pt-0']], + 'group 1', + '/legend', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + '/fieldset', + ['fieldset' => ['class' => 'form-group']], + ['legend' => ['class' => 'col-form-label pt-0']], + 'group 2', + '/legend', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-3', + 'value' => 3, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-3']], + 'option 3', + '/label', + '/div', + ['div' => ['class' => 'form-check']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-4', + 'value' => 4, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-4']], + 'option 4', + '/label', + '/div', + '/fieldset', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignMultipleCheckboxControlOptionGroupsNestedInput() { $this->Form->create($this->article, [ @@ -6903,6 +8460,71 @@ public function testInlineAlignMultipleCheckboxControlWithTooltip() $this->assertHtml($expected, $result); } + /** + * Inline multi checkbox controls currently do not render error messages. + */ + public function testInlineAlignMultipleCheckboxControlWithError() + { + $this->withErrorReporting(0, function () { + $this->article['errors'] = [ + 'users' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + }); + + $result = $this->Form->control('users', [ + 'multiple' => 'checkbox', + 'options' => [ + 1 => 'option 1', + 2 => 'option 2', + ], + ]); + $expected = [ + ['div' => [ + 'class' => 'form-group multicheckbox is-invalid', + 'role' => 'group', + 'aria-labelledby' => 'users-group-label', + ]], + ['span' => ['id' => 'users-group-label', 'class' => 'sr-only']], + 'Users', + '/span', + ['input' => [ + 'class' => 'is-invalid', + 'type' => 'hidden', + 'name' => 'users', + 'value' => '', + ]], + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-1', + 'value' => 1, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-1']], + 'option 1', + '/label', + '/div', + ['div' => ['class' => 'form-check form-check-inline']], + ['input' => [ + 'class' => 'form-check-input is-invalid', + 'type' => 'checkbox', + 'name' => 'users[]', + 'id' => 'users-2', + 'value' => 2, + ]], + ['label' => ['class' => 'form-check-label', 'for' => 'users-2']], + 'option 2', + '/label', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testInlineAlignMultipleCheckboxControlWithPerOptionConfiguration() { $this->withErrorReporting(0, function () { @@ -7160,6 +8782,35 @@ public function testDefaultAlignFileControlWithTooltip() $this->assertHtml($expected, $result); } + public function testDefaultAlignFileControlWithError() + { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('file', [ + 'type' => 'file', + ]); + $expected = [ + ['div' => ['class' => 'form-group file is-invalid']], + ['label' => ['for' => 'file']], + 'File', + '/label', + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid form-control-file', + ]], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignFileControl() { $this->Form->create($this->article, [ @@ -7231,6 +8882,44 @@ public function testHorizontalAlignFileControlWithTooltip() $this->assertHtml($expected, $result); } + public function testHorizontalAlignFileControlWithError() + { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('file', [ + 'type' => 'file', + ]); + $expected = [ + ['div' => ['class' => 'form-group row file is-invalid']], + ['label' => ['class' => 'col-form-label pt-1 col-sm-5', 'for' => 'file']], + 'File', + '/label', + ['div' => ['class' => 'col-sm-7']], + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid form-control-file', + ]], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testInlineAlignFileControl() { $this->withErrorReporting(0, function () { @@ -7292,6 +8981,39 @@ public function testInlineAlignFileControlWithTooltip() $this->assertHtml($expected, $result); } + public function testInlineAlignFileControlWithError() + { + $this->withErrorReporting(0, function () { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + }); + + $result = $this->Form->control('file', [ + 'type' => 'file', + ]); + $expected = [ + ['div' => ['class' => 'form-group file is-invalid']], + ['label' => ['class' => 'sr-only', 'for' => 'file']], + 'File', + '/label', + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid form-control-file', + ]], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignRangeControl() { $this->Form->create($this->article); @@ -7357,6 +9079,41 @@ public function testDefaultAlignRangeControlWithTooltip() $this->assertHtml($expected, $result); } + public function testDefaultAlignRangeControlWithError() + { + $this->article['errors'] = [ + 'height' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('height', [ + 'type' => 'range', + 'min' => 0, + 'max' => 10, + 'step' => 1, + ]); + $expected = [ + 'div' => ['class' => 'form-group range is-invalid'], + ['label' => ['for' => 'height']], + 'Height', + '/label', + 'input' => [ + 'type' => 'range', + 'name' => 'height', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'id' => 'height', + 'class' => 'is-invalid form-control', + ], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignRangeControl() { $this->Form->create($this->article, [ @@ -7440,6 +9197,50 @@ public function testHorizontalAlignRangeControlWithTooltip() $this->assertHtml($expected, $result); } + public function testHorizontalAlignRangeControlWithError() + { + $this->article['errors'] = [ + 'height' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('height', [ + 'type' => 'range', + 'min' => 0, + 'max' => 10, + 'step' => 1, + ]); + $expected = [ + 'div' => ['class' => 'form-group row range is-invalid'], + ['label' => ['class' => 'col-form-label col-sm-5', 'for' => 'height']], + 'Height', + '/label', + ['div' => ['class' => 'col-sm-7']], + 'input' => [ + 'type' => 'range', + 'name' => 'height', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'id' => 'height', + 'class' => 'is-invalid form-control', + ], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testInlineAlignRangeControl() { $this->Form->create($this->article, [ @@ -7509,6 +9310,43 @@ public function testInlineAlignRangeControlWithTooltip() $this->assertHtml($expected, $result); } + public function testInlineAlignRangeControlWithError() + { + $this->article['errors'] = [ + 'height' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + + $result = $this->Form->control('height', [ + 'type' => 'range', + 'min' => 0, + 'max' => 10, + 'step' => 1, + ]); + $expected = [ + 'div' => ['class' => 'form-group range is-invalid'], + ['label' => ['class' => 'sr-only', 'for' => 'height']], + 'Height', + '/label', + 'input' => [ + 'type' => 'range', + 'name' => 'height', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'id' => 'height', + 'class' => 'is-invalid form-control', + ], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignSubmit() { $this->Form->create($this->article); @@ -9630,26 +11468,104 @@ public function testDefaultAlignCustomRangeControl() 'custom' => true, ]); $expected = [ - 'div' => ['class' => 'form-group range'], - ['label' => ['for' => 'height']], + 'div' => ['class' => 'form-group range'], + ['label' => ['for' => 'height']], + 'Height', + '/label', + 'input' => [ + 'type' => 'range', + 'name' => 'height', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'id' => 'height', + 'class' => 'custom-range', + ], + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testDefaultAlignCustomRangeControlWithError() + { + $this->article['errors'] = [ + 'height' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('height', [ + 'type' => 'range', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'custom' => true, + ]); + $expected = [ + 'div' => ['class' => 'form-group range is-invalid'], + ['label' => ['for' => 'height']], + 'Height', + '/label', + 'input' => [ + 'type' => 'range', + 'name' => 'height', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'id' => 'height', + 'class' => 'custom-range is-invalid', + ], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + + public function testHorizontalAlignCustomRangeControl() + { + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('height', [ + 'type' => 'range', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'custom' => true, + ]); + $expected = [ + 'div' => ['class' => 'form-group row range'], + ['label' => ['class' => 'col-form-label col-sm-5', 'for' => 'height']], 'Height', '/label', - 'input' => [ - 'type' => 'range', - 'name' => 'height', - 'min' => 0, - 'max' => 10, - 'step' => 1, - 'id' => 'height', - 'class' => 'custom-range', - ], + ['div' => ['class' => 'col-sm-7']], + 'input' => [ + 'type' => 'range', + 'name' => 'height', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'id' => 'height', + 'class' => 'custom-range', + ], + '/div', '/div', ]; $this->assertHtml($expected, $result); } - public function testHorizontalAlignCustomRangeControl() + public function testHorizontalAlignCustomRangeControlWithError() { + $this->article['errors'] = [ + 'height' => ['error message'], + ]; $this->Form->create($this->article, [ 'align' => [ 'sm' => [ @@ -9667,7 +11583,7 @@ public function testHorizontalAlignCustomRangeControl() 'custom' => true, ]); $expected = [ - 'div' => ['class' => 'form-group row range'], + 'div' => ['class' => 'form-group row range is-invalid'], ['label' => ['class' => 'col-form-label col-sm-5', 'for' => 'height']], 'Height', '/label', @@ -9679,8 +11595,11 @@ public function testHorizontalAlignCustomRangeControl() 'max' => 10, 'step' => 1, 'id' => 'height', - 'class' => 'custom-range', + 'class' => 'custom-range is-invalid', ], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', '/div', '/div', ]; @@ -9719,6 +11638,44 @@ public function testInlineAlignCustomRangeControl() $this->assertHtml($expected, $result); } + public function testInlineAlignCustomRangeControlWithError() + { + $this->article['errors'] = [ + 'height' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + + $result = $this->Form->control('height', [ + 'type' => 'range', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'custom' => true, + ]); + $expected = [ + 'div' => ['class' => 'form-group range is-invalid'], + ['label' => ['class' => 'sr-only', 'for' => 'height']], + 'Height', + '/label', + 'input' => [ + 'type' => 'range', + 'name' => 'height', + 'min' => 0, + 'max' => 10, + 'step' => 1, + 'id' => 'height', + 'class' => 'custom-range is-invalid', + ], + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignCustomSelectControl() { $this->Form->create($this->article); @@ -10086,7 +12043,7 @@ public function testDefaultAlignCustomFileControl() ]); $expected = [ ['div' => ['class' => 'form-group file']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10116,7 +12073,7 @@ public function testDefaultAlignCustomFileControlWithTooltip() ]); $expected = [ ['div' => ['class' => 'form-group file']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10132,6 +12089,38 @@ public function testDefaultAlignCustomFileControlWithTooltip() $this->assertHtml($expected, $result); } + public function testDefaultAlignCustomFileControlWithError() + { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('file', [ + 'type' => 'file', + 'custom' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-group file is-invalid']], + ['div' => ['class' => 'custom-file is-invalid']], + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid custom-file-input', + ]], + ['label' => ['class' => 'custom-file-label', 'for' => 'file']], + 'File', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testDefaultAlignCustomFileControlInputGroupAppend() { $this->Form->create($this->article); @@ -10144,7 +12133,7 @@ public function testDefaultAlignCustomFileControlInputGroupAppend() $expected = [ ['div' => ['class' => 'form-group file']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10183,7 +12172,7 @@ public function testDefaultAlignCustomFileControlInputGroupPrepend() 'prepend', '/span', '/div', - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10200,6 +12189,46 @@ public function testDefaultAlignCustomFileControlInputGroupPrepend() $this->assertHtml($expected, $result); } + public function testDefaultAlignCustomFileControlInputGroupWithError() + { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article); + + $result = $this->Form->control('file', [ + 'type' => 'file', + 'custom' => true, + 'append' => 'append', + ]); + $expected = [ + ['div' => ['class' => 'form-group file is-invalid']], + ['div' => ['class' => 'input-group is-invalid']], + ['div' => ['class' => 'custom-file is-invalid']], + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid custom-file-input', + ]], + ['label' => ['class' => 'custom-file-label', 'for' => 'file']], + 'File', + '/label', + '/div', + ['div' => ['class' => 'input-group-append']], + ['span' => ['class' => 'input-group-text']], + 'append', + '/span', + '/div', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignCustomFileControl() { $this->Form->create($this->article, [ @@ -10218,7 +12247,7 @@ public function testHorizontalAlignCustomFileControl() $expected = [ ['div' => ['class' => 'form-group row file']], ['div' => ['class' => 'offset-sm-5 col-sm-7']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10235,6 +12264,47 @@ public function testHorizontalAlignCustomFileControl() $this->assertHtml($expected, $result); } + public function testHorizontalAlignCustomFileControlWithError() + { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('file', [ + 'type' => 'file', + 'custom' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-group row file is-invalid']], + ['div' => ['class' => 'offset-sm-5 col-sm-7']], + ['div' => ['class' => 'custom-file is-invalid']], + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid custom-file-input', + ]], + ['label' => ['class' => 'custom-file-label', 'for' => 'file']], + 'File', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testHorizontalAlignCustomFileControlInputGroupAppend() { $this->Form->create($this->article, [ @@ -10255,7 +12325,7 @@ public function testHorizontalAlignCustomFileControlInputGroupAppend() ['div' => ['class' => 'form-group row file']], ['div' => ['class' => 'offset-sm-5 col-sm-7']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10303,7 +12373,7 @@ public function testHorizontalAlignCustomFileControlInputGroupPrepend() 'prepend', '/span', '/div', - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10321,6 +12391,55 @@ public function testHorizontalAlignCustomFileControlInputGroupPrepend() $this->assertHtml($expected, $result); } + public function testHorizontalAlignCustomFileControlInputGroupWithError() + { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => [ + 'sm' => [ + 'left' => 5, + 'middle' => 7, + ], + ], + ]); + + $result = $this->Form->control('file', [ + 'type' => 'file', + 'custom' => true, + 'append' => 'append', + ]); + $expected = [ + ['div' => ['class' => 'form-group row file is-invalid']], + ['div' => ['class' => 'offset-sm-5 col-sm-7']], + ['div' => ['class' => 'input-group is-invalid']], + ['div' => ['class' => 'custom-file is-invalid']], + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid custom-file-input', + ]], + ['label' => ['class' => 'custom-file-label', 'for' => 'file']], + 'File', + '/label', + '/div', + ['div' => ['class' => 'input-group-append']], + ['span' => ['class' => 'input-group-text']], + 'append', + '/span', + '/div', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testInlineAlignCustomFileControl() { $this->withErrorReporting(0, function () { @@ -10335,7 +12454,7 @@ public function testInlineAlignCustomFileControl() ]); $expected = [ ['div' => ['class' => 'form-group file']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10351,6 +12470,42 @@ public function testInlineAlignCustomFileControl() $this->assertHtml($expected, $result); } + public function testInlineAlignCustomFileControlWithError() + { + $this->withErrorReporting(0, function () { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + }); + + $result = $this->Form->control('file', [ + 'type' => 'file', + 'custom' => true, + ]); + $expected = [ + ['div' => ['class' => 'form-group file is-invalid']], + ['div' => ['class' => 'custom-file is-invalid']], + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid custom-file-input', + ]], + ['label' => ['class' => 'custom-file-label', 'for' => 'file']], + 'File', + '/label', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testInlineAlignCustomFileControlInputGroupAppend() { $this->withErrorReporting(0, function () { @@ -10367,7 +12522,7 @@ public function testInlineAlignCustomFileControlInputGroupAppend() $expected = [ ['div' => ['class' => 'form-group file']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10410,7 +12565,7 @@ public function testInlineAlignCustomFileControlInputGroupPrepend() 'prepend', '/span', '/div', - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10427,6 +12582,50 @@ public function testInlineAlignCustomFileControlInputGroupPrepend() $this->assertHtml($expected, $result); } + public function testInlineAlignCustomFileControlInputGroupWithError() + { + $this->withErrorReporting(0, function () { + $this->article['errors'] = [ + 'file' => ['error message'], + ]; + $this->Form->create($this->article, [ + 'align' => 'inline', + ]); + }); + + $result = $this->Form->control('file', [ + 'type' => 'file', + 'custom' => true, + 'append' => 'append', + ]); + $expected = [ + ['div' => ['class' => 'form-group file is-invalid']], + ['div' => ['class' => 'input-group is-invalid']], + ['div' => ['class' => 'custom-file is-invalid']], + ['input' => [ + 'type' => 'file', + 'name' => 'file', + 'id' => 'file', + 'class' => 'is-invalid custom-file-input', + ]], + ['label' => ['class' => 'custom-file-label', 'for' => 'file']], + 'File', + '/label', + '/div', + ['div' => ['class' => 'input-group-append']], + ['span' => ['class' => 'input-group-text']], + 'append', + '/span', + '/div', + '/div', + ['div' => ['class' => 'invalid-feedback']], + 'error message', + '/div', + '/div', + ]; + $this->assertHtml($expected, $result); + } + public function testCustomFileControlInputGroupInferLabelFromField() { $this->Form->create($this->article); @@ -10439,7 +12638,7 @@ public function testCustomFileControlInputGroupInferLabelFromField() $expected = [ ['div' => ['class' => 'form-group file']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10473,7 +12672,7 @@ public function testCustomFileControlInputGroupInferLabelFromAssociatedField() $expected = [ ['div' => ['class' => 'form-group file']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'associated[0][file]', @@ -10510,7 +12709,7 @@ public function testCustomFileControlInputGroupLabelTextFromOptions() $expected = [ ['div' => ['class' => 'form-group file']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10547,7 +12746,7 @@ public function testCustomFileControlInputGroupLabelAttributes() $expected = [ ['div' => ['class' => 'form-group file']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10582,7 +12781,7 @@ public function testCustomFileControlInputGroupLabelEscaping() $expected = [ ['div' => ['class' => 'form-group file']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file', @@ -10615,7 +12814,7 @@ public function testCustomFileControlInputGroupLabelEscaping() $expected = [ ['div' => ['class' => 'form-group file']], ['div' => ['class' => 'input-group']], - ['div' => ['class' => 'custom-file']], + ['div' => ['class' => 'custom-file ']], ['input' => [ 'type' => 'file', 'name' => 'file',