Skip to main content

yew_nav_link/components/
tab_panel.rs

1//! # `NavTabPanel`
2//!
3//! Content panel associated with a [`NavTab`](super::NavTab).
4//! Renders a `<div>` with `role="tabpanel"` and `aria-labelledby`.
5//!
6//! # Example
7//!
8//! ```rust
9//! use yew::prelude::*;
10//! use yew_nav_link::components::NavTabPanel;
11//!
12//! #[component]
13//! fn Panels() -> Html {
14//!     html! {
15//!         <>
16//!             <NavTabPanel id="panel-1" labelled_by="tab-1" hidden={false}>
17//!                 <p>{ "Panel 1 content" }</p>
18//!             </NavTabPanel>
19//!             <NavTabPanel id="panel-2" labelled_by="tab-2" hidden={true}>
20//!                 <p>{ "Panel 2 content" }</p>
21//!             </NavTabPanel>
22//!         </>
23//!     }
24//! }
25//! ```
26//!
27//! # CSS Classes
28//!
29//! | Class | Condition |
30//! |-------|-----------|
31//! | `nav-tab-panel` | Always applied |
32//!
33//! # Props
34//!
35//! | Prop | Type | Default | Description |
36//! |------|------|---------|-------------|
37//! | `hidden` | `bool` | — | Whether the panel is hidden (required) |
38//! | `id` | `Option<&'static str>` | `None` | Panel element id |
39//! | `labelled_by` | `Option<&'static str>` | `None` | aria-labelledby target |
40//! | `classes` | `Classes` | — | Additional CSS classes |
41//! | `children` | `Children` | — | Panel content |
42
43use yew::prelude::*;
44
45/// Properties for the [`NavTabPanel`] component.
46///
47/// | Prop | Type | Default | Description |
48/// |------|------|---------|-------------|
49/// | `hidden` | `bool` | — | Whether the panel is hidden (required) |
50/// | `id` | `Option<&'static str>` | `None` | Panel element id |
51/// | `labelled_by` | `Option<&'static str>` | `None` | aria-labelledby target |
52/// | `classes` | `Classes` | — | Additional CSS classes |
53/// | `children` | `Children` | — | Panel content |
54#[derive(Properties, Clone, PartialEq, Debug)]
55pub struct NavTabPanelProps {
56    /// Additional CSS classes applied to the panel.
57    #[prop_or_default]
58    pub classes: Classes,
59
60    /// Optional `id` attribute for the panel element.
61    #[prop_or_default]
62    pub id: Option<&'static str>,
63
64    /// Optional `aria-labelledby` referencing the tab button `id`.
65    #[prop_or_default]
66    pub labelled_by: Option<&'static str>,
67
68    /// Whether the panel is hidden.
69    pub hidden: bool,
70
71    /// Content rendered inside the panel.
72    pub children: Children
73}
74
75/// Tab panel component that holds the content for a single tab.
76///
77/// Renders a `<div>` with `role="tabpanel"`.
78///
79/// # CSS Classes
80///
81/// - `nav-tab-panel` - Always applied
82#[function_component]
83pub fn NavTabPanel(props: &NavTabPanelProps) -> Html {
84    let mut classes = props.classes.clone();
85    classes.push("nav-tab-panel");
86
87    html! {
88        <div
89            {classes}
90            id={props.id}
91            role="tabpanel"
92            aria-labelledby={props.labelled_by}
93            hidden={props.hidden}
94        >
95            { for props.children.iter() }
96        </div>
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn nav_tab_panel_hidden() {
106        let props = NavTabPanelProps {
107            classes:     Classes::default(),
108            id:          Some("panel-1"),
109            labelled_by: Some("tab-1"),
110            hidden:      true,
111            children:    Children::new(vec![])
112        };
113
114        assert!(props.hidden);
115        assert_eq!(props.id, Some("panel-1"));
116    }
117
118    #[test]
119    fn nav_tab_panel_visible() {
120        let props = NavTabPanelProps {
121            classes:     Classes::default(),
122            id:          Some("panel-1"),
123            labelled_by: Some("tab-1"),
124            hidden:      false,
125            children:    Children::new(vec![])
126        };
127
128        assert!(!props.hidden);
129    }
130}