use leptos::children::Children;
use leptos::prelude::{ClassAttribute, CustomAttribute, ElementChild, Get, Signal};
use leptos::{IntoView, component, view};
use crate::util::{Size, TestAttr};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TagColor {
Black,
Danger,
Dark,
Info,
Light,
Link,
Primary,
Success,
Warning,
White,
}
impl TagColor {
fn bulma(self) -> &'static str {
match self {
TagColor::Black => "is-black",
TagColor::Danger => "is-danger",
TagColor::Dark => "is-dark",
TagColor::Info => "is-info",
TagColor::Light => "is-light",
TagColor::Link => "is-link",
TagColor::Primary => "is-primary",
TagColor::Success => "is-success",
TagColor::Warning => "is-warning",
TagColor::White => "is-white",
}
}
}
#[component]
pub fn Tag(
#[prop(optional)]
color: Option<TagColor>,
#[prop(optional)]
size: Option<Size>,
#[prop(optional)]
light: bool,
#[prop(optional)]
rounded: bool,
#[prop(optional, into)]
classes: Option<Signal<String>>,
#[prop(optional, into)]
test_attr: Option<TestAttr>,
children: Children,
) -> impl IntoView {
let class = move || {
let mut class_parts: Vec<&str> = vec!["tag"];
if let Some(color_value) = color {
class_parts.push(color_value.bulma());
}
if let Some(size_value) = size {
let size_class = size_value.bulma();
if !size_class.is_empty() {
class_parts.push(size_class);
}
}
if light {
class_parts.push("is-light");
}
if rounded {
class_parts.push("is-rounded");
}
if let Some(class_signal) = &classes {
let extra_classes = class_signal.get();
if !extra_classes.is_empty() {
return format!("{} {}", class_parts.join(" "), extra_classes);
}
}
class_parts.join(" ")
};
let (data_testid, data_cy) = match &test_attr {
Some(attr) if attr.key == "data-testid" => (Some(attr.value.clone()), None),
Some(attr) if attr.key == "data-cy" => (None, Some(attr.value.clone())),
_ => (None, None),
};
view! {
<span
class=class
attr:data-testid=move || data_testid.clone()
attr:data-cy=move || data_cy.clone()
>
{children()}
</span>
}
}
#[cfg(test)]
mod tests {
use super::*;
use leptos::prelude::RenderHtml;
#[test]
fn tag_renders_basic() {
let html = view! { <Tag>"Hi"</Tag> }.to_html();
assert!(html.contains(r#"class="tag""#), "expected base tag class");
assert!(html.contains(">Hi<"), "expected tag content");
}
#[test]
fn tag_color_and_rounded() {
let html = view! { <Tag color=TagColor::Success rounded=true>"OK"</Tag> }.to_html();
assert!(
html.contains(r#"class="tag is-success is-rounded""#),
"expected color and rounded classes"
);
}
#[test]
fn tag_renders_test_id() {
let html = view! {
<Tag test_attr=TestAttr::test_id("tag-test")>"Content"</Tag>
}
.to_html();
assert!(
html.contains(r#"data-testid="tag-test""#),
"expected data-testid attribute; got: {}",
html
);
}
#[test]
fn tag_no_test_attr_when_not_provided() {
let html = view! {
<Tag>"Content"</Tag>
}
.to_html();
assert!(
!html.contains("data-testid") && !html.contains("data-cy"),
"expected no test attribute; got: {}",
html
);
}
#[test]
fn tag_accepts_custom_test_attr_key() {
let html = view! {
<Tag test_attr=TestAttr::new("data-cy", "tag-cy")>"Content"</Tag>
}
.to_html();
assert!(
html.contains(r#"data-cy="tag-cy""#),
"expected custom data-cy attribute; got: {}",
html
);
}
}
#[cfg(all(test, target_arch = "wasm32"))]
mod wasm_tests {
}