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}