🐛 Fix locked flex and grid elements cannot be selected in viewer role (#9865)

* 🐛 Fix locked flex and grid elements cannot be selected in viewer role

*  Add playwright test
This commit is contained in:
Luis de Dios 2026-06-01 08:30:27 +02:00 committed by GitHub
parent a5c8bcaf9e
commit 0b56fd2f77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 194 additions and 4 deletions

View File

@ -313,6 +313,9 @@
(top-nested-frame objects position nil))
([objects position excluded]
(top-nested-frame objects position excluded false))
([objects position excluded read-only?]
(assert (or (nil? excluded) (set? excluded)))
(let [frames (cond->> (get-frames-by-position objects position)
@ -323,7 +326,8 @@
:always
(remove #(or ^boolean (true? (:hidden %))
^boolean (true? (:blocked %)))))
^boolean (and (true? (:blocked %))
(not read-only?)))))
frame-set (into #{} (map #(dm/get-prop % :id)) frames)]

View File

@ -0,0 +1,144 @@
{
"~:features": {
"~#set": [
"fdata/path-data",
"plugins/runtime",
"design-tokens/v1",
"variants/v1",
"layout/grid",
"styles/v2",
"fdata/objects-map",
"tokens/numeric-input",
"render-wasm/v1",
"components/v2",
"fdata/shape-data-type"
]
},
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f7920a",
"~:permissions": {
"~:type": "~:membership",
"~:is-owner": false,
"~:is-admin": false,
"~:can-edit": false,
"~:can-read": true,
"~:is-logged": true
},
"~:has-media-trimmed": false,
"~:comment-thread-seqn": 0,
"~:name": "New File 1",
"~:revn": 35,
"~:modified-at": "~m1779815420126",
"~:vern": 0,
"~:id": "~u35e4ba42-fc4e-817d-8008-12287202d225",
"~:is-shared": false,
"~:migrations": {
"~#ordered-set": [
"legacy-2",
"legacy-3",
"legacy-5",
"legacy-6",
"legacy-7",
"legacy-8",
"legacy-9",
"legacy-10",
"legacy-11",
"legacy-12",
"legacy-13",
"legacy-14",
"legacy-16",
"legacy-17",
"legacy-18",
"legacy-19",
"legacy-25",
"legacy-26",
"legacy-27",
"legacy-28",
"legacy-29",
"legacy-31",
"legacy-32",
"legacy-33",
"legacy-34",
"legacy-36",
"legacy-37",
"legacy-38",
"legacy-39",
"legacy-40",
"legacy-41",
"legacy-42",
"legacy-43",
"legacy-44",
"legacy-45",
"legacy-46",
"legacy-47",
"legacy-48",
"legacy-49",
"legacy-50",
"legacy-51",
"legacy-52",
"legacy-53",
"legacy-54",
"legacy-55",
"legacy-56",
"legacy-57",
"legacy-59",
"legacy-62",
"legacy-65",
"legacy-66",
"legacy-67",
"0001-remove-tokens-from-groups",
"0002-normalize-bool-content-v2",
"0002-clean-shape-interactions",
"0003-fix-root-shape",
"0003-convert-path-content-v2",
"0005-deprecate-image-type",
"0006-fix-old-texts-fills",
"0008-fix-library-colors-v4",
"0009-clean-library-colors",
"0009-add-partial-text-touched-flags",
"0010-fix-swap-slots-pointing-non-existent-shapes",
"0011-fix-invalid-text-touched-flags",
"0012-fix-position-data",
"0013-fix-component-path",
"0013-clear-invalid-strokes-and-fills",
"0014-fix-tokens-lib-duplicate-ids",
"0014-clear-components-nil-objects",
"0015-fix-text-attrs-blank-strings",
"0015-clean-shadow-color",
"0016-copy-fills-from-position-data-to-text-node",
"0017-fix-layout-flex-dir",
"0018-remove-unneeded-objects-from-components",
"0019-fix-missing-swap-slots",
"0020-sync-component-id-with-near-main",
"0021-fix-shape-svg-attrs",
"0022-normalize-component-root-and-resync",
"0021-repair-bad-tokens"
]
},
"~:version": 67,
"~:project-id": "~u35e4ba42-fc4e-817d-8008-12286b51ef68",
"~:created-at": "~m1779652715531",
"~:backend": "legacy-db",
"~:data": {
"~:pages": [
"~u35e4ba42-fc4e-817d-8008-12287202d226"
],
"~:pages-index": {
"~u35e4ba42-fc4e-817d-8008-12287202d226": {
"~:objects": {
"~#penpot/objects-map/v2": {
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.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.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",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,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~u76c88c49-9f53-80db-8008-1494b9c81dca\",\"~u76c88c49-9f53-80db-8008-1494bf3f9d6f\"]]]",
"~u76c88c49-9f53-80db-8008-1494b9c81dca": "[\"~#shape\",[\"^ \",\"~:y\",-243,\"~:layout-grid-columns\",[[\"^ \",\"~:type\",\"~:flex\",\"~:value\",1],[\"^ \",\"^2\",\"^3\",\"^4\",1]],\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",0,\"~:p3\",0,\"~:p4\",0],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:layout\",\"~:grid\",\"~:hide-in-viewer\",false,\"~:name\",\"Unlocked board with Grid\",\"~:layout-align-items\",\"~:start\",\"~:width\",462,\"~:layout-grid-cells\",[\"^ \",\"~u76c88c49-9f53-80db-8008-1495083eb543\",[\"^ \",\"~:justify-self\",\"~:auto\",\"~:column\",1,\"~:id\",\"~u76c88c49-9f53-80db-8008-1495083eb543\",\"~:position\",\"^L\",\"~:column-span\",1,\"~:align-self\",\"^L\",\"~:row\",1,\"~:row-span\",1,\"~:shapes\",[]],\"~u76c88c49-9f53-80db-8008-1495083eb544\",[\"^ \",\"^K\",\"^L\",\"^M\",2,\"^N\",\"~u76c88c49-9f53-80db-8008-1495083eb544\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",1,\"^S\",1,\"^T\",[]],\"~u76c88c49-9f53-80db-8008-1495083eb545\",[\"^ \",\"^K\",\"^L\",\"^M\",1,\"^N\",\"~u76c88c49-9f53-80db-8008-1495083eb545\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",2,\"^S\",1,\"^T\",[]],\"~u76c88c49-9f53-80db-8008-1495083eb546\",[\"^ \",\"^K\",\"^L\",\"^M\",2,\"^N\",\"~u76c88c49-9f53-80db-8008-1495083eb546\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",2,\"^S\",1,\"^T\",[]]],\"~:layout-padding-type\",\"~:simple\",\"^2\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",241.99999451637268,\"~:y\",-243]],[\"^10\",[\"^ \",\"~:x\",703.9999945163727,\"~:y\",-243]],[\"^10\",[\"^ \",\"~:x\",703.9999945163727,\"~:y\",-53]],[\"^10\",[\"^ \",\"~:x\",241.99999451637268,\"~:y\",-53]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",0],\"~:transform-inverse\",[\"^>\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:layout-justify-content\",\"~:stretch\",\"~:r1\",0,\"^N\",\"~u76c88c49-9f53-80db-8008-1494b9c81dca\",\"~:layout-justify-items\",\"^G\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-align-content\",\"^19\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",241.99999451637268,\"~:proportion\",1,\"~:r4\",0,\"~:layout-grid-rows\",[[\"^ \",\"^2\",\"^3\",\"^4\",1],[\"^ \",\"^2\",\"^3\",\"^4\",1]],\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",241.99999451637268,\"~:y\",-243,\"^H\",462,\"~:height\",190,\"~:x1\",241.99999451637268,\"~:y1\",-243,\"~:x2\",703.9999945163727,\"~:y2\",-53]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:layout-grid-dir\",\"^R\",\"~:flip-x\",null,\"^1E\",190,\"~:flip-y\",null,\"^T\",[]]]",
"~u76c88c49-9f53-80db-8008-1494bf3f9d6f": "[\"~#shape\",[\"^ \",\"~:y\",-412,\"~:layout-grid-columns\",[[\"^ \",\"~:type\",\"~:flex\",\"~:value\",1],[\"^ \",\"^2\",\"^3\",\"^4\",1]],\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",0,\"~:p3\",0,\"~:p4\",0],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:layout\",\"~:grid\",\"~:hide-in-viewer\",false,\"~:name\",\"Locked Board with Grid\",\"~:layout-align-items\",\"~:start\",\"~:width\",459.00002002716064,\"~:layout-grid-cells\",[\"^ \",\"~u76c88c49-9f53-80db-8008-1495048fa9c0\",[\"^ \",\"~:justify-self\",\"~:auto\",\"~:column\",1,\"~:id\",\"~u76c88c49-9f53-80db-8008-1495048fa9c0\",\"~:position\",\"^L\",\"~:column-span\",1,\"~:align-self\",\"^L\",\"~:row\",1,\"~:row-span\",1,\"~:shapes\",[]],\"~u76c88c49-9f53-80db-8008-1495048fa9c1\",[\"^ \",\"^K\",\"^L\",\"^M\",2,\"^N\",\"~u76c88c49-9f53-80db-8008-1495048fa9c1\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",1,\"^S\",1,\"^T\",[]],\"~u76c88c49-9f53-80db-8008-1495048fa9c2\",[\"^ \",\"^K\",\"^L\",\"^M\",1,\"^N\",\"~u76c88c49-9f53-80db-8008-1495048fa9c2\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",2,\"^S\",1,\"^T\",[]],\"~u76c88c49-9f53-80db-8008-1495048fa9c3\",[\"^ \",\"^K\",\"^L\",\"^M\",2,\"^N\",\"~u76c88c49-9f53-80db-8008-1495048fa9c3\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",2,\"^S\",1,\"^T\",[]]],\"~:layout-padding-type\",\"~:simple\",\"^2\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",241.99999445676804,\"~:y\",-412]],[\"^10\",[\"^ \",\"~:x\",701.0000144839287,\"~:y\",-412]],[\"^10\",[\"^ \",\"~:x\",701.0000144839287,\"~:y\",-283]],[\"^10\",[\"^ \",\"~:x\",241.99999445676804,\"~:y\",-283]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",0],\"~:transform-inverse\",[\"^>\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:layout-justify-content\",\"~:stretch\",\"~:r1\",0,\"^N\",\"~u76c88c49-9f53-80db-8008-1494bf3f9d6f\",\"~:layout-justify-items\",\"^G\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-align-content\",\"^19\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",241.99999445676804,\"~:blocked\",true,\"~:proportion\",1,\"~:r4\",0,\"~:layout-grid-rows\",[[\"^ \",\"^2\",\"^3\",\"^4\",1],[\"^ \",\"^2\",\"^3\",\"^4\",1]],\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",241.99999445676804,\"~:y\",-412,\"^H\",459.00002002716064,\"~:height\",129,\"~:x1\",241.99999445676804,\"~:y1\",-412,\"~:x2\",701.0000144839287,\"~:y2\",-283]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:layout-grid-dir\",\"^R\",\"~:flip-x\",null,\"^1F\",129,\"~:flip-y\",null,\"^T\",[]]]"
}
},
"~:id": "~u35e4ba42-fc4e-817d-8008-12287202d226",
"~:name": "Page 1"
}
},
"~:id": "~u35e4ba42-fc4e-817d-8008-12287202d225",
"~:options": {
"~:components-v2": true,
"~:base-font-size": "16px"
}
}
}

View File

@ -521,3 +521,39 @@ test("BUG 13822 - Problems with z-index", async({
await workspacePage.waitForFirstRenderWithoutUI();
await expect(workspacePage.canvas).toHaveScreenshot();
});
test("Bug 14250 - User with viewer role can select a locked board with a grid", async ({
page,
}) => {
const workspacePage = new WasmWorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.mockRPC("get-teams", "get-teams-role-viewer.json");
await workspacePage.mockRPC(
/get\-file\?/,
"workspace/get-file-14250.json",
);
await workspacePage.goToWorkspace();
// Select the board from the layer tree to reveal its position
// on the canvas via the selection rectangle overlay
await workspacePage.clickLeafLayer("Locked Board with Grid");
await page.waitForSelector(".viewport-selrect");
// Get the selection rectangle bounding box (page coordinates)
// and calculate its center relative to the viewport element
const selrectBox = await page.locator(".viewport-selrect").boundingBox();
const viewportBox = await workspacePage.viewport.boundingBox();
const centerX = selrectBox.x + selrectBox.width / 2 - viewportBox.x;
const centerY = selrectBox.y + selrectBox.height / 2 - viewportBox.y;
// Deselect by pressing Escape
await page.keyboard.press("Escape");
// Click on the canvas at the board's center
await workspacePage.clickAt(centerX, centerY);
// Verify the board is now selected in the layers bar
await workspacePage.expectSelectedLayer("Locked Board with Grid");
});

View File

@ -185,6 +185,7 @@
hover-disabled-ref (mf/use-ref hover-disabled?)
focus-ref (mf/use-ref focus)
transform-ref (mf/use-ref transform)
read-only-ref (mf/use-ref read-only?)
last-point-ref (mf/use-var nil)
mod-str (mf/use-memo #(rx/subject))
@ -256,11 +257,15 @@
(mf/deps transform)
#(mf/set-ref-val! transform-ref transform))
(mf/use-effect
(mf/deps read-only?)
#(mf/set-ref-val! read-only-ref read-only?))
(hooks/use-stream
over-shapes-stream-debounced
(mf/deps objects)
(fn [_]
(reset! hover-top-frame-id (ctt/top-nested-frame objects (deref last-point-ref)))))
(reset! hover-top-frame-id (ctt/top-nested-frame objects (deref last-point-ref) nil (mf/ref-val read-only-ref)))))
;; This ref is a cache of sorted ids. Sorting is expensive so we save the list
(let [sorted-ids-cache (mf/use-ref {})]

View File

@ -113,7 +113,8 @@
(mf/use-fn
(mf/deps (:id frame) on-frame-select workspace-read-only? blocked?)
(fn [event]
(when (and (dom/left-mouse? event) (not blocked?))
(when (and (dom/left-mouse? event)
(or (not blocked?) workspace-read-only?))
(dom/prevent-default event)
(dom/stop-propagation event)
(on-frame-select event (:id frame)))))
@ -198,7 +199,7 @@
[:g.frame-title {:id (dm/str "frame-title-" (:id frame))
:data-edit-grid is-grid-edition
:transform (vwu/title-transform frame zoom is-grid-edition)
:pointer-events (when (:blocked frame) "none")}
:pointer-events (when (and (:blocked frame) (not workspace-read-only?)) "none")}
(when show-icon?
[:svg {:x 0
:y -9