yew_nav_link/components/tabs.rs
1//! # `NavTabs`
2//!
3//! Tab navigation container that wraps [`NavTab`](super::NavTab) items.
4//! Renders a `<ul>` with `role="tablist"` and optional full-width layout.
5//!
6//! # Example
7//!
8//! ```rust
9//! use yew::prelude::*;
10//! use yew_nav_link::components::{NavTab, NavTabs};
11//!
12//! #[component]
13//! fn TabBar() -> Html {
14//! html! {
15//! <NavTabs id="main-tabs">
16//! <NavTab active=true onclick={None}>{ "Tab 1" }</NavTab>
17//! <NavTab active=false onclick={None}>{ "Tab 2" }</NavTab>
18//! </NavTabs>
19//! }
20//! }
21//! ```
22//!
23//! # CSS Classes
24//!
25//! | Class | Condition |
26//! |-------|-----------|
27//! | `nav-tabs` | Always applied |
28//! | `nav-tabs-fill` | Applied when `full_width` is `true` |
29//!
30//! # Props
31//!
32//! | Prop | Type | Default | Description |
33//! |------|------|---------|-------------|
34//! | `full_width` | `bool` | `false` | Stretch tabs to fill width |
35//! | `role` | `&'static str` | `"tablist"` | ARIA role |
36//! | `id` | `Option<&'static str>` | `None` | Container id |
37//! | `classes` | `Classes` | — | Additional CSS classes |
38//! | `children` | `Children` | — | Tab items |
39
40use yew::prelude::*;
41
42/// Properties for the [`NavTabs`] component.
43///
44/// | Prop | Type | Default | Description |
45/// |------|------|---------|-------------|
46/// | `full_width` | `bool` | `false` | Stretch tabs to fill width |
47/// | `role` | `&'static str` | `"tablist"` | ARIA role |
48/// | `id` | `Option<&'static str>` | `None` | Container id |
49/// | `classes` | `Classes` | — | Additional CSS classes |
50/// | `children` | `Children` | — | Tab items |
51#[derive(Properties, Clone, PartialEq, Debug)]
52pub struct NavTabsProps {
53 /// Additional CSS classes applied to the tabs container.
54 #[prop_or_default]
55 pub classes: Classes,
56
57 /// ARIA role for the tab list. Defaults to `"tablist"`.
58 #[prop_or("tablist")]
59 pub role: &'static str,
60
61 /// Optional `id` attribute for the tabs container.
62 #[prop_or_default]
63 pub id: Option<&'static str>,
64
65 /// Whether tabs should stretch to fill the full width of the container.
66 #[prop_or_default]
67 pub full_width: bool,
68
69 /// Tab items rendered inside the container.
70 pub children: Children
71}
72
73/// Tab navigation container that wraps [`NavTab`](super::NavTab) items.
74///
75/// Renders a `<ul>` element with ARIA `role="tablist"`.
76///
77/// # CSS Classes
78///
79/// - `nav-tabs` - Always applied
80/// - `nav-tabs-fill` - Applied when `full_width` is `true`
81#[function_component]
82pub fn NavTabs(props: &NavTabsProps) -> Html {
83 let mut classes = props.classes.clone();
84 classes.push("nav-tabs");
85
86 if props.full_width {
87 classes.push("nav-tabs-fill");
88 }
89
90 html! {
91 <ul
92 {classes}
93 id={props.id}
94 role={props.role}
95 >
96 { for props.children.iter() }
97 </ul>
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn nav_tabs_props_default() {
107 let props = NavTabsProps {
108 classes: Classes::default(),
109 role: "tablist",
110 id: None,
111 full_width: false,
112 children: Children::new(vec![])
113 };
114
115 assert_eq!(props.role, "tablist");
116 assert!(!props.full_width);
117 }
118
119 #[test]
120 fn nav_tabs_full_width() {
121 let props = NavTabsProps {
122 classes: Classes::default(),
123 role: "tablist",
124 id: Some("main-tabs"),
125 full_width: true,
126 children: Children::new(vec![])
127 };
128
129 assert!(props.full_width);
130 assert_eq!(props.id, Some("main-tabs"));
131 }
132}