Repository: biomejs/biome (language: rust)

Issue:
📝 HTML element content must break when attributes break
### Environment information

```bash
playground
```

### Configuration

```JSON

```

### Playground link

https://biomejs.dev/playground/?bracketSameLine=true&lintRules=all&files.main.html=PAAhAC0ALQAgAEMAbwBuAHQAZQBuAHQAIABiAHIAZQBhAGsAcwAgAGIAZQBjAGEAdQBzAGUAIABhAHQAdAByAGkAYgB1AHQAZQBzACAAYgByAGUAYQBrACAALQAtAD4ACgA8AGQAaQB2ACAAYQB0AHQAcgBpAGIAdQB0AGUAPQAiAHIAZQBhAGwAbAB5ACAAbABvAG4AZwAgAGYAbwByACAAdABoAGkAcwAgAGUAeABhAG0AcABsAGUAIgAgAAoAIAAgAGEAYwB0AHUAYQBsAGwAeQA9ACIAcwBvACAAbABvAG4AZwAgAGkAdAAgAHMAaABvAHUAbABkACAAYgByAGUAYQBrACAAYQBuAGQAIABiAGUAYwBvAG0AZQAgAG0AdQBsAHQAaQBsAGkAbgBlACIACgA%2BAHQAaQBuAHkAIABjAG8AbgB0AGUAbgB0ADwALwBkAGkAdgA%2BAAoACgA8ACEALQAtACAAQwBvAG4AdABlAG4AdAAgAGQAbwBlAHMAIABuAG8AdAAgAGIAcgBlAGEAawAgAC0ALQA%2BAAoAPABkAGkAdgAgAGEAdAB0AHIAPQAiAHQAaQBuAHkAIgAKAD4AdABpAG4AeQAgAGMAbwBuAHQAZQBuAHQAPAAvAGQAaQB2AD4A

### Code of Conduct

- [x] I agree to follow Biome's Code of Conduct


Discussion / hints:

Fix patch:
diff --git a/crates/biome_html_formatter/src/html/auxiliary/element.rs b/crates/biome_html_formatter/src/html/auxiliary/element.rs
index 6e4100e77c17..476a41ccd97b 100644
--- a/crates/biome_html_formatter/src/html/auxiliary/element.rs
+++ b/crates/biome_html_formatter/src/html/auxiliary/element.rs
@@ -103,9 +103,11 @@ impl FormatNodeRule<HtmlElement> for FormatHtmlElement {
             None
         };
 
+        let attr_group_id = f.group_id("element-attr-group-id");
         FormatNodeRule::fmt(
             &FormatHtmlOpeningElement::default().with_options(FormatHtmlOpeningElementOptions {
                 r_angle_is_borrowed: borrowed_r_angle.is_some(),
+                attr_group_id,
             }),
             &opening_element,
             f,
@@ -128,13 +130,35 @@ impl FormatNodeRule<HtmlElement> for FormatHtmlElement {
                     flat_children,
                     expanded_children,
                 } => {
-                    write!(
-                        f,
-                        [best_fitting![
-                            format_args![flat_children],
-                            format_args![expanded_children]
-                        ]]
-                    )?;
+                    // FIXME: `if_group_breaks` and `if_group_fits_on_line` are supposed to be inverses of each other,
+                    // and they are. However, if the condition in the `if` below is not met, then neither of the 2
+                    // calls print any content. This is a bug.
+                    if opening_element.attributes().len() > 1 {
+                        write!(
+                            f,
+                            [
+                                // If the attribute group breaks, prettier always breaks the children as well.
+                                &if_group_breaks(&expanded_children)
+                                    .with_group_id(Some(attr_group_id)),
+                                // If the attribute group does NOT break, print whatever fits best for the children.
+                                &if_group_fits_on_line(&best_fitting![
+                                    format_args![flat_children],
+                                    format_args![expanded_children]
+                                ])
+                                .with_group_id(Some(attr_group_id)),
+                            ]
+                        )?;
+                    } else {
+                        // The workaround for the bug mentioned above is to unconditionally print the children in the
+                        // cases where the previous block would not print anything.
+                        write!(
+                            f,
+                            [&best_fitting![
+                                format_args![flat_children],
+                                format_args![expanded_children]
+                            ]]
+                        )?;
+                    }
                 }
             }
         }
diff --git a/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs b/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs
index 89139c86ecae..cbe358a06780 100644
--- a/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs
+++ b/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs
@@ -1,5 +1,5 @@
 use crate::prelude::*;
-use biome_formatter::{write, FormatRuleWithOptions};
+use biome_formatter::{write, FormatRuleWithOptions, GroupId};
 use biome_html_syntax::{HtmlOpeningElement, HtmlOpeningElementFields};
 #[derive(Debug, Clone, Default)]
 pub(crate) struct FormatHtmlOpeningElement {
@@ -11,11 +11,15 @@ pub(crate) struct FormatHtmlOpeningElement {
     /// [FormatHtmlElementList]: crate::html::lists::element_list::FormatHtmlElementList
     /// [HtmlElementList]: biome_html_syntax::HtmlElementList
     r_angle_is_borrowed: bool,
+
+    attr_group_id: Option<GroupId>,
 }
 
 pub(crate) struct FormatHtmlOpeningElementOptions {
     /// Whether or not the r_angle is borrowed, and therefore managed by a different formatter.
     pub r_angle_is_borrowed: bool,
+
+    pub attr_group_id: GroupId,
 }
 
 impl FormatRuleWithOptions<HtmlOpeningElement> for FormatHtmlOpeningElement {
@@ -23,6 +27,7 @@ impl FormatRuleWithOptions<HtmlOpeningElement> for FormatHtmlOpeningElement {
 
     fn with_options(mut self, options: Self::Options) -> Self {
         self.r_angle_is_borrowed = options.r_angle_is_borrowed;
+        self.attr_group_id = Some(options.attr_group_id);
         self
     }
 }
@@ -39,7 +44,6 @@ impl FormatNodeRule<HtmlOpeningElement> for FormatHtmlOpeningElement {
         let bracket_same_line = f.options().bracket_same_line().value();
         write!(f, [l_angle_token.format(), name.format()])?;
 
-        let attr_group_id = f.group_id("element-attr-group-id");
         write!(
             f,
             [&group(&format_with(|f| {
@@ -58,7 +62,7 @@ impl FormatNodeRule<HtmlOpeningElement> for FormatHtmlOpeningElement {
                 }
                 Ok(())
             }))
-            .with_group_id(Some(attr_group_id))]
+            .with_group_id(self.attr_group_id)]
         )?;
 
         Ok(())


Test patch:
diff --git a/crates/biome_html_formatter/tests/specs/html/content-breaks-when-attr-breaks.html b/crates/biome_html_formatter/tests/specs/html/content-breaks-when-attr-breaks.html
new file mode 100644
index 000000000000..74b379a2b66a
--- /dev/null
+++ b/crates/biome_html_formatter/tests/specs/html/content-breaks-when-attr-breaks.html
@@ -0,0 +1,5 @@
+<div
+  test="content must break if attributes break"
+  explanation="This group of attributes should break and become multiline."
+  details="The content must do the same, despite it being short."
+>short content</div>
diff --git a/crates/biome_html_formatter/tests/specs/html/content-breaks-when-attr-breaks.html.snap b/crates/biome_html_formatter/tests/specs/html/content-breaks-when-attr-breaks.html.snap
new file mode 100644
index 000000000000..27c6af0ed928
--- /dev/null
+++ b/crates/biome_html_formatter/tests/specs/html/content-breaks-when-attr-breaks.html.snap
@@ -0,0 +1,42 @@
+---
+source: crates/biome_formatter_test/src/snapshot_builder.rs
+info: content-breaks-when-attr-breaks.html
+---
+# Input
+
+```html
+<div
+  test="content must break if attributes break"
+  explanation="This group of attributes should break and become multiline."
+  details="The content must do the same, despite it being short."
+>short content</div>
+
+```
+
+
+=============================
+
+# Outputs
+
+## Output 1
+
+-----
+Indent style: Tab
+Indent width: 2
+Line ending: LF
+Line width: 80
+Attribute Position: Auto
+Bracket same line: false
+Whitespace sensitivity: css
+Indent script and style: false
+-----
+
+```html
+<div
+	test="content must break if attributes break"
+	explanation="This group of attributes should break and become multiline."
+	details="The content must do the same, despite it being short."
+>
+	short content
+</div>
+```
diff --git a/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html.snap b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html.snap
index e9346ea33517..811febbd8084 100644
--- a/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html.snap
+++ b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html.snap
@@ -42,7 +42,9 @@ Indent script and style: false
 	style="color: red"
 	data-foo="bar"
 	data-bar="foo"
->hello world</div>
+>
+	hello world
+</div>
 ```
 
 ## Output 1
@@ -64,5 +66,7 @@ Indent script and style: false
 	class="world really-long-class-name another-really-long-class-name"
 	style="color: red"
 	data-foo="bar"
-	data-bar="foo">hello world</div>
+	data-bar="foo">
+	hello world
+</div>
 ```