Skip to main content

kozan_core/html/
html_heading_element.rs

1//! `HTMLHeadingElement` — heading elements h1 through h6.
2//!
3//! Chrome equivalent: `HTMLHeadingElement`. One class for all heading levels.
4//! The tag name varies at runtime ("h1" through "h6"), controlled by
5//! `Document::create_with_tag()`.
6//!
7//! # Usage
8//!
9//! ```ignore
10//! // Convenience method (recommended):
11//! let h2 = doc.create_heading(2);
12//!
13//! // Or explicit:
14//! let h3 = doc.create_with_tag::<HtmlHeadingElement>("h3");
15//! ```
16
17use crate::Handle;
18use crate::dom::traits::Element;
19use kozan_macros::{Element, Props};
20
21/// A heading element (`<h1>` through `<h6>`).
22///
23/// Chrome equivalent: `HTMLHeadingElement`.
24/// One type for all 6 levels. The actual level is determined by the
25/// runtime tag name, not the const `TAG_NAME`.
26#[derive(Copy, Clone, Element)]
27#[element(data = HeadingData, manual_html)]
28pub struct HtmlHeadingElement(Handle);
29
30/// Element-specific data for heading elements.
31#[derive(Default, Clone, Props)]
32#[props(element = HtmlHeadingElement)]
33pub struct HeadingData {
34    /// The heading level (1-6). Set automatically by `Document::create_heading()`.
35    #[prop]
36    pub level: u8,
37}
38
39impl HtmlHeadingElement {
40    /// Get the heading level from the tag name.
41    ///
42    /// Returns 1-6 based on the actual runtime tag.
43    /// Falls back to the stored `level` data.
44    #[must_use]
45    pub fn heading_level(&self) -> u8 {
46        match self.tag_name() {
47            "h1" => 1,
48            "h2" => 2,
49            "h3" => 3,
50            "h4" => 4,
51            "h5" => 5,
52            "h6" => 6,
53            _ => self.level(),
54        }
55    }
56}
57
58#[cfg(test)]
59mod tests {
60
61    use crate::dom::document::Document;
62    use crate::dom::traits::Element;
63
64    #[test]
65    fn heading_level_h1_through_h6() {
66        let doc = Document::new();
67        for level in 1..=6u8 {
68            let h = doc.create_heading(level);
69            assert_eq!(
70                h.heading_level(),
71                level,
72                "heading_level() wrong for h{level}"
73            );
74        }
75    }
76
77    #[test]
78    fn heading_tag_name_matches_level() {
79        let doc = Document::new();
80        let expected_tags = ["h1", "h2", "h3", "h4", "h5", "h6"];
81        for (i, expected) in expected_tags.iter().enumerate() {
82            let h = doc.create_heading((i as u8) + 1);
83            assert_eq!(h.tag_name(), *expected);
84        }
85    }
86
87    #[test]
88    fn heading_level_data_prop() {
89        let doc = Document::new();
90        let h3 = doc.create_heading(3);
91        // The level data prop is set by create_heading.
92        assert_eq!(h3.level(), 3);
93    }
94}