stratum_components/utility/
focus_scope.rs1use crate::common::merge_classes;
4use stratum_core::render::RenderOutput;
5
6#[derive(Debug, Clone, PartialEq, Default)]
8pub struct FocusScopeProps {
9 pub trapped: bool,
11 pub auto_focus: bool,
13 pub class: Option<String>,
14}
15
16pub struct FocusScope;
17
18impl FocusScope {
19 pub fn classes(props: &FocusScopeProps) -> String {
20 merge_classes("", &props.class)
21 }
22
23 pub fn render(props: &FocusScopeProps) -> RenderOutput {
24 let mut output = RenderOutput::new()
25 .with_tag("div")
26 .with_class(Self::classes(props));
27
28 if props.trapped {
29 output = output.with_data("focus-trap", "true");
30 }
31 if props.auto_focus {
32 output = output.with_data("auto-focus", "true");
33 }
34
35 output
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn focus_scope_default() {
45 let props = FocusScopeProps::default();
46 let output = FocusScope::render(&props);
47 assert_eq!(output.effective_tag(), "div");
48 assert!(output.data_attrs.is_empty());
49 }
50
51 #[test]
52 fn focus_scope_trapped() {
53 let props = FocusScopeProps {
54 trapped: true,
55 ..Default::default()
56 };
57 let output = FocusScope::render(&props);
58 assert!(
59 output
60 .data_attrs
61 .iter()
62 .any(|(k, v)| k == "focus-trap" && v == "true")
63 );
64 }
65
66 #[test]
67 fn focus_scope_auto_focus() {
68 let props = FocusScopeProps {
69 auto_focus: true,
70 ..Default::default()
71 };
72 let output = FocusScope::render(&props);
73 assert!(
74 output
75 .data_attrs
76 .iter()
77 .any(|(k, v)| k == "auto-focus" && v == "true")
78 );
79 }
80}