radix_leptos_primitives/components/
scroll_area.rs

1use crate::utils::merge_classes;
2use leptos::children::Children;
3use leptos::prelude::*;
4
5/// Scroll Area component for custom scrollable areas
6///
7/// Provides accessible scroll area with custom scrollbar styling
8#[component]
9pub fn ScrollArea(
10    #[prop(optional)] class: Option<String>,
11    #[prop(optional)] style: Option<String>,
12    #[prop(optional)] children: Option<Children>,
13    #[prop(optional)] orientation: Option<ScrollAreaOrientation>,
14    #[prop(optional)] scroll_hidden: Option<bool>,
15) -> impl IntoView {
16    let orientation = orientation.unwrap_or_default();
17    let scroll_hidden = scroll_hidden.unwrap_or(false);
18
19    let class = merge_classes(vec![
20        "scroll-area",
21        &orientation.to_class(),
22        class.as_deref().unwrap_or(""),
23    ]);
24
25    view! {
26        <div
27            class=class
28            style=style
29            data-orientation=orientation.to_aria()
30        >
31            {children.map(|c| c())}
32        </div>
33    }
34}
35
36/// Scroll Area Viewport component
37#[component]
38pub fn ScrollAreaViewport(
39    #[prop(optional)] class: Option<String>,
40    #[prop(optional)] style: Option<String>,
41    #[prop(optional)] children: Option<Children>,
42) -> impl IntoView {
43    let class = merge_classes(vec!["scroll-area-viewport", class.as_deref().unwrap_or("")]);
44
45    view! {
46        <div
47            class=class
48            style=style
49        >
50            {children.map(|c| c())}
51        </div>
52    }
53}
54
55/// Scroll Area Scrollbar component
56#[component]
57pub fn ScrollAreaScrollbar(
58    #[prop(optional)] class: Option<String>,
59    #[prop(optional)] style: Option<String>,
60    #[prop(optional)] children: Option<Children>,
61    #[prop(optional)] orientation: Option<ScrollAreaOrientation>,
62    #[prop(optional)] force_mount: Option<bool>,
63) -> impl IntoView {
64    let orientation = orientation.unwrap_or_default();
65    let force_mount = force_mount.unwrap_or(false);
66
67    let class = merge_classes(vec![
68        "scroll-area-scrollbar",
69        &orientation.to_class(),
70        class.as_deref().unwrap_or(""),
71    ]);
72
73    view! {
74        <div
75            class=class
76            style=style
77            data-orientation=orientation.to_aria()
78        >
79            {children.map(|c| c())}
80        </div>
81    }
82}
83
84/// Scroll Area Thumb component
85#[component]
86pub fn ScrollAreaThumb(
87    #[prop(optional)] class: Option<String>,
88    #[prop(optional)] style: Option<String>,
89) -> impl IntoView {
90    let class = merge_classes(vec!["scroll-area-thumb", class.as_deref().unwrap_or("")]);
91
92    view! {
93        <div
94            class=class
95            style=style
96        />
97    }
98}
99
100/// Scroll Area Corner component
101#[component]
102pub fn ScrollAreaCorner(
103    #[prop(optional)] class: Option<String>,
104    #[prop(optional)] style: Option<String>,
105) -> impl IntoView {
106    let class = merge_classes(vec!["scroll-area-corner", class.as_deref().unwrap_or("")]);
107
108    view! {
109        <div
110            class=class
111            style=style
112        />
113    }
114}
115
116/// Scroll Area Orientation enum
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
118pub enum ScrollAreaOrientation {
119    #[default]
120    Vertical,
121    Horizontal,
122    Both,
123}
124
125impl ScrollAreaOrientation {
126    pub fn to_class(&self) -> &'static str {
127        match self {
128            ScrollAreaOrientation::Vertical => "vertical",
129            ScrollAreaOrientation::Horizontal => "horizontal",
130            ScrollAreaOrientation::Both => "both",
131        }
132    }
133
134    pub fn to_aria(&self) -> &'static str {
135        match self {
136            ScrollAreaOrientation::Vertical => "vertical",
137            ScrollAreaOrientation::Horizontal => "horizontal",
138            ScrollAreaOrientation::Both => "both",
139        }
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use crate::utils::merge_classes;
146    use crate::ScrollAreaOrientation;
147    use wasm_bindgen_test::*;
148
149    wasm_bindgen_test_configure!(run_in_browser);
150
151    // Scroll Area Tests
152    #[test]
153    fn test_scroll_area_creation() {}
154
155    #[test]
156    fn test_scroll_area_with_class() {}
157
158    #[test]
159    fn test_scroll_area_with_style() {}
160
161    #[test]
162    fn test_scroll_area_vertical_orientation() {}
163
164    #[test]
165    fn test_scroll_area_horizontal_orientation() {}
166
167    #[test]
168    fn test_scroll_area_both_orientation() {}
169
170    #[test]
171    fn test_scroll_area_scroll_hidden() {}
172
173    // Scroll Area Viewport Tests
174    #[test]
175    fn test_scroll_area_viewport_creation() {}
176
177    #[test]
178    fn test_scroll_area_viewport_with_class() {}
179
180    #[test]
181    fn test_scroll_area_viewport_with_style() {}
182
183    // Scroll Area Scrollbar Tests
184    #[test]
185    fn test_scroll_area_scrollbar_creation() {}
186
187    #[test]
188    fn test_scroll_area_scrollbar_with_class() {}
189
190    #[test]
191    fn test_scroll_area_scrollbar_with_style() {}
192
193    #[test]
194    fn test_scroll_area_scrollbar_vertical_orientation() {}
195
196    #[test]
197    fn test_scroll_area_scrollbar_horizontal_orientation() {}
198
199    #[test]
200    fn test_scroll_area_scrollbar_both_orientation() {}
201
202    #[test]
203    fn test_scroll_area_scrollbar_force_mount() {}
204
205    // Scroll Area Thumb Tests
206    #[test]
207    fn test_scroll_area_thumb_creation() {}
208
209    #[test]
210    fn test_scroll_area_thumb_with_class() {}
211
212    #[test]
213    fn test_scroll_area_thumb_with_style() {}
214
215    // Scroll Area Corner Tests
216    #[test]
217    fn test_scroll_area_corner_creation() {}
218
219    #[test]
220    fn test_scroll_area_corner_with_class() {}
221
222    #[test]
223    fn test_scroll_area_corner_with_style() {}
224
225    // Scroll Area Orientation Tests
226    #[test]
227    fn test_scroll_area_orientation_default() {
228        let orientation = ScrollAreaOrientation::default();
229        assert_eq!(orientation, ScrollAreaOrientation::Vertical);
230    }
231
232    #[test]
233    fn test_scroll_area_orientation_vertical() {
234        let orientation = ScrollAreaOrientation::Vertical;
235        assert_eq!(orientation.to_class(), "vertical");
236        assert_eq!(orientation.to_aria(), "vertical");
237    }
238
239    #[test]
240    fn test_scroll_area_orientation_horizontal() {
241        let orientation = ScrollAreaOrientation::Horizontal;
242        assert_eq!(orientation.to_class(), "horizontal");
243        assert_eq!(orientation.to_aria(), "horizontal");
244    }
245
246    #[test]
247    fn test_scroll_area_orientation_both() {
248        let orientation = ScrollAreaOrientation::Both;
249        assert_eq!(orientation.to_class(), "both");
250        assert_eq!(orientation.to_aria(), "both");
251    }
252
253    // Helper Function Tests
254    #[test]
255    fn test_merge_classes_empty() {
256        let result = merge_classes(Vec::new());
257        assert_eq!(result, "");
258    }
259
260    #[test]
261    fn test_merge_classes_single() {
262        let result = merge_classes(vec!["class1"]);
263        assert_eq!(result, "class1");
264    }
265
266    #[test]
267    fn test_merge_classes_multiple() {
268        let result = merge_classes(vec!["class1", "class2", "class3"]);
269        assert_eq!(result, "class1 class2 class3");
270    }
271
272    #[test]
273    fn test_merge_classes_with_empty() {
274        let result = merge_classes(vec!["class1", "", "class3"]);
275        assert_eq!(result, "class1 class3");
276    }
277
278    // Property-based tests
279    #[test]
280    fn test_scroll_area_property_based() {
281        use proptest::prelude::*;
282        proptest!(|(____class in ".*", __style in ".*")| {
283
284        });
285    }
286
287    #[test]
288    fn test_scroll_area_viewport_property_based() {
289        use proptest::prelude::*;
290        proptest!(|(____class in ".*", __style in ".*")| {
291
292        });
293    }
294
295    #[test]
296    fn test_scroll_area_scrollbar_property_based() {
297        use proptest::prelude::*;
298        proptest!(|(____class in ".*", __style in ".*")| {
299
300        });
301    }
302
303    #[test]
304    fn test_scroll_area_thumb_property_based() {
305        use proptest::prelude::*;
306        proptest!(|(____class in ".*", __style in ".*")| {
307
308        });
309    }
310
311    #[test]
312    fn test_scroll_area_corner_property_based() {
313        use proptest::prelude::*;
314        proptest!(|(____class in ".*", __style in ".*")| {
315
316        });
317    }
318}