freya_components/
checkbox.rs1use dioxus::prelude::*;
2use freya_elements::{
3 self as dioxus_elements,
4 events::KeyboardEvent,
5};
6use freya_hooks::{
7 use_applied_theme,
8 use_focus,
9 CheckboxTheme,
10 CheckboxThemeWith,
11};
12
13use crate::TickIcon;
14
15#[cfg_attr(feature = "docs",
83 doc = embed_doc_image::embed_image!("checkbox", "images/gallery_checkbox.png")
84)]
85#[allow(non_snake_case)]
86#[component]
87pub fn Checkbox(
88 selected: bool,
90 theme: Option<CheckboxThemeWith>,
92) -> Element {
93 let focus = use_focus();
94 let CheckboxTheme {
95 border_fill,
96 unselected_fill,
97 selected_fill,
98 selected_icon_fill,
99 } = use_applied_theme!(&theme, checkbox);
100 let (inner_fill, outer_fill) = if selected {
101 (selected_fill.as_ref(), selected_fill.as_ref())
102 } else {
103 ("transparent", unselected_fill.as_ref())
104 };
105 let border = if focus.is_focused_with_keyboard() {
106 format!("2 inner {outer_fill}, 4 outer {border_fill}")
107 } else {
108 format!("2 inner {outer_fill}")
109 };
110
111 let onkeydown = move |e: KeyboardEvent| {
112 if !focus.validate_keydown(&e) {
113 e.stop_propagation();
114 }
115 };
116
117 rsx!(
118 rect {
119 a11y_id: focus.attribute(),
120 width: "18",
121 height: "18",
122 padding: "4",
123 main_align: "center",
124 cross_align: "center",
125 corner_radius: "4",
126 border,
127 background: "{inner_fill}",
128 onkeydown,
129 if selected {
130 TickIcon {
131 fill: selected_icon_fill
132 }
133 }
134 }
135 )
136}
137
138#[cfg(test)]
139mod test {
140 use std::collections::HashSet;
141
142 use dioxus::prelude::use_signal;
143 use freya::prelude::*;
144 use freya_testing::prelude::*;
145
146 #[tokio::test]
147 pub async fn checkbox() {
148 #[derive(PartialEq, Eq, Hash)]
149 enum Choice {
150 First,
151 Second,
152 Third,
153 }
154
155 fn checkbox_app() -> Element {
156 let mut selected = use_signal::<HashSet<Choice>>(HashSet::default);
157
158 rsx!(
159 Tile {
160 onselect: move |_| {
161 if selected.read().contains(&Choice::First) {
162 selected.write().remove(&Choice::First);
163 } else {
164 selected.write().insert(Choice::First);
165 }
166 },
167 leading: rsx!(
168 Checkbox {
169 selected: selected.read().contains(&Choice::First),
170 }
171 ),
172 label { "First choice" }
173 }
174 Tile {
175 onselect: move |_| {
176 if selected.read().contains(&Choice::Second) {
177 selected.write().remove(&Choice::Second);
178 } else {
179 selected.write().insert(Choice::Second);
180 }
181 },
182 leading: rsx!(
183 Checkbox {
184 selected: selected.read().contains(&Choice::Second),
185 }
186 ),
187 label { "Second choice" }
188 }
189 Tile {
190 onselect: move |_| {
191 if selected.read().contains(&Choice::Third) {
192 selected.write().remove(&Choice::Third);
193 } else {
194 selected.write().insert(Choice::Third);
195 }
196 },
197 leading: rsx!(
198 Checkbox {
199 selected: selected.read().contains(&Choice::Third),
200 }
201 ),
202 label { "Third choice" }
203 }
204 )
205 }
206
207 let mut utils = launch_test(checkbox_app);
208 let root = utils.root();
209 utils.wait_for_update().await;
210
211 assert!(root.get(0).get(0).get(0).get(0).is_placeholder());
213 assert!(root.get(1).get(0).get(0).get(0).is_placeholder());
214 assert!(root.get(2).get(0).get(0).get(0).is_placeholder());
215
216 utils.click_cursor((20., 50.)).await;
217
218 assert!(root.get(0).get(0).get(0).get(0).is_placeholder());
219 assert!(root.get(1).get(0).get(0).get(0).is_element());
220 assert!(root.get(2).get(0).get(0).get(0).is_placeholder());
221
222 utils.click_cursor((10., 90.)).await;
223 utils.wait_for_update().await;
224
225 assert!(root.get(0).get(0).get(0).get(0).is_placeholder());
226 assert!(root.get(1).get(0).get(0).get(0).is_element());
227 assert!(root.get(2).get(0).get(0).get(0).is_element());
228
229 utils.click_cursor((10., 10.)).await;
230 utils.click_cursor((10., 50.)).await;
231 utils.wait_for_update().await;
232
233 assert!(root.get(0).get(0).get(0).get(0).is_element());
234 assert!(root.get(1).get(0).get(0).get(0).is_placeholder());
235 assert!(root.get(2).get(0).get(0).get(0).is_element());
236 }
237}