Skip to main content

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}