mirror of
https://github.com/lexmount/moli.git
synced 2026-11-04 16:06:44 +00:00
fix(forms): expose selectedcontent inner text without a box
This commit is contained in:
@@ -13,6 +13,7 @@ use super::super::{
|
||||
JsContextHost, document::set_detached_text_replacement_value,
|
||||
node::node_runtime_and_handle_from_object_or_detached, throw_dom_exception,
|
||||
};
|
||||
use super::geometry::observable_client_rects;
|
||||
use super::{
|
||||
html_element_getter_receiver, html_element_setter_receiver, observable_sources_with_fragments,
|
||||
property_string_value,
|
||||
@@ -817,6 +818,29 @@ fn node_inner_text(
|
||||
if rendered_state.box_state() == ElementBoxState::NoBox {
|
||||
return Ok(node.text_content(runtime.dom_host().dom()));
|
||||
}
|
||||
// The lightweight rendered-state path derives box presence from computed
|
||||
// display values. A selectedcontent element can still have no box when
|
||||
// its native select ancestor suppresses author children. Consult the
|
||||
// actual layout surface for this control-specific boundary so innerText
|
||||
// follows its required textContent fallback. Once a customizable select
|
||||
// produces real child boxes, its non-empty rect list keeps it on the
|
||||
// normal rendered-text path.
|
||||
if runtime
|
||||
.dom_host()
|
||||
.selectedcontent_nearest_ancestor_select(handle)
|
||||
.is_some()
|
||||
&& rendered_state
|
||||
.target_style()
|
||||
.is_some_and(|style| style.display != ComputedDisplayKind::Contents)
|
||||
&& observable_client_rects(
|
||||
runtime,
|
||||
handle,
|
||||
moli_layout::LayoutFlushReason::SynchronousGeometry,
|
||||
)?
|
||||
.is_empty()
|
||||
{
|
||||
return Ok(node.text_content(runtime.dom_host().dom()));
|
||||
}
|
||||
|
||||
let mut writer = InnerTextWriter::default();
|
||||
append_inner_text(
|
||||
|
||||
+2
-1
@@ -1446,6 +1446,7 @@ const selectedcontent = document.getElementById('selectedcontent');
|
||||
const sourceSpan = document.querySelector('#one > span');
|
||||
window.selectedcontentState = [
|
||||
selectedcontent.textContent.trim(),
|
||||
selectedcontent.innerText.trim(),
|
||||
selectedcontent.firstElementChild !== sourceSpan,
|
||||
];
|
||||
select.value = 'two';
|
||||
@@ -1468,7 +1469,7 @@ window.selectedcontentState.push(selectedcontent.textContent.trim());
|
||||
.expect("selectedcontent parser state should evaluate");
|
||||
assert_eq!(
|
||||
result.get("value").and_then(serde_json::Value::as_str),
|
||||
Some(r#"["one",true,"two","STRONG","updated","one"]"#),
|
||||
Some(r#"["one","one",true,"two","STRONG","updated","one"]"#),
|
||||
"parser option completion and select setters must synchronously clone the selected option children"
|
||||
);
|
||||
}));
|
||||
|
||||
@@ -89,6 +89,34 @@ fn dom_parser_inner_text_is_html_only() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selectedcontent_inner_text_falls_back_when_its_select_child_box_is_suppressed() {
|
||||
let mut vm = new_parsed_test_vm(
|
||||
"https://selectedcontent-inner-text.test/",
|
||||
"<!doctype html><html><body><div id=before>before</div></body></html>",
|
||||
);
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
void document.body.innerText;
|
||||
const select = document.createElement('select');
|
||||
select.innerHTML = '<button><selectedcontent id="selectedcontent">default</selectedcontent></button><option>one</option>';
|
||||
select.style.appearance = 'base-select';
|
||||
document.body.append(select);
|
||||
const selectedcontent = document.getElementById('selectedcontent');
|
||||
[
|
||||
selectedcontent.textContent,
|
||||
selectedcontent.innerText,
|
||||
selectedcontent.getClientRects().length
|
||||
].join('|')
|
||||
"#,
|
||||
)
|
||||
.expect("selectedcontent innerText should evaluate");
|
||||
|
||||
assert_eq!(result, "one|one|0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn live_inner_text_applies_inline_text_transform() {
|
||||
let mut vm = new_rendered_test_vm(
|
||||
|
||||
Reference in New Issue
Block a user