telegram_webapp_sdk/dom/
document.rs1use wasm_bindgen::JsValue;
5use web_sys::{Element, HtmlElement};
6
7pub struct Doc;
13
14impl Doc {
15 pub fn get_element_by_id(&self, id: &str) -> Option<Element> {
18 web_sys::window()?.document()?.get_element_by_id(id)
19 }
20
21 pub fn query_selector(&self, selector: &str) -> Result<Option<Element>, JsValue> {
26 web_sys::window()
27 .and_then(|w| w.document())
28 .ok_or_else(|| JsValue::from_str("document not available"))?
29 .query_selector(selector)
30 }
31
32 pub fn create_element(&self, tag: &str) -> Result<Element, JsValue> {
36 web_sys::window()
37 .ok_or_else(|| JsValue::from_str("window not available"))?
38 .document()
39 .ok_or_else(|| JsValue::from_str("document not available"))?
40 .create_element(tag)
41 }
42
43 pub fn body(&self) -> Result<HtmlElement, JsValue> {
47 web_sys::window()
48 .ok_or_else(|| JsValue::from_str("window not available"))?
49 .document()
50 .ok_or_else(|| JsValue::from_str("document not available"))?
51 .body()
52 .ok_or_else(|| JsValue::from_str("body not available"))
53 }
54}
55
56impl Default for Doc {
57 fn default() -> Self {
58 Self
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use wasm_bindgen::JsCast;
65 use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};
66 use web_sys::Element;
67
68 use super::*;
69 use crate::dom::ElementExt;
70
71 wasm_bindgen_test_configure!(run_in_browser);
72
73 fn fresh_root(id: &str) -> Element {
74 let doc = Doc;
75 let body = doc.body().expect("body");
76 let container = doc.create_element("div").expect("create container");
77 container.set_id(id);
78 body.append_child(&container).expect("append container");
79 container
80 }
81
82 fn cleanup(root: &Element) {
83 root.remove();
84 }
85
86 #[wasm_bindgen_test]
87 #[allow(dead_code, clippy::unused_unit)]
88 fn create_element_returns_tagged() {
89 let el = Doc.create_element("section").expect("create");
90 assert_eq!(el.tag_name().to_ascii_lowercase(), "section");
91 }
92
93 #[wasm_bindgen_test]
94 #[allow(dead_code, clippy::unused_unit)]
95 fn get_element_by_id_finds_existing_and_misses() {
96 let root = fresh_root("doc-test-get-by-id");
97 assert!(Doc.get_element_by_id("doc-test-get-by-id").is_some());
98 assert!(Doc.get_element_by_id("doc-test-absent").is_none());
99 cleanup(&root);
100 }
101
102 #[wasm_bindgen_test]
103 #[allow(dead_code, clippy::unused_unit)]
104 fn query_selector_finds_and_misses() {
105 let root = fresh_root("doc-test-qs");
106 let child = Doc.create_element("span").expect("span");
107 child.set_class("hit");
108 root.append_child(&child).expect("append");
109
110 let found = Doc.query_selector("#doc-test-qs .hit").expect("ok");
111 assert!(found.is_some());
112 let absent = Doc.query_selector("#doc-test-qs .nope").expect("ok");
113 assert!(absent.is_none());
114
115 cleanup(&root);
116 }
117
118 #[wasm_bindgen_test]
119 #[allow(dead_code, clippy::unused_unit)]
120 fn query_selector_invalid_returns_err() {
121 assert!(Doc.query_selector(">>>broken<<<").is_err());
122 }
123
124 #[wasm_bindgen_test]
125 #[allow(dead_code, clippy::unused_unit)]
126 fn body_returns_html_element() {
127 let body = Doc.body().expect("body");
128 let as_el: &Element = body.unchecked_ref();
129 assert_eq!(as_el.tag_name().to_ascii_lowercase(), "body");
130 }
131}