diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index b44911b9535ba..1a58330efa509 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add `form_label_content` and `form_help_content` block to form themes * Add `#[Template()]` to describe how to render arrays returned by controllers + * Add support for toggle buttons in Bootstrap 5 form theme 6.1 --- diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig index eef6f606edc14..a8b381ec84eab 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig @@ -209,30 +209,48 @@ {%- endblock submit_widget %} {%- block checkbox_widget -%} - {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-check-input')|trim}) -%} + {%- set attr_class = attr_class|default(attr.class|default('')) -%} + {%- set row_class = '' -%} + {%- if 'btn-check' not in attr_class -%} + {%- set attr_class = attr_class ~ ' form-check-input' -%} + {%- set row_class = 'form-check' -%} + {%- endif -%} + {%- set attr = attr|merge({class: attr_class|trim}) -%} {%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%} - {%- set row_class = 'form-check' -%} {%- if 'checkbox-inline' in parent_label_class %} {% set row_class = row_class ~ ' form-check-inline' %} {% endif -%} {%- if 'checkbox-switch' in parent_label_class %} {% set row_class = row_class ~ ' form-switch' %} {% endif -%} -
- {{- form_label(form, null, { widget: parent() }) -}} -
+ {%- if row_class is not empty -%} +
+ {%- endif -%} + {{- form_label(form, null, { widget: parent() }) -}} + {%- if row_class is not empty -%} +
+ {%- endif -%} {%- endblock checkbox_widget %} {%- block radio_widget -%} - {%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-check-input')|trim}) -%} + {%- set attr_class = attr_class|default(attr.class|default('')) -%} + {%- set row_class = '' -%} + {%- if 'btn-check' not in attr_class -%} + {%- set attr_class = attr_class ~ ' form-check-input' -%} + {%- set row_class = 'form-check' -%} + {%- endif -%} + {%- set attr = attr|merge({class: attr_class|trim}) -%} {%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%} - {%- set row_class = 'form-check' -%} {%- if 'radio-inline' in parent_label_class -%} {%- set row_class = row_class ~ ' form-check-inline' -%} {%- endif -%} -
- {{- form_label(form, null, { widget: parent() }) -}} -
+ {%- if row_class is not empty -%} +
+ {%- endif -%} + {{- form_label(form, null, { widget: parent() }) -}} + {%- if row_class is not empty -%} +
+ {%- endif -%} {%- endblock radio_widget %} {%- block choice_widget_collapsed -%} @@ -276,7 +294,11 @@ {%- block checkbox_radio_label -%} {#- Do not display the label if widget is not defined in order to prevent double label rendering -#} {%- if widget is defined -%} - {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' form-check-label')|trim}) -%} + {%- set label_attr_class = label_attr_class|default(label_attr.class|default('')) -%} + {%- if 'btn' not in label_attr_class -%} + {%- set label_attr_class = label_attr_class ~ ' form-check-label' -%} + {%- endif -%} + {%- set label_attr = label_attr|merge({class: label_attr_class|trim}) -%} {%- if not compound -%} {% set label_attr = label_attr|merge({'for': id}) %} {%- endif -%} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTest.php index 1403372bc5599..7ac695ec927c3 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTest.php @@ -404,6 +404,21 @@ public function testCheckboxSwitchWithValue() ); } + public function testCheckboxToggleWithValue() + { + $form = $this->factory->createNamed('name', CheckboxType::class, false, [ + 'value' => 'foo&bar', + ]); + + $this->assertWidgetMatchesXpath($form->createView(), ['id' => 'my&id', 'attr' => ['class' => 'btn-check my&class'], 'label_attr' => ['class' => 'btn btn-primary']], + '/input[@type="checkbox"][@name="name"][@id="my&id"][@class="btn-check my&class"][@value="foo&bar"] + /following-sibling::label + [@class="btn btn-primary required"] + [.="[trans]Name[/trans]"] +' + ); + } + public function testMultipleChoiceSkipsPlaceholder() { $form = $this->factory->createNamed('name', ChoiceType::class, ['&a'], [