htmx_components/server/
html_element.rs

1use super::opt_attrs::opt_attrs;
2use rscx::{component, props};
3use std::collections::HashMap;
4use rscx_web_macros::*;
5
6#[html_element]
7pub struct HtmlElementProps {
8    #[builder(default)]
9    children: String,
10
11    #[builder(setter(into), default=String::from("HtmlElement"))]
12    component_name: String,
13
14    #[builder(setter(into), default=String::from("div"))]
15    tag: String,
16}
17
18#[component]
19pub fn HtmlElement(props: HtmlElementProps) -> String {
20    let attrs = opt_attrs(
21        HashMap::from([("data-rsx", props.component_name.clone())])
22            .into_iter()
23            .chain(props.html_attrs_to_hashmap())
24            .collect::<HashMap<&str, String>>(),
25    );
26
27    format!(
28        "<{} {}>{}</{}>",
29        props.tag, attrs, props.children, props.tag
30    )
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use crate::server::attrs::Attrs;
37    use rscx::html;
38
39    #[tokio::test]
40    async fn test_with_no_attrs() {
41        let html = html! {
42            <HtmlElement />
43        };
44
45        assert_eq!(html, String::from("<div data-rsx=\"HtmlElement\"></div>"));
46    }
47
48    #[tokio::test]
49    async fn test_with_tag_set() {
50        let html = html! {
51            <HtmlElement tag="button" />
52        };
53
54        assert_eq!(
55            html,
56            String::from("<button data-rsx=\"HtmlElement\"></button>")
57        );
58    }
59
60    #[tokio::test]
61    async fn test_with_children() {
62        let html = html! {
63            <HtmlElement tag="button">
64                <p>Paragraph text.</p>
65            </HtmlElement>
66        };
67
68        assert_eq!(
69            html,
70            String::from("<button data-rsx=\"HtmlElement\"><p>Paragraph text.</p></button>")
71        );
72    }
73
74    #[tokio::test]
75    async fn test_with_data_attributes() {
76        let html = html! {
77            <HtmlElement
78                tag="button"
79                attrs=Attrs::with("data-foo", "baz".into())
80            >
81                <h1>Header text.</h1>
82            </HtmlElement>
83        };
84
85        assert_eq!(
86            html,
87            String::from(
88                "<button data-foo=\"baz\" data-rsx=\"HtmlElement\"><h1>Header text.</h1></button>"
89            )
90        );
91    }
92
93    #[tokio::test]
94    async fn test_with_attrs_with_omit() {
95        // Emulate usage by a component
96        // Common use case: we want to apply the `class` attribute (or any attribute) manually
97        // Then pass the right of the props, omitting `class`.
98        let built_props = HtmlElementProps::builder();
99        let outer_props = built_props
100            .class("THIS_CLASS_SHOULD_BE_OMITTED")
101            .id("set-id")
102            .role("set-role")
103            .build();
104
105        let html = html! {
106            <HtmlElement
107                class="hard-coded-class"
108                attrs=Attrs::from(outer_props).omit(vec!["class"])
109            >
110                What an awesome element!
111            </HtmlElement>
112        };
113
114        assert_eq!(
115            html,
116            String::from(
117                "<div class=\"hard-coded-class\" data-rsx=\"HtmlElement\" id=\"set-id\" role=\"set-role\">What an awesome element!</div>"
118            )
119        );
120    }
121}