// Verifies that a `Navbar` with its content in a `navbar::Panel` renders a working side panel: the // toggle button points to the panel, the panel carries its own classes and attributes, and the // items of the navbar are its only content. use pagetop::prelude::*; use pagetop_bootsier::theme::*; fn cx() -> Context { Context::default().with_theme(&pagetop_bootsier::Bootsier) } fn panel(placement: bs::offcanvas::Placement) -> bs::navbar::Panel { bs::navbar::Panel::new().with_placement(placement) } fn home() -> bs::navbar::Item { bs::navbar::Item::nav(bs::Nav::new().with_item(bs::nav::Item::link(Lc::n("Home"), "/"))) } async fn render(navbar: Navbar) -> String { let mut navbar = navbar.with_item(home()); navbar.render(&mut cx()).await.into_string() } // The `id` the toggle button points to. fn target(html: &str) -> &str { html.split(r##"data-bs-target="#"##) .nth(1) .and_then(|rest| rest.split('"').next()) .unwrap_or_default() } #[pagetop::test] async fn offcanvas_button_points_to_the_panel_id() { let brand = || Brand::new().with_title(Lc::n("PageTop")); let start = bs::offcanvas::Placement::Start; let navbars = [ bs::Navbar::offcanvas(panel(start)), bs::Navbar::offcanvas_brand_left(brand(), panel(start)), bs::Navbar::offcanvas_brand_right(brand(), panel(start)), ]; for navbar in navbars { let html = render(navbar).await; // The panel is rendered by the navbar, which derives its `id` from its own. let target = target(&html); assert!(!target.is_empty(), "the button has no target in {html}"); assert!(html.contains(&format!(r#"aria-controls="{target}""#))); assert!( html.contains(&format!(r#"id="{target}""#)), "no panel with id `{target}` in {html}" ); } } #[pagetop::test] async fn offcanvas_panel_gets_its_own_classes() { for (placement, class) in [ (bs::offcanvas::Placement::Start, "offcanvas-start"), (bs::offcanvas::Placement::End, "offcanvas-end"), (bs::offcanvas::Placement::Top, "offcanvas-top"), (bs::offcanvas::Placement::Bottom, "offcanvas-bottom"), ] { let html = render(bs::Navbar::offcanvas(panel(placement))).await; // Without them Bootstrap would show the panel inline instead of hiding and placing it. assert!( html.contains(&format!(r#"class="offcanvas {class}""#)), "missing `offcanvas {class}` in {html}" ); } } #[pagetop::test] async fn offcanvas_panel_follows_the_expand_of_the_navbar() { let navbar = bs::Navbar::offcanvas(panel(bs::offcanvas::Placement::Start)); let html = render(navbar.with_expand(Breakpoint::Lg)).await; // Bootstrap shows the panel inline from `navbar-expand-lg`; the panel has no breakpoint of its // own that could contradict it, and it is not open on load. assert!(html.contains("navbar-expand-lg")); assert!(html.contains(r#"class="offcanvas offcanvas-start""#)); assert!(!html.contains("offcanvas-lg") && !html.contains("offcanvas-md")); } #[pagetop::test] async fn offcanvas_panel_title_labels_the_panel() { let start = bs::offcanvas::Placement::Start; let html = render(bs::Navbar::offcanvas( bs::navbar::Panel::new() .with_title(Lc::n("Main menu")) .with_placement(start), )) .await; let target = target(&html); assert!(html.contains(&format!( r#"