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'], [