use crate::components::tabs::Alignment;
use crate::util::TestAttr;
use leptos::prelude::{
AriaAttributes, Children, ClassAttribute, CustomAttribute, ElementChild, Get, IntoView, Signal,
component, view,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BreadcrumbSize {
Small,
Medium,
Large,
}
impl BreadcrumbSize {
fn bulma(self) -> &'static str {
match self {
BreadcrumbSize::Small => "is-small",
BreadcrumbSize::Medium => "is-medium",
BreadcrumbSize::Large => "is-large",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BreadcrumbSeparator {
Arrow,
Bullet,
Dot,
Succeeds,
}
impl BreadcrumbSeparator {
fn bulma(self) -> &'static str {
match self {
BreadcrumbSeparator::Arrow => "has-arrow-separator",
BreadcrumbSeparator::Bullet => "has-bullet-separator",
BreadcrumbSeparator::Dot => "has-dot-separator",
BreadcrumbSeparator::Succeeds => "has-succeeds-separator",
}
}
}
#[component]
pub fn Breadcrumb(
children: Children,
#[prop(optional, into)]
classes: Signal<String>,
#[prop(optional, into)]
size: Signal<Option<BreadcrumbSize>>,
#[prop(optional, into)]
alignment: Signal<Option<Alignment>>,
#[prop(optional, into)]
separator: Signal<Option<BreadcrumbSeparator>>,
#[prop(optional, into)]
test_attr: Option<TestAttr>,
) -> impl IntoView {
let class = {
let classes = classes.clone();
let size = size.clone();
let alignment = alignment.clone();
let separator = separator.clone();
move || {
let mut parts = vec!["breadcrumb".to_string()];
let extra = classes.get();
if !extra.trim().is_empty() {
parts.push(extra);
}
if let Some(sz) = size.get() {
parts.push(sz.bulma().to_string());
}
if let Some(align) = alignment.get() {
parts.push(match align {
Alignment::Centered => "is-centered".to_string(),
Alignment::Right => "is-right".to_string(),
});
}
if let Some(sep) = separator.get() {
parts.push(sep.bulma().to_string());
}
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! {
<nav
class=move || class()
aria-label="breadcrumbs"
attr:data-testid=move || data_testid.clone()
attr:data-cy=move || data_cy.clone()
>
<ul>
{children()}
</ul>
</nav>
}
}
#[cfg(test)]
mod tests {
use super::*;
use leptos::prelude::RenderHtml;
#[test]
fn breadcrumb_renders_base_and_children() {
let html = view! {
<Breadcrumb>
<li><a href="#">"Bulma"</a></li>
<li class="is-active"><a href="#" aria-current="page">"Breadcrumb"</a></li>
</Breadcrumb>
}
.to_html();
assert!(
html.contains(r#"class="breadcrumb""#),
"expected base 'breadcrumb' class; got: {}",
html
);
assert!(html.contains("<ul"), "expected inner list; got: {}", html);
assert!(
html.contains("Bulma") && html.contains("Breadcrumb"),
"expected children; got: {}",
html
);
}
#[test]
fn breadcrumb_size_alignment_separator_classes() {
let html = view! {
<Breadcrumb
size=leptos::prelude::Signal::derive(|| Some(BreadcrumbSize::Small))
alignment=leptos::prelude::Signal::derive(|| Some(Alignment::Right))
separator=leptos::prelude::Signal::derive(|| Some(BreadcrumbSeparator::Dot))
classes="extra"
>
<li><a href="#">"A"</a></li>
<li class="is-active"><a href="#" aria-current="page">"B"</a></li>
</Breadcrumb>
}
.to_html();
assert!(
html.contains("breadcrumb extra"),
"expected extra classes; got: {}",
html
);
assert!(
html.contains("is-small"),
"expected size class; got: {}",
html
);
assert!(
html.contains("is-right"),
"expected alignment class; got: {}",
html
);
assert!(
html.contains("has-dot-separator"),
"expected separator class; got: {}",
html
);
}
}
#[cfg(all(test, target_arch = "wasm32"))]
mod wasm_tests {
use super::*;
use crate::components::tabs::Alignment;
use crate::util::TestAttr;
use leptos::prelude::*;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn breadcrumb_renders_test_attr_as_data_testid() {
let html = view! {
<Breadcrumb
classes="extra"
size=Signal::derive(|| Some(BreadcrumbSize::Small))
alignment=Signal::derive(|| Some(Alignment::Centered))
separator=Signal::derive(|| Some(BreadcrumbSeparator::Arrow))
test_attr="breadcrumb-test"
>
<li><a href="#">"A"</a></li>
</Breadcrumb>
}
.to_html();
assert!(
html.contains(r#"data-testid="breadcrumb-test""#),
"expected data-testid attribute; got: {}",
html
);
}
#[wasm_bindgen_test]
fn breadcrumb_no_test_attr_when_not_provided() {
let html = view! {
<Breadcrumb>
<li><a href="#">"A"</a></li>
</Breadcrumb>
}
.to_html();
assert!(
!html.contains("data-testid") && !html.contains("data-cy"),
"expected no test attribute; got: {}",
html
);
}
}