Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/blocks/src/v8/preview/StudioBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
* In a frame it answers only messages from the parent window whose origin is
* one of `trustedOrigins`. On a page that isn't framed it answers no message;
* there it only listens for the "." shortcut (`editShortcut`), which opens the
* page in Studio's editor, as v7's `LiveControls` did. Studio signs the editor
* page in Studio's editor, as v7's `LiveControls` did. A key typed in a field
* is left alone, also in a field inside a shadow root (a third-party widget's
* form), whose events reach the document with the host as their target, so the
* script reads the field from the event's composed path. Studio signs the editor
* in: `/choose-editor` sends a visitor who isn't signed in to Studio's login
* and back.
* The binding passes Studio's origins in (`@decocms/blocks` holds no list of
Expand Down Expand Up @@ -117,7 +120,7 @@ if(window.self!==window.top){
});
}else if(s.hasAttribute("data-shortcut")){
document.addEventListener("keydown",function(e){
var t=e.target;
var t=e.composedPath?e.composedPath()[0]:e.target;
if(e.defaultPrevented||(t&&(t.tagName==="INPUT"||t.tagName==="TEXTAREA"||t.tagName==="SELECT"||t.isContentEditable)))return;
if(e.key!=="."&&!(e.ctrlKey&&e.shiftKey&&e.key==="E"))return;
e.preventDefault();
Expand Down
21 changes: 21 additions & 0 deletions packages/blocks/src/v8/preview/preview.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,27 @@ describe("StudioBridge", () => {
input.remove();
});

it("leaves typing in a field inside a shadow root alone (a third-party widget's form)", () => {
const open = vi.spyOn(window, "open").mockImplementation(() => null);
unmount = mountBridge({ site: "shop", trustedOrigins: STUDIO, editShortcut: true }, false);
const host = document.createElement("div");
document.body.appendChild(host);
const input = document.createElement("input");
host.attachShadow({ mode: "open" }).appendChild(input);
// The document sees the host as the target; the email's "." must still type.
const e = new KeyboardEvent("keydown", {
key: ".",
bubbles: true,
cancelable: true,
composed: true,
});
input.dispatchEvent(e);
expect(e.defaultPrevented).toBe(false);
expect(press({ key: ".", ctrlKey: true }, host).defaultPrevented).toBe(true);
expect(open).toHaveBeenCalledTimes(1);
host.remove();
});

/** The choose-editor URL a Ctrl+"." opened (a plain "." navigates the tab, which jsdom can't follow). */
function opened(open: { mock: { calls: unknown[][] } }, call = 0): Record<string, string> {
const url = new URL(open.mock.calls[call][0] as string);
Expand Down