🐛 Fix view mode child click blocked by parent mouse-leave interaction overlay

This commit is contained in:
alonso.torres 2026-05-29 01:30:36 +02:00 committed by Alonso Torres
parent e90b14eb37
commit 6a0f24e691
5 changed files with 717 additions and 0 deletions

View File

@ -0,0 +1,578 @@
{
"~:id": "~ucc000000-0000-0000-0000-000000000003",
"~:file-id": "~ucc000000-0000-0000-0000-000000000001",
"~:created-at": "~m1737715191353",
"~:data": {
"~:options": {},
"~:objects": {
"~u00000000-0000-0000-0000-000000000000": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:rotation": 0,
"~:name": "Root Frame",
"~:width": 0.01,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0
}
},
{
"~#point": {
"~:x": 0.01,
"~:y": 0.01
}
},
{
"~#point": {
"~:x": 0,
"~:y": 0.01
}
}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:r3": 0,
"~:r1": 0,
"~:id": "~u00000000-0000-0000-0000-000000000000",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1,
"~:r4": 0,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 0.01,
"~:height": 0.01,
"~:x1": 0,
"~:y1": 0,
"~:x2": 0.01,
"~:y2": 0.01
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 0.01,
"~:flip-y": null,
"~:shapes": [
"~ucc000000-0001-0000-0000-000000000001",
"~ucc000000-0002-0000-0000-000000000001",
"~ucc000000-0003-0000-0000-000000000001"
]
}
},
"~ucc000000-0001-0000-0000-000000000001": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Screen1",
"~:width": 400,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 0,
"~:y": 0
}
},
{
"~#point": {
"~:x": 400,
"~:y": 0
}
},
{
"~#point": {
"~:x": 400,
"~:y": 300
}
},
{
"~#point": {
"~:x": 0,
"~:y": 300
}
}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:r3": 0,
"~:r1": 0,
"~:id": "~ucc000000-0001-0000-0000-000000000001",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 0,
"~:proportion": 1,
"~:r4": 0,
"~:selrect": {
"~#rect": {
"~:x": 0,
"~:y": 0,
"~:width": 400,
"~:height": 300,
"~:x1": 0,
"~:y1": 0,
"~:x2": 400,
"~:y2": 300
}
},
"~:fills": [
{
"~:fill-color": "#FFFFFF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 300,
"~:flip-y": null,
"~:shapes": []
}
},
"~ucc000000-0002-0000-0000-000000000001": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Screen2",
"~:width": 400,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 500,
"~:y": 0
}
},
{
"~#point": {
"~:x": 900,
"~:y": 0
}
},
{
"~#point": {
"~:x": 900,
"~:y": 300
}
},
{
"~#point": {
"~:x": 500,
"~:y": 300
}
}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:r3": 0,
"~:r1": 0,
"~:id": "~ucc000000-0002-0000-0000-000000000001",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 500,
"~:proportion": 1,
"~:r4": 0,
"~:selrect": {
"~#rect": {
"~:x": 500,
"~:y": 0,
"~:width": 400,
"~:height": 300,
"~:x1": 500,
"~:y1": 0,
"~:x2": 900,
"~:y2": 300
}
},
"~:fills": [
{
"~:fill-color": "#F0F0F0",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 300,
"~:flip-y": null,
"~:shapes": [
"~ucc000000-0002-0001-0000-000000000001"
]
}
},
"~ucc000000-0003-0000-0000-000000000001": {
"~#shape": {
"~:y": 0,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "Screen3",
"~:width": 400,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 1000,
"~:y": 0
}
},
{
"~#point": {
"~:x": 1400,
"~:y": 0
}
},
{
"~#point": {
"~:x": 1400,
"~:y": 300
}
},
{
"~#point": {
"~:x": 1000,
"~:y": 300
}
}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:r3": 0,
"~:r1": 0,
"~:id": "~ucc000000-0003-0000-0000-000000000001",
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
"~:strokes": [],
"~:x": 1000,
"~:proportion": 1,
"~:r4": 0,
"~:selrect": {
"~#rect": {
"~:x": 1000,
"~:y": 0,
"~:width": 400,
"~:height": 300,
"~:x1": 1000,
"~:y1": 0,
"~:x2": 1400,
"~:y2": 300
}
},
"~:fills": [
{
"~:fill-color": "#E0E0FF",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 300,
"~:flip-y": null,
"~:shapes": []
}
},
"~ucc000000-0002-0001-0000-000000000001": {
"~#shape": {
"~:y": 50,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:rotation": 0,
"~:grow-type": "~:fixed",
"~:hide-in-viewer": false,
"~:name": "HighlightState",
"~:width": 300,
"~:type": "~:frame",
"~:points": [
{
"~#point": {
"~:x": 550,
"~:y": 50
}
},
{
"~#point": {
"~:x": 850,
"~:y": 50
}
},
{
"~#point": {
"~:x": 850,
"~:y": 250
}
},
{
"~#point": {
"~:x": 550,
"~:y": 250
}
}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:r3": 0,
"~:r1": 0,
"~:id": "~ucc000000-0002-0001-0000-000000000001",
"~:parent-id": "~ucc000000-0002-0000-0000-000000000001",
"~:frame-id": "~ucc000000-0002-0000-0000-000000000001",
"~:strokes": [],
"~:x": 550,
"~:proportion": 1,
"~:r4": 0,
"~:selrect": {
"~#rect": {
"~:x": 550,
"~:y": 50,
"~:width": 300,
"~:height": 200,
"~:x1": 550,
"~:y1": 50,
"~:x2": 850,
"~:y2": 250
}
},
"~:fills": [
{
"~:fill-color": "#CCCCCC",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 200,
"~:flip-y": null,
"~:shapes": [
"~ucc000000-0002-0001-0000-000000000002"
],
"~:interactions": [
{
"~:event-type": "~:mouse-leave",
"~:action-type": "~:navigate",
"~:destination": "~ucc000000-0001-0000-0000-000000000001",
"~:preserve-scroll": false
}
]
}
},
"~ucc000000-0002-0001-0000-000000000002": {
"~#shape": {
"~:y": 190,
"~:hide-fill-on-export": false,
"~:transform": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:rotation": 0,
"~:name": "HoverQuickToolArrowRight",
"~:width": 60,
"~:type": "~:rect",
"~:points": [
{
"~#point": {
"~:x": 780,
"~:y": 190
}
},
{
"~#point": {
"~:x": 840,
"~:y": 190
}
},
{
"~#point": {
"~:x": 840,
"~:y": 230
}
},
{
"~#point": {
"~:x": 780,
"~:y": 230
}
}
],
"~:r2": 0,
"~:proportion-lock": false,
"~:transform-inverse": {
"~#matrix": {
"~:a": 1,
"~:b": 0,
"~:c": 0,
"~:d": 1,
"~:e": 0,
"~:f": 0
}
},
"~:r3": 0,
"~:r1": 0,
"~:id": "~ucc000000-0002-0001-0000-000000000002",
"~:parent-id": "~ucc000000-0002-0001-0000-000000000001",
"~:frame-id": "~ucc000000-0002-0001-0000-000000000001",
"~:strokes": [],
"~:x": 780,
"~:proportion": 1,
"~:r4": 0,
"~:selrect": {
"~#rect": {
"~:x": 780,
"~:y": 190,
"~:width": 60,
"~:height": 40,
"~:x1": 780,
"~:y1": 190,
"~:x2": 840,
"~:y2": 230
}
},
"~:fills": [
{
"~:fill-color": "#FF6600",
"~:fill-opacity": 1
}
],
"~:flip-x": null,
"~:height": 40,
"~:flip-y": null,
"~:interactions": [
{
"~:event-type": "~:click",
"~:action-type": "~:navigate",
"~:destination": "~ucc000000-0003-0000-0000-000000000001",
"~:preserve-scroll": false
}
]
}
}
},
"~:id": "~ucc000000-0000-0000-0000-000000000002",
"~:name": "Page 1",
"~:flows": {}
}
}

View File

@ -0,0 +1,85 @@
{
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true,
"~:can-read": true,
"~:is-logged": true,
"~:in-team": true
},
"~:libraries": [],
"~:file": {
"~:features": {
"~#set": [
"layout/grid",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:has-media-trimmed": false,
"~:comment-thread-seqn": 0,
"~:name": "Interaction Blocks Child Test",
"~:revn": 1,
"~:modified-at": "~m1737715191361",
"~:vern": 0,
"~:id": "~ucc000000-0000-0000-0000-000000000001",
"~:is-shared": false,
"~:version": 61,
"~:project-id": "~ucc000000-0000-0000-0000-000000000004",
"~:created-at": "~m1737708162019",
"~:data": {
"~:id": "~ucc000000-0000-0000-0000-000000000001",
"~:options": {
"~:components-v2": true
},
"~:pages": [
"~ucc000000-0000-0000-0000-000000000002"
],
"~:pages-index": {
"~ucc000000-0000-0000-0000-000000000002": {
"~#penpot/pointer": [
"~ucc000000-0000-0000-0000-000000000003",
{
"~:created-at": "~m1737715191371"
}
]
}
}
}
},
"~:team": {
"~:id": "~ucc000000-0000-0000-0000-000000000005",
"~:created-at": "~m1737707537358",
"~:modified-at": "~m1737707537358",
"~:name": "Test Team",
"~:is-default": false,
"~:features": {
"~#set": [
"layout/grid",
"fdata/pointer-map",
"fdata/objects-map",
"components/v2",
"fdata/shape-data-type"
]
},
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": true,
"~:is-admin": true,
"~:can-edit": true,
"~:can-read": true,
"~:is-logged": true,
"~:in-team": true
}
},
"~:project": {
"~:id": "~ucc000000-0000-0000-0000-000000000004",
"~:name": "Test Project",
"~:team-id": "~ucc000000-0000-0000-0000-000000000005"
},
"~:share-links": [],
"~:fonts": []
}

View File

@ -56,6 +56,21 @@ export class ViewerPage extends BaseWebSocketPage {
);
}
async setupFileWithInteractionBlocksChild() {
await this.mockRPC(
/get\-view\-only\-bundle\?/,
"viewer/get-view-only-bundle-interaction-blocks-child.json",
);
await this.mockRPC(
"get-comment-threads?file-id=*",
"workspace/get-comment-threads-empty.json",
);
await this.mockRPC(
"get-file-fragment?file-id=*&fragment-id=*",
"viewer/get-file-fragment-interaction-blocks-child.json",
);
}
async setupFileWithComments() {
await this.mockRPC(
/get\-view\-only\-bundle\?/,

View File

@ -8,6 +8,44 @@ test.beforeEach(async ({ page }) => {
const multipleBoardsFileId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb0";
const multipleBoardsPageId = "dd5cc0bb-91ff-81b9-8004-77df9cd3edb3";
const interactionBlocksChildFileId = "cc000000-0000-0000-0000-000000000001";
const interactionBlocksChildPageId = "cc000000-0000-0000-0000-000000000002";
test("Child click interaction works when parent has mouse-leave interaction", async ({
page,
}) => {
const viewer = new ViewerPage(page);
await viewer.setupLoggedInUser();
await viewer.setupFileWithInteractionBlocksChild();
// Start at Screen2 (index=1): HighlightState (frame w/ mouse-leave) contains
// HoverQuickToolArrowRight (rect w/ click → Screen3)
await viewer.goToViewer({
fileId: interactionBlocksChildFileId,
pageId: interactionBlocksChildPageId,
});
// Click Next to go to Screen2 (index=1 in the 3-board sequence)
await viewer.page.getByRole("button", { name: "Next" }).click();
await expect(viewer.page).toHaveURL(/index=1/);
// The child shape (HoverQuickToolArrowRight) has a click interaction.
// Its shape-container g element has cursor="pointer" set via SVG attribute.
// The parent (HighlightState) only has mouse-leave, so cursor is not "pointer".
const childShapeContainer = viewer.page
.locator('svg.main_ui_viewer_interactions__not-fixed g[cursor="pointer"]')
.first();
await expect(childShapeContainer).toBeVisible();
// Clicking on the child should navigate to Screen3.
// Bug (before fix): parent's interaction overlay rect is rendered on top and blocks this click.
// Note: Screen3 is at index=0 in the viewer (frames sorted by Z-index, highest first).
await childShapeContainer.click();
await expect(viewer.page).toHaveURL(/index=0/, { timeout: 5000 });
});
test("Navigate with arrows", async ({ page }) => {
const viewer = new ViewerPage(page);
await viewer.setupLoggedInUser();

View File

@ -291,6 +291,7 @@
:stroke "var(--color-accent-tertiary)"
:stroke-width (if show-interactions 1 0)
:fill-opacity (if show-interactions 0.2 0)
:pointer-events "none"
:transform (gsh/transform-str shape)}])))