Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88dc25865b | |||
| 1d4e2731cf |
@@ -603,6 +603,55 @@ const YOUTUBE_VIDEO_STATES = new Map<
|
|||||||
ValueOf<typeof YOUTUBE_STATES>
|
ValueOf<typeof YOUTUBE_STATES>
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
const isGoogleDrivePreviewLink = (link: string | null | undefined) => {
|
||||||
|
if (!link) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(link);
|
||||||
|
return (
|
||||||
|
url.hostname === "drive.google.com" &&
|
||||||
|
/^\/file\/d\/[^/]+\/preview\/?$/.test(url.pathname)
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isGoogleDriveEmbeddableElement = (
|
||||||
|
element: ExcalidrawElement | null | undefined,
|
||||||
|
) => {
|
||||||
|
if (!isEmbeddableElement(element)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const embedLink = getEmbedLink(toValidURL(element.link || ""));
|
||||||
|
return (
|
||||||
|
embedLink?.type === "video" && isGoogleDrivePreviewLink(embedLink.link)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMBEDDABLE_CANVAS_GUARD_CLASS = "excalidraw__embeddable-canvas-guard";
|
||||||
|
const EMBEDDABLE_CANVAS_GUARDS = [
|
||||||
|
{
|
||||||
|
key: "top",
|
||||||
|
style: { top: 0, left: 0, right: 0, height: "33.333%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "bottom",
|
||||||
|
style: { bottom: 0, left: 0, right: 0, height: "33.333%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "left",
|
||||||
|
style: { top: "33.333%", bottom: "33.333%", left: 0, width: "33.333%" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "right",
|
||||||
|
style: { top: "33.333%", bottom: "33.333%", right: 0, width: "33.333%" },
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
let IS_PLAIN_PASTE = false;
|
let IS_PLAIN_PASTE = false;
|
||||||
let IS_PLAIN_PASTE_TIMER = 0;
|
let IS_PLAIN_PASTE_TIMER = 0;
|
||||||
let PLAIN_PASTE_TOAST_SHOWN = false;
|
let PLAIN_PASTE_TOAST_SHOWN = false;
|
||||||
@@ -1283,6 +1332,46 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
return this.iFrameRefs.get(element.id);
|
return this.iFrameRefs.get(element.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private deactivateActiveEmbeddable = () => {
|
||||||
|
if (this.state.activeEmbeddable) {
|
||||||
|
flushSync(() => {
|
||||||
|
this.setState({ activeEmbeddable: null });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleEmbeddableCanvasGuardPointerDown = (
|
||||||
|
event: React.PointerEvent<HTMLDivElement>,
|
||||||
|
) => {
|
||||||
|
this.deactivateActiveEmbeddable();
|
||||||
|
this.handleCanvasPointerDown(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleEmbeddableCanvasGuardPointerMove = (
|
||||||
|
event: React.PointerEvent<HTMLDivElement>,
|
||||||
|
) => {
|
||||||
|
this.lastViewportPosition.x = event.clientX;
|
||||||
|
this.lastViewportPosition.y = event.clientY;
|
||||||
|
this.deactivateActiveEmbeddable();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleEmbeddableCanvasGuardTouchStart = (
|
||||||
|
event: React.TouchEvent<HTMLDivElement>,
|
||||||
|
) => {
|
||||||
|
if (event.touches.length >= 2) {
|
||||||
|
this.deactivateActiveEmbeddable();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleEmbeddableCanvasGuardWheel = (
|
||||||
|
event: React.WheelEvent<HTMLDivElement>,
|
||||||
|
) => {
|
||||||
|
this.lastViewportPosition.x = event.clientX;
|
||||||
|
this.lastViewportPosition.y = event.clientY;
|
||||||
|
this.deactivateActiveEmbeddable();
|
||||||
|
this.handleWheel(event);
|
||||||
|
};
|
||||||
|
|
||||||
private handleIframeLikeElementHover = ({
|
private handleIframeLikeElementHover = ({
|
||||||
hitElement,
|
hitElement,
|
||||||
scenePointer,
|
scenePointer,
|
||||||
@@ -1305,12 +1394,25 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
))
|
))
|
||||||
) {
|
) {
|
||||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||||
|
const isDriveEmbedActivating =
|
||||||
|
(this.state.viewModeEnabled ||
|
||||||
|
this.state.activeTool.type === "selection") &&
|
||||||
|
isGoogleDriveEmbeddableElement(hitElement);
|
||||||
this.setState({
|
this.setState({
|
||||||
activeEmbeddable: { element: hitElement, state: "hover" },
|
activeEmbeddable: {
|
||||||
|
element: hitElement,
|
||||||
|
state: isDriveEmbedActivating ? "active" : "hover",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} else if (this.state.activeEmbeddable?.state === "hover") {
|
} else if (this.state.activeEmbeddable?.state === "hover") {
|
||||||
this.setState({ activeEmbeddable: null });
|
this.setState({ activeEmbeddable: null });
|
||||||
|
} else if (
|
||||||
|
this.state.activeEmbeddable?.state === "active" &&
|
||||||
|
isGoogleDriveEmbeddableElement(this.state.activeEmbeddable.element) &&
|
||||||
|
this.state.activeEmbeddable.element !== hitElement
|
||||||
|
) {
|
||||||
|
this.setState({ activeEmbeddable: null });
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -1734,6 +1836,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
const isHovered =
|
const isHovered =
|
||||||
this.state.activeEmbeddable?.element === el &&
|
this.state.activeEmbeddable?.element === el &&
|
||||||
this.state.activeEmbeddable?.state === "hover";
|
this.state.activeEmbeddable?.state === "hover";
|
||||||
|
const shouldConstrainDriveInteraction =
|
||||||
|
isActive && isGoogleDriveEmbeddableElement(el);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -1785,6 +1889,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
width: isVisible ? `${el.width}px` : 0,
|
width: isVisible ? `${el.width}px` : 0,
|
||||||
height: isVisible ? `${el.height}px` : 0,
|
height: isVisible ? `${el.height}px` : 0,
|
||||||
transform: isVisible ? `rotate(${el.angle}rad)` : "none",
|
transform: isVisible ? `rotate(${el.angle}rad)` : "none",
|
||||||
|
position: "relative",
|
||||||
pointerEvents: isActive
|
pointerEvents: isActive
|
||||||
? POINTER_EVENTS.enabled
|
? POINTER_EVENTS.enabled
|
||||||
: POINTER_EVENTS.disabled,
|
: POINTER_EVENTS.disabled,
|
||||||
@@ -1827,6 +1932,26 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{shouldConstrainDriveInteraction &&
|
||||||
|
EMBEDDABLE_CANVAS_GUARDS.map(({ key, style }) => (
|
||||||
|
<div
|
||||||
|
key={key}
|
||||||
|
className={EMBEDDABLE_CANVAS_GUARD_CLASS}
|
||||||
|
onPointerDown={
|
||||||
|
this.handleEmbeddableCanvasGuardPointerDown
|
||||||
|
}
|
||||||
|
onPointerMove={
|
||||||
|
this.handleEmbeddableCanvasGuardPointerMove
|
||||||
|
}
|
||||||
|
onTouchStart={this.handleEmbeddableCanvasGuardTouchStart}
|
||||||
|
onWheel={this.handleEmbeddableCanvasGuardWheel}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
zIndex: 2,
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -4733,6 +4858,16 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.state.activeEmbeddable &&
|
||||||
|
(event.ctrlKey ||
|
||||||
|
event.metaKey ||
|
||||||
|
event.key === "Control" ||
|
||||||
|
event.key === "Meta")
|
||||||
|
) {
|
||||||
|
this.setState({ activeEmbeddable: null });
|
||||||
|
}
|
||||||
|
|
||||||
if (!isInputLike(event.target)) {
|
if (!isInputLike(event.target)) {
|
||||||
if (
|
if (
|
||||||
(event.key === KEYS.ESCAPE || event.key === KEYS.ENTER) &&
|
(event.key === KEYS.ESCAPE || event.key === KEYS.ENTER) &&
|
||||||
@@ -5611,6 +5746,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
selectedElementIds: makeNextSelectedElementIds({}, this.state),
|
selectedElementIds: makeNextSelectedElementIds({}, this.state),
|
||||||
activeEmbeddable: null,
|
activeEmbeddable: null,
|
||||||
});
|
});
|
||||||
|
} else if (this.state.activeEmbeddable) {
|
||||||
|
this.setState({
|
||||||
|
activeEmbeddable: null,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
gesture.initialScale = this.state.zoom.value;
|
gesture.initialScale = this.state.zoom.value;
|
||||||
});
|
});
|
||||||
@@ -8165,6 +8304,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
y: event.clientY,
|
y: event.clientY,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (gesture.pointers.size >= 2 && this.state.activeEmbeddable) {
|
||||||
|
this.setState({ activeEmbeddable: null });
|
||||||
|
}
|
||||||
|
|
||||||
if (gesture.pointers.size === 2) {
|
if (gesture.pointers.size === 2) {
|
||||||
gesture.lastCenter = getCenter(gesture.pointers);
|
gesture.lastCenter = getCenter(gesture.pointers);
|
||||||
gesture.initialScale = this.state.zoom.value;
|
gesture.initialScale = this.state.zoom.value;
|
||||||
@@ -12584,7 +12727,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
event.target instanceof HTMLTextAreaElement ||
|
event.target instanceof HTMLTextAreaElement ||
|
||||||
event.target instanceof HTMLIFrameElement ||
|
event.target instanceof HTMLIFrameElement ||
|
||||||
(event.target instanceof HTMLElement &&
|
(event.target instanceof HTMLElement &&
|
||||||
event.target.classList.contains(CLASSES.FRAME_NAME))
|
(event.target.classList.contains(CLASSES.FRAME_NAME) ||
|
||||||
|
event.target.classList.contains(EMBEDDABLE_CANVAS_GUARD_CLASS)))
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// prevent zooming the browser (but allow scrolling DOM)
|
// prevent zooming the browser (but allow scrolling DOM)
|
||||||
|
|||||||
@@ -0,0 +1,315 @@
|
|||||||
|
import { Excalidraw } from "../index";
|
||||||
|
|
||||||
|
import { Pointer } from "./helpers/ui";
|
||||||
|
import { act, fireEvent, GlobalTestState, render, waitFor } from "./test-utils";
|
||||||
|
|
||||||
|
import type { ExcalidrawProps } from "../types";
|
||||||
|
|
||||||
|
describe("embeddable interactions", () => {
|
||||||
|
const h = window.h;
|
||||||
|
const mouse = new Pointer("mouse");
|
||||||
|
|
||||||
|
const renderGoogleDriveEmbeddable = async (
|
||||||
|
excalidrawProps: Partial<ExcalidrawProps> = {},
|
||||||
|
) => {
|
||||||
|
const renderResult = await render(<Excalidraw {...excalidrawProps} />);
|
||||||
|
const fileId = "1AbCdEfGhIjKlMnOpQrStUvWxYz123456";
|
||||||
|
const src = `https://drive.google.com/file/d/${fileId}/preview`;
|
||||||
|
let embeddable!: NonNullable<
|
||||||
|
ReturnType<typeof h.app.insertEmbeddableElement>
|
||||||
|
>;
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
const insertedEmbeddable = h.app.insertEmbeddableElement({
|
||||||
|
sceneX: 40,
|
||||||
|
sceneY: 40,
|
||||||
|
link: `https://drive.google.com/file/d/${fileId}/view?usp=sharing`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!insertedEmbeddable) {
|
||||||
|
throw new Error("Google Drive embeddable not inserted");
|
||||||
|
}
|
||||||
|
|
||||||
|
embeddable = insertedEmbeddable;
|
||||||
|
});
|
||||||
|
|
||||||
|
(
|
||||||
|
h.app as unknown as {
|
||||||
|
embedsValidationStatus: Map<string, boolean>;
|
||||||
|
}
|
||||||
|
).embedsValidationStatus.set(embeddable.id, true);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
h.setState({ width: 1000, height: 1000 });
|
||||||
|
h.app.scene.triggerUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getIframe = () => {
|
||||||
|
const iframe = renderResult.container.querySelector<HTMLIFrameElement>(
|
||||||
|
"iframe.excalidraw__embeddable",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!iframe) {
|
||||||
|
throw new Error("Google Drive iframe not rendered");
|
||||||
|
}
|
||||||
|
|
||||||
|
return iframe;
|
||||||
|
};
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(getIframe().src).toBe(src);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...renderResult,
|
||||||
|
embeddable,
|
||||||
|
getIframe,
|
||||||
|
src,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderYouTubeEmbeddable = async (
|
||||||
|
excalidrawProps: Partial<ExcalidrawProps> = {},
|
||||||
|
) => {
|
||||||
|
const renderResult = await render(<Excalidraw {...excalidrawProps} />);
|
||||||
|
let embeddable!: NonNullable<
|
||||||
|
ReturnType<typeof h.app.insertEmbeddableElement>
|
||||||
|
>;
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
const insertedEmbeddable = h.app.insertEmbeddableElement({
|
||||||
|
sceneX: 40,
|
||||||
|
sceneY: 40,
|
||||||
|
link: "https://www.youtube.com/watch?v=gkGMXY0wekg",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!insertedEmbeddable) {
|
||||||
|
throw new Error("YouTube embeddable not inserted");
|
||||||
|
}
|
||||||
|
|
||||||
|
embeddable = insertedEmbeddable;
|
||||||
|
});
|
||||||
|
|
||||||
|
(
|
||||||
|
h.app as unknown as {
|
||||||
|
embedsValidationStatus: Map<string, boolean>;
|
||||||
|
}
|
||||||
|
).embedsValidationStatus.set(embeddable.id, true);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
h.setState({ width: 1000, height: 1000 });
|
||||||
|
h.app.scene.triggerUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
renderResult.container.querySelector("iframe.excalidraw__embeddable"),
|
||||||
|
).not.toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...renderResult,
|
||||||
|
embeddable,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const setActiveEmbeddable = (
|
||||||
|
embeddable: NonNullable<ReturnType<typeof h.app.insertEmbeddableElement>>,
|
||||||
|
) => {
|
||||||
|
act(() => {
|
||||||
|
h.setState({
|
||||||
|
activeEmbeddable: { element: embeddable, state: "active" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
it("lets the initial Google Drive video click land in the iframe center", async () => {
|
||||||
|
const { container, embeddable, getIframe, src } =
|
||||||
|
await renderGoogleDriveEmbeddable();
|
||||||
|
|
||||||
|
mouse.moveTo(
|
||||||
|
embeddable.x + embeddable.width / 2,
|
||||||
|
embeddable.y + embeddable.height / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
container.querySelector<HTMLElement>(
|
||||||
|
".excalidraw__embeddable-container__inner",
|
||||||
|
)?.style.pointerEvents,
|
||||||
|
).toBe("all");
|
||||||
|
expect(h.state.activeEmbeddable?.element.id).toBe(embeddable.id);
|
||||||
|
expect(h.state.activeEmbeddable?.state).toBe("active");
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll(".excalidraw__embeddable-canvas-guard"),
|
||||||
|
).toHaveLength(4);
|
||||||
|
expect(
|
||||||
|
container.querySelector(".excalidraw__embeddable-hint"),
|
||||||
|
).toBeNull();
|
||||||
|
expect(getIframe().src).toBe(src);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns Drive embeddable edge interactions to the canvas", async () => {
|
||||||
|
const { container, embeddable } = await renderGoogleDriveEmbeddable();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
h.setState({
|
||||||
|
activeEmbeddable: { element: embeddable, state: "active" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const guard = container.querySelector<HTMLElement>(
|
||||||
|
".excalidraw__embeddable-canvas-guard",
|
||||||
|
);
|
||||||
|
expect(guard).not.toBeNull();
|
||||||
|
|
||||||
|
fireEvent.pointerMove(guard!, {
|
||||||
|
clientX: embeddable.x + 1,
|
||||||
|
clientY: embeddable.y + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(h.state.activeEmbeddable).toBeNull();
|
||||||
|
expect(
|
||||||
|
container.querySelector<HTMLElement>(
|
||||||
|
".excalidraw__embeddable-container__inner",
|
||||||
|
)?.style.pointerEvents,
|
||||||
|
).toBe("none");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("deactivates an interactive embeddable on Ctrl/Cmd keydown", async () => {
|
||||||
|
const { embeddable } = await renderGoogleDriveEmbeddable({
|
||||||
|
handleKeyboardGlobally: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
mouse.moveTo(
|
||||||
|
embeddable.x + embeddable.width / 2,
|
||||||
|
embeddable.y + embeddable.height / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(h.state.activeEmbeddable?.state).toBe("active");
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.keyDown(document, {
|
||||||
|
key: "Control",
|
||||||
|
ctrlKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(h.state.activeEmbeddable).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles Ctrl/Cmd wheel on Drive embeddable guards", async () => {
|
||||||
|
const { container, embeddable } = await renderGoogleDriveEmbeddable();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
h.setState({
|
||||||
|
activeEmbeddable: { element: embeddable, state: "active" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const guard = container.querySelector<HTMLElement>(
|
||||||
|
".excalidraw__embeddable-canvas-guard",
|
||||||
|
);
|
||||||
|
expect(guard).not.toBeNull();
|
||||||
|
|
||||||
|
const prevZoom = h.state.zoom.value;
|
||||||
|
|
||||||
|
fireEvent.wheel(guard!, {
|
||||||
|
clientX: embeddable.x + 1,
|
||||||
|
clientY: embeddable.y + 1,
|
||||||
|
ctrlKey: true,
|
||||||
|
deltaY: -100,
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(h.state.activeEmbeddable).toBeNull();
|
||||||
|
expect(h.state.zoom.value).toBeGreaterThan(prevZoom);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("deactivates a non-Drive interactive embeddable on Ctrl/Cmd keydown", async () => {
|
||||||
|
const { container, embeddable } = await renderYouTubeEmbeddable({
|
||||||
|
handleKeyboardGlobally: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
setActiveEmbeddable(embeddable);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(
|
||||||
|
container.querySelector<HTMLElement>(
|
||||||
|
".excalidraw__embeddable-container__inner",
|
||||||
|
)?.style.pointerEvents,
|
||||||
|
).toBe("all");
|
||||||
|
expect(
|
||||||
|
container.querySelectorAll(".excalidraw__embeddable-canvas-guard"),
|
||||||
|
).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.keyDown(document, {
|
||||||
|
key: "Control",
|
||||||
|
ctrlKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(h.state.activeEmbeddable).toBeNull();
|
||||||
|
expect(
|
||||||
|
container.querySelector<HTMLElement>(
|
||||||
|
".excalidraw__embeddable-container__inner",
|
||||||
|
)?.style.pointerEvents,
|
||||||
|
).toBe("none");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("deactivates a non-Drive interactive embeddable on parent-observed pinch", async () => {
|
||||||
|
const { embeddable } = await renderYouTubeEmbeddable();
|
||||||
|
|
||||||
|
setActiveEmbeddable(embeddable);
|
||||||
|
|
||||||
|
fireEvent.pointerDown(GlobalTestState.interactiveCanvas, {
|
||||||
|
clientX: embeddable.x + 10,
|
||||||
|
clientY: embeddable.y + 10,
|
||||||
|
pointerId: 1,
|
||||||
|
pointerType: "touch",
|
||||||
|
});
|
||||||
|
fireEvent.pointerDown(GlobalTestState.interactiveCanvas, {
|
||||||
|
clientX: embeddable.x + 30,
|
||||||
|
clientY: embeddable.y + 30,
|
||||||
|
pointerId: 2,
|
||||||
|
pointerType: "touch",
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(h.state.activeEmbeddable).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.pointerUp(GlobalTestState.interactiveCanvas, {
|
||||||
|
pointerId: 1,
|
||||||
|
pointerType: "touch",
|
||||||
|
});
|
||||||
|
fireEvent.pointerUp(GlobalTestState.interactiveCanvas, {
|
||||||
|
pointerId: 2,
|
||||||
|
pointerType: "touch",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("deactivates a non-Drive interactive embeddable on Safari gesturestart", async () => {
|
||||||
|
const { embeddable } = await renderYouTubeEmbeddable();
|
||||||
|
|
||||||
|
setActiveEmbeddable(embeddable);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
document.dispatchEvent(
|
||||||
|
new Event("gesturestart", { bubbles: true, cancelable: true }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(h.state.activeEmbeddable).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user