From a3f7a1def63754f7b1c8ca30556a0c92bcee3c33 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Mon, 13 Apr 2026 18:29:15 +0200 Subject: [PATCH] :bug: Fix bugs with multiselection (#8932) * :bug: Fix app crash when selecting shapes with one hidden * :bug: Fix opacity input when mixed values --- CHANGES.md | 2 + .../workspace/get-file-fragment-tokens.json | 15 ++++-- .../playwright/ui/specs/tokens/apply.spec.js | 54 +++++++++++++++++++ .../sidebar/options/menus/layer.cljs | 9 ++-- 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8117220348..5f92376246 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -47,6 +47,8 @@ - Fix selected color tokens [Taiga #13930](https://tree.taiga.io/project/penpot/issue/13930) - Fix dashboard Recent/Deleted titles overlapped by scrolling content (by @rockchris99) [Github #8577](https://github.com/penpot/penpot/issues/8577) - Display resolved values of inactive tokens [Taiga #13628](https://tree.taiga.io/project/penpot/issue/13628) +- Fix app crash when selecting shapes with one hidden [Taiga #13959](https://tree.taiga.io/project/penpot/issue/13959) +- Fix opacity mixed value [Taiga #13960](https://tree.taiga.io/project/penpot/issue/13960) ## 2.15.0 (Unreleased) diff --git a/frontend/playwright/data/workspace/get-file-fragment-tokens.json b/frontend/playwright/data/workspace/get-file-fragment-tokens.json index 128f45d28d..69061c79eb 100644 --- a/frontend/playwright/data/workspace/get-file-fragment-tokens.json +++ b/frontend/playwright/data/workspace/get-file-fragment-tokens.json @@ -186,7 +186,8 @@ }, "~:fills": [ { - "~:fill-color": "#7f9cf5" + "~:fill-color": "#7f9cf5", + "~:fill-opacity": 1 } ], "~:flip-x": null, @@ -235,7 +236,8 @@ "~:letter-spacing": "0", "~:fills": [ { - "~:fill-color": "#ffffff" + "~:fill-color": "#ffffff", + "~:fill-opacity": 1 } ], "~:font-family": "sourcesanspro", @@ -257,7 +259,8 @@ "~:letter-spacing": "0", "~:fills": [ { - "~:fill-color": "#ffffff" + "~:fill-color": "#ffffff", + "~:fill-opacity": 1 } ], "~:font-family": "sourcesanspro" @@ -328,7 +331,8 @@ "~:y2": 37.33333456516266, "~:fills": [ { - "~:fill-color": "#ffffff" + "~:fill-color": "#ffffff", + "~:fill-opacity": 1 } ], "~:x2": 86.60417175292969, @@ -445,7 +449,8 @@ }, "~:fills": [ { - "~:fill-color": "#ffffff" + "~:fill-color": "#ffffff", + "~:fill-opacity": 1 } ], "~:flip-x": null, diff --git a/frontend/playwright/ui/specs/tokens/apply.spec.js b/frontend/playwright/ui/specs/tokens/apply.spec.js index bda71e08ba..a866fefe6a 100644 --- a/frontend/playwright/ui/specs/tokens/apply.spec.js +++ b/frontend/playwright/ui/specs/tokens/apply.spec.js @@ -927,3 +927,57 @@ test.describe("Tokens: Detach token", () => { await expect(brokenPill).not.toBeVisible(); }); }); + +test("Bug: 13959, User select shapes with different hidden state.", async ({ + page, +}) => { + const { workspacePage } = + await setupTokensFileRender(page); + + await page.getByRole("tab", { name: "Layers" }).click(); + + await workspacePage.layers.getByTestId("layer-row").nth(1).click(); + const layerMenuSection = page.getByRole("region", { + name: "Layer menu section", + }); + await expect(layerMenuSection).toBeVisible(); + await layerMenuSection + .getByRole("button", { name: "Toggle layer visibility" }) + .click(); + await expect(layerMenuSection).toBeVisible(); + await workspacePage.layers + .getByTestId("layer-row") + .nth(0) + .click({ modifiers: ["Shift"] }); + await expect(layerMenuSection).toBeVisible(); +}); + +test("Bug: 13960, User select shapes with different opacity and input show mixed state.", async ({ + page, +}) => { + const { workspacePage } = + await setupTokensFileRender(page); + + await page.getByRole("tab", { name: "Layers" }).click(); + + await workspacePage.layers.getByTestId("layer-row").nth(1).click(); + const layerMenuSection = page.getByRole("region", { + name: "Layer menu section", + }); + await expect(layerMenuSection).toBeVisible(); + await layerMenuSection + .getByRole('textbox', { name: 'Opacity' }) + .fill('50'); + await expect(layerMenuSection).toBeVisible(); + await workspacePage.layers + .getByTestId("layer-row") + .nth(0) + .click({ modifiers: ["Shift"] }); + await expect(layerMenuSection + .getByRole('textbox', { name: 'Opacity' })).toBeVisible(); + await expect(layerMenuSection + .getByRole('textbox', { name: 'Opacity' })).toBeVisible(); + + await expect(layerMenuSection + .getByRole('textbox', { name: 'Opacity' })).toHaveAttribute("placeholder", "Mixed"); +}); diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs index 3c08d9d6d8..41ec031902 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs @@ -69,6 +69,8 @@ hidden? (get values :hidden) blocked? (get values :blocked) + opacity (get values :opacity) + on-detach-token (mf/use-fn (mf/deps ids) @@ -237,10 +239,11 @@ (tr "settings.multiple") "--") :align :right - :disabled hidden? + :disabled (if (or (= :multiple hidden?) hidden?) true false) :class (stl/css :numeric-input-wrapper) - :value (* 100 - (or (get values :opacity) 1))}] + :value (if (= :multiple opacity) + opacity + (* 100 (d/nilv opacity 1)))}] (cond (or (= :multiple hidden?) (not hidden?))