Skip to content

Commit

Permalink
Get started cleaning up interesttarget implementation [1/N]
Browse files Browse the repository at this point in the history
This is the first in a series of patches to clean up and update the
`interesttarget` implementation to match recent OpenUI discussions.

This CL:
 - Gets rid of `interestaction`. See the large comment posted here:
   openui/open-ui#1064 (comment)
 - Adds a connection to `InterestLost` when elements are de-focused.
 - Adds support (tentatively) for dialogs being shown modally.
 - Remove keyboard/focus support for `interesttarget`. This will
   get re-added later in its new form, via a hotkey rather than
   focus.

Bug: 326681249
Change-Id: I26f07a00c4fb1d2b1da92b64d91f330c02a11468
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6166890
Auto-Submit: Mason Freed <[email protected]>
Reviewed-by: David Baron <[email protected]>
Commit-Queue: Mason Freed <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1408202}
  • Loading branch information
mfreed7 authored and chromium-wpt-export-bot committed Jan 17, 2025
1 parent f98387d commit ed55e1d
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 430 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,82 +84,4 @@
"interestTargetElement attribute value must be an instance of Element",
);
}, "interestTargetElement throws error on assignment of non Element");

test(function () {
assert_false(buttonInvoker.hasAttribute("interestaction"));
assert_equals(buttonInvoker.interestAction, "");
assert_false(aInvoker.hasAttribute("interestaction"));
assert_equals(aInvoker.interestAction, "");
assert_false(inputInvoker.hasAttribute("interestaction"));
assert_equals(inputInvoker.interestAction, "");
}, "interestAction reflects '' when attribute not present");

test(function () {
buttonInvoker.setAttribute("interestaction", "");
assert_equals(buttonInvoker.getAttribute("interestaction"), "");
assert_equals(buttonInvoker.interestAction, "");
aInvoker.setAttribute("interestaction", "");
assert_equals(aInvoker.getAttribute("interestaction"), "");
assert_equals(aInvoker.interestAction, "");
inputInvoker.setAttribute("interestaction", "");
assert_equals(inputInvoker.getAttribute("interestaction"), "");
assert_equals(inputInvoker.interestAction, "");
}, "interestAction reflects '' when attribute empty, setAttribute version");

test(function () {
buttonInvoker.interestAction = "";
assert_equals(buttonInvoker.getAttribute("interestaction"), "");
assert_equals(buttonInvoker.interestAction, "");
aInvoker.interestAction = "";
assert_equals(aInvoker.getAttribute("interestaction"), "");
assert_equals(aInvoker.interestAction, "");
inputInvoker.interestAction = "";
assert_equals(inputInvoker.getAttribute("interestaction"), "");
assert_equals(inputInvoker.interestAction, "");
}, "interestAction reflects '' when attribute empty, IDL setter version");

test(function () {
buttonInvoker.interestAction = "fooBarBaz";
assert_equals(buttonInvoker.getAttribute("interestaction"), "fooBarBaz");
assert_equals(buttonInvoker.interestAction, "fooBarBaz");
aInvoker.interestAction = "fooBarBaz";
assert_equals(aInvoker.getAttribute("interestaction"), "fooBarBaz");
assert_equals(aInvoker.interestAction, "fooBarBaz");
inputInvoker.interestAction = "fooBarBaz";
assert_equals(inputInvoker.getAttribute("interestaction"), "fooBarBaz");
assert_equals(inputInvoker.interestAction, "fooBarBaz");
}, "interestAction reflects same casing");

test(function () {
buttonInvoker.interestAction = [];
assert_equals(buttonInvoker.getAttribute("interestaction"), "");
assert_equals(buttonInvoker.interestAction, "");
aInvoker.interestAction = [];
assert_equals(aInvoker.getAttribute("interestaction"), "");
assert_equals(aInvoker.interestAction, "");
inputInvoker.interestAction = [];
assert_equals(inputInvoker.getAttribute("interestaction"), "");
assert_equals(inputInvoker.interestAction, "");
}, "interestAction reflects '' when attribute set to []");

test(function () {
buttonInvoker.interestAction = [1, 2, 3];
assert_equals(buttonInvoker.getAttribute("interestaction"), "1,2,3");
assert_equals(buttonInvoker.interestAction, "1,2,3");
aInvoker.interestAction = [1, 2, 3];
assert_equals(aInvoker.getAttribute("interestaction"), "1,2,3");
assert_equals(aInvoker.interestAction, "1,2,3");
inputInvoker.interestAction = [1, 2, 3];
assert_equals(inputInvoker.getAttribute("interestaction"), "1,2,3");
assert_equals(inputInvoker.interestAction, "1,2,3");
}, "interestAction reflects tostring value");

test(function () {
buttonInvoker.interestAction = {};
assert_equals(buttonInvoker.interestAction, "[object Object]");
aInvoker.interestAction = {};
assert_equals(aInvoker.interestAction, "[object Object]");
inputInvoker.interestAction = {};
assert_equals(inputInvoker.interestAction, "[object Object]");
}, "interestAction reflects tostring value 2");
</script>
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<!doctype html>
<!DOCTYPE html>
<meta charset="utf-8" />
<meta name="author" title="Keith Cirkel" href="mailto:[email protected]" />
<meta name="author" title="Luke Warlow" href="mailto:[email protected]" />
<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" />
<link
rel="help"
href="https://open-ui.org/components/interest-invokers.explainer/"
/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
Expand All @@ -20,63 +23,35 @@
const slot = shadow.appendChild(document.createElement("slot"));
let childEvent = null;
let childEventTarget = null;
let childEventInvoker = null;
let childEventSource = null;
let hostEvent = null;
let hostEventTarget = null;
let hostEventInvoker = null;
slot.addEventListener(
"interest",
(e) => {
let hostEventSource = null;
slot.addEventListener("interest", (e) => {
childEvent = e;
childEventTarget = e.target;
childEventInvoker = e.invoker;
},
{ once: true },
);
host.addEventListener(
"interest",
(e) => {
childEventSource = e.source;
}, { once: true });
host.addEventListener("interest", (e) => {
hostEvent = e;
hostEventTarget = e.target;
hostEventInvoker = e.invoker;
},
{ once: true },
);
hostEventSource = e.source;
}, { once: true });
const event = new InterestEvent("interest", {
bubbles: true,
invoker: slot,
source: slot,
composed: true,
});
slot.dispatchEvent(event);
assert_true(childEvent instanceof InterestEvent, "slot saw interest event");
assert_equals(
childEventTarget,
slot,
"target is child inside shadow boundary",
);
assert_equals(
childEventInvoker,
slot,
"invoker is child inside shadow boundary",
);
assert_equals(
hostEvent,
childEvent,
"event dispatch propagates across shadow boundary",
);
assert_equals(
hostEventTarget,
host,
"target is retargeted to shadowroot host",
);
assert_equals(
hostEventInvoker,
host,
"invoker is retargeted to shadowroot host",
);
}, "InterestEvent propagates across shadow boundaries retargeting invoker");
assert_equals(childEventTarget, slot, "target is child inside shadow boundary");
assert_equals(childEventSource, slot, "source is child inside shadow boundary");
assert_equals(hostEvent, childEvent, "event dispatch propagates across shadow boundary");
assert_equals(hostEventTarget, host, "target is retargeted to shadowroot host");
assert_equals(hostEventSource, host, "source is retargeted to shadowroot host");
}, "InterestEvent propagates across shadow boundaries retargeting invoker source");

test(function (t) {
promise_test(async (t) => {
const host = document.createElement("div");
document.body.append(host);
t.add_cleanup(() => host.remove());
Expand All @@ -86,19 +61,16 @@
button.interestTargetElement = interestee;
let event = null;
let eventTarget = null;
let eventInvoker = null;
interestee.addEventListener(
"interest",
(e) => {
let eventSource = null;
interestee.addEventListener("interest", (e) => {
event = e;
eventTarget = e.target;
eventInvoker = e.invoker;
},
{ once: true },
);
button.focus();
eventSource = e.source;
},{ once: true });
await hoverOver(button);
assert_true(!!event,"InterestEvent gets fired");
assert_true(event instanceof InterestEvent);
assert_equals(eventTarget, interestee, "target is interestee");
assert_equals(eventInvoker, host, "interestee is host");
assert_equals(eventSource, host, "interestee is host");
}, "cross shadow InterestEvent retargets interestee to host element");
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -16,152 +16,80 @@
<script>
test(function () {
const event = new InterestEvent("test");
assert_equals(event.action, "");
assert_readonly(event, "action", "readonly attribute value");
}, "action is a readonly defaulting to ''");
assert_equals(event.source, null);
assert_readonly(event, "source", "readonly attribute value");
}, "source is readonly defaulting to null");

test(function () {
const event = new InterestEvent("test");
assert_equals(event.invoker, null);
assert_readonly(event, "invoker", "readonly attribute value");
}, "invoker is readonly defaulting to null");

test(function () {
const event = new InterestEvent("test", { action: "sAmPle" });
assert_equals(event.action, "sAmPle");
}, "action reflects initialized attribute");

test(function () {
const event = new InterestEvent("test", { action: undefined });
assert_equals(event.action, "");
}, "action set to undefined");

test(function () {
const event = new InterestEvent("test", { action: null });
assert_equals(event.action, "null");
}, "action set to null");

test(function () {
const event = new InterestEvent("test", { action: false });
assert_equals(event.action, "false");
}, "action set to false");

test(function () {
const event = new InterestEvent("test", { action: "" });
assert_equals(event.action, "");
}, "action explicitly set to empty string");

test(function () {
const event = new InterestEvent("test", { action: true });
assert_equals(event.action, "true");
}, "action set to true");

test(function () {
const event = new InterestEvent("test", { action: 0.5 });
assert_equals(event.action, "0.5");
}, "action set to a number");

test(function () {
const event = new InterestEvent("test", { action: [] });
assert_equals(event.action, "");
}, "action set to []");

test(function () {
const event = new InterestEvent("test", { action: [1, 2, 3] });
assert_equals(event.action, "1,2,3");
}, "action set to [1, 2, 3]");

test(function () {
const event = new InterestEvent("test", { action: { sample: 0.5 } });
assert_equals(event.action, "[object Object]");
}, "action set to an object");

test(function () {
const event = new InterestEvent("test", {
action: {
toString() {
return "sample";
},
},
});
assert_equals(event.action, "sample");
}, "action set to an object with a toString function");

test(function () {
const eventInit = { action: "sample", invoker: document.body };
const eventInit = { source: document.body };
const event = new InterestEvent("test", eventInit);
assert_equals(event.action, "sample");
assert_equals(event.invoker, document.body);
}, "InterestEventInit properties set value");
assert_equals(event.source, document.body);
}, "InterestEventInit properties set value (manual event)");

test(function () {
const eventInit = {
action: "open",
invoker: document.getElementById("div"),
source: document.getElementById("div"),
};
const event = new InterestEvent("beforetoggle", eventInit);
assert_equals(event.action, "open");
assert_equals(event.invoker, document.getElementById("div"));
}, "InterestEventInit properties set value 2");
assert_equals(event.source, document.getElementById("div"));
}, "InterestEventInit properties set value (beforetoggle event)");

test(function () {
const eventInit = {
action: "closed",
invoker: document.getElementById("button"),
source: document.getElementById("button"),
};
const event = new InterestEvent("toggle", eventInit);
assert_equals(event.action, "closed");
assert_equals(event.invoker, document.getElementById("button"));
}, "InterestEventInit properties set value 3");
assert_equals(event.source, document.getElementById("button"));
}, "InterestEventInit properties set value (toggle event)");

test(function () {
const event = new InterestEvent("test", { invoker: undefined });
assert_equals(event.invoker, null);
}, "invoker set to undefined");
const event = new InterestEvent("test", { source: undefined });
assert_equals(event.source, null);
}, "source set to undefined");

test(function () {
const event = new InterestEvent("test", { invoker: null });
assert_equals(event.invoker, null);
}, "invoker set to null");
const event = new InterestEvent("test", { source: null });
assert_equals(event.source, null);
}, "source set to null");

test(function () {
assert_throws_js(
TypeError,
function () {
new InterestEvent("test", { invoker: false });
new InterestEvent("test", { source: false });
},
"invoker is not an object",
"source is not an object",
);
}, "invoker set to false");
}, "source set to false");

test(function () {
assert_throws_js(
TypeError,
function () {
const event = new InterestEvent("test", { invoker: true });
const event = new InterestEvent("test", { source: true });
},
"invoker is not an object",
"source is not an object",
);
}, "invoker set to true");
}, "source set to true");

test(function () {
assert_throws_js(
TypeError,
function () {
const event = new InterestEvent("test", { invoker: {} });
const event = new InterestEvent("test", { source: {} });
},
"invoker is not an object",
"source is not an object",
);
}, "invoker set to {}");
}, "source set to {}");

test(function () {
assert_throws_js(
TypeError,
function () {
const eventInit = { action: "closed", invoker: new XMLHttpRequest() };
const eventInit = { source: new XMLHttpRequest() };
const event = new InterestEvent("toggle", eventInit);
},
"invoker is not an Element",
"source is not an Element",
);
}, "invoker set to non-Element EventTarget");
}, "source set to non-Element EventTarget");
</script>
Loading

0 comments on commit ed55e1d

Please sign in to comment.