Skip to main content

yew_nav_link/components/
tabs.rs

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