| 18 |
18 |
|
//! Plus the overlay container, which is where an
|
| 19 |
19 |
|
//! [`Outcome::Over`](quasi_router::Outcome::Over) lands. It is emitted empty
|
| 20 |
20 |
|
//! and stays empty until something is drawn into it, so a document with chrome
|
| 21 |
|
- |
//! and no overlay open is a document with one spare `div` in it.
|
|
21 |
+ |
//! and no overlay open is a document with one spare `div` in it. That spare
|
|
22 |
+ |
//! `div` is the rule and not an edge case: any app declaring chrome gets one,
|
|
23 |
+ |
//! because a route can answer `Over` without a binding to open it, and
|
|
24 |
+ |
//! [`overlay_target`](quasi_http::Serves::overlay_target) promises this
|
|
25 |
+ |
//! renderer has a container to aim at whatever the app declared.
|
| 22 |
26 |
|
//!
|
| 23 |
27 |
|
//! # Interpreting a key name is this crate's job, not the description's
|
| 24 |
28 |
|
//!
|
| 59 |
63 |
|
for binding in &chrome.bindings {
|
| 60 |
64 |
|
binding_html(binding, out);
|
| 61 |
65 |
|
}
|
| 62 |
|
- |
// Emitted only alongside bindings, because a document with no way to open
|
| 63 |
|
- |
// an overlay has nothing to put in one. A host driving `Outcome::Over` from
|
| 64 |
|
- |
// somewhere other than a binding declares a binding for it. A panel is not
|
| 65 |
|
- |
// a way to open one: it is drawn, and what its controls call is a route.
|
| 66 |
|
- |
if !chrome.bindings.is_empty() {
|
|
66 |
+ |
// Emitted whenever the app declares any chrome, because a route can answer
|
|
67 |
+ |
// `Outcome::Over` without a binding being involved: a control on a screen
|
|
68 |
+ |
// calls a route, and the route answers with an overlay. Bindings were the
|
|
69 |
+ |
// wrong condition. They were the only way an overlay was opened when this
|
|
70 |
+ |
// was written, and goingson opens one from a row's control with no binding
|
|
71 |
+ |
// anywhere in its chrome, so the guard dropped the container out of exactly
|
|
72 |
+ |
// the document that needed it.
|
|
73 |
+ |
//
|
|
74 |
+ |
// Not unconditional, so an app declaring no chrome at all still gets the
|
|
75 |
+ |
// byte-for-byte document above.
|
|
76 |
+ |
//
|
|
77 |
+ |
// The comment rides in front rather than inside: an overlay swap replaces
|
|
78 |
+ |
// the container's contents, so a comment within it would be destroyed by
|
|
79 |
+ |
// the first `Over` and gone by the time anyone inspected the element.
|
|
80 |
+ |
if !chrome.bindings.is_empty() || chrome.panel.is_some() {
|
|
81 |
+ |
out.push_str("<!-- quasi: overlay container; an Outcome::Over lands here -->");
|
| 67 |
82 |
|
out.push_str("<div id=\"");
|
| 68 |
83 |
|
out.push_str(OVERLAY_ID);
|
| 69 |
|
- |
out.push_str("\"></div>");
|
|
84 |
+ |
out.push_str("\" data-chrome=\"overlay\"></div>");
|
| 70 |
85 |
|
}
|
| 71 |
86 |
|
if let Some(panel) = &chrome.panel {
|
| 72 |
87 |
|
panel_html(panel, opts, fills, out);
|
| 281 |
296 |
|
assert!(out.contains("id=\"timer\""), "{out}");
|
| 282 |
297 |
|
assert!(out.contains("chrome-panel"), "{out}");
|
| 283 |
298 |
|
assert!(out.contains("00:12:04"), "{out}");
|
| 284 |
|
- |
// No binding, so no way to open an overlay and nothing to put in one.
|
| 285 |
|
- |
assert!(!out.contains(OVERLAY_ID), "{out}");
|
|
299 |
+ |
// A panel and no bindings, which is goingson's chrome, and the case
|
|
300 |
+ |
// that used to lose the container: its Focus control calls a route
|
|
301 |
+ |
// that answers `Over`, and the retarget aimed at an element the
|
|
302 |
+ |
// document did not have.
|
|
303 |
+ |
assert!(out.contains(OVERLAY_ID), "{out}");
|
|
304 |
+ |
}
|
|
305 |
+ |
|
|
306 |
+ |
#[test]
|
|
307 |
+ |
fn the_overlay_container_says_what_it_is_and_says_it_outside_itself() {
|
|
308 |
+ |
use quasi_router::Node;
|
|
309 |
+ |
|
|
310 |
+ |
let chrome = Chrome::new().presenting("timer", Node::text("00:12:04"));
|
|
311 |
+ |
let mut out = String::new();
|
|
312 |
+ |
chrome_html(&chrome, &Emit::default(), &HashMap::new(), &mut out);
|
|
313 |
+ |
// The annotation is for whoever finds an empty div in devtools, so it
|
|
314 |
+ |
// has to survive the swap that fills the div. Before the element, not
|
|
315 |
+ |
// within it.
|
|
316 |
+ |
let comment = out.find("<!-- quasi: overlay").expect("annotated");
|
|
317 |
+ |
let container = out.find("<div id=\"quasi-overlay\"").expect("emitted");
|
|
318 |
+ |
assert!(comment < container, "{out}");
|
|
319 |
+ |
assert!(out.contains("data-chrome=\"overlay\""), "{out}");
|
| 286 |
320 |
|
}
|
| 287 |
321 |
|
|
| 288 |
322 |
|
#[test]
|
| 296 |
330 |
|
assert!(out.contains("hx-target=\"#quasi-overlay\""), "{out}");
|
| 297 |
331 |
|
assert!(out.contains("from:body"), "{out}");
|
| 298 |
332 |
|
assert!(out.contains("aria-label=\"Search\""), "{out}");
|
| 299 |
|
- |
assert!(out.contains("<div id=\"quasi-overlay\"></div>"), "{out}");
|
|
333 |
+ |
assert!(
|
|
334 |
+ |
out.contains("<div id=\"quasi-overlay\" data-chrome=\"overlay\"></div>"),
|
|
335 |
+ |
"{out}"
|
|
336 |
+ |
);
|
| 300 |
337 |
|
}
|
| 301 |
338 |
|
|
| 302 |
339 |
|
#[test]
|