From 3724f5b9f37f699a7df4498dede9e5250cc18347 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Fri, 17 Apr 2026 14:06:54 +0200 Subject: [PATCH] WIP kakota --- common/src/app/common/attrs.cljc | 19 +- common/test/common_tests/attrs_test.cljc | 159 ++++++++++++ common/test/common_tests/runner.cljc | 2 + .../ui/specs/multiselection.spec.js | 234 ++++++++++++++++++ .../playwright/ui/specs/tokens/apply.spec.js | 2 +- frontend/src/app/main/data/workspace.cljs | 2 +- .../src/app/main/data/workspace/texts.cljs | 2 +- .../workspace/sidebar/options/menus/fill.cljs | 3 +- .../sidebar/options/menus/grid_cell.cljs | 2 +- .../sidebar/options/menus/stroke.cljs | 2 +- .../workspace/sidebar/options/menus/text.cljs | 3 +- .../sidebar/options/shapes/multiple.cljs | 6 +- 12 files changed, 418 insertions(+), 18 deletions(-) create mode 100644 common/test/common_tests/attrs_test.cljc create mode 100644 frontend/playwright/ui/specs/multiselection.spec.js diff --git a/common/src/app/common/attrs.cljc b/common/src/app/common/attrs.cljc index 93211bad42..12df6e01a9 100644 --- a/common/src/app/common/attrs.cljc +++ b/common/src/app/common/attrs.cljc @@ -85,11 +85,10 @@ ;; :r4 nil} ;; (defn get-attrs-multi - ([objs attrs] - (get-attrs-multi objs attrs default-equal identity)) - - ([objs attrs eqfn sel] + ([objs attrs origin] + (get-attrs-multi objs attrs origin default-equal identity)) + ([objs attrs origin eqfn sel] (loop [attr (first attrs) attrs (rest attrs) result (transient {})] @@ -107,7 +106,11 @@ :multiple value) (= new-val :multiple) :multiple - (= value ::unset) (sel new-val) + (= value ::unset) + (if (and (= origin :text-multi)(= attr :typography-ref-id)) + :multiple + (sel new-val)) + (eqfn new-val value) value :else :multiple)] @@ -134,6 +137,6 @@ text-node-attrs (->> attrs (filter (set txt/text-node-attrs)))] (merge defaults - (get-attrs-multi (->> (txt/node-seq txt/is-root-node? content)) root-attrs) - (get-attrs-multi (->> (txt/node-seq txt/is-paragraph-node? content)) paragraph-attrs) - (get-attrs-multi (->> (txt/node-seq txt/is-text-node? content)) text-node-attrs)))) + (get-attrs-multi (->> (txt/node-seq txt/is-root-node? content)) root-attrs :text-multi) + (get-attrs-multi (->> (txt/node-seq txt/is-paragraph-node? content)) paragraph-attrs :text-multi) + (get-attrs-multi (->> (txt/node-seq txt/is-text-node? content)) text-node-attrs :text-multi)))) diff --git a/common/test/common_tests/attrs_test.cljc b/common/test/common_tests/attrs_test.cljc new file mode 100644 index 0000000000..ece9ff47fe --- /dev/null +++ b/common/test/common_tests/attrs_test.cljc @@ -0,0 +1,159 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns common-tests.attrs-test + (:require + [app.common.attrs :as attrs] + [clojure.test :as t])) + +(t/deftest get-attrs-multi-same-value + (t/testing "returns value when all objects have the same attribute value" + (let [objs [{:attr "red"} + {:attr "red"} + {:attr "red"}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {:attr "red"} result)))) + + (t/testing "returns nil when all objects have nil value" + (let [objs [{:attr nil} + {:attr nil}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {:attr nil} result))))) + +(t/deftest get-attrs-multi-different-values + (t/testing "returns :multiple when objects have different concrete values" + (let [objs [{:attr "red"} + {:attr "blue"}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {:attr :multiple} result))))) + +(t/deftest get-attrs-multi-missing-key + (t/testing "returns :multiple when one object has the attribute and another doesn't" + (let [objs [{:attr "red"} + {:other "value"}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {:attr :multiple} result)))) + + (t/testing "returns :multiple when one object has UUID and another is missing" + (let [uuid #uuid "550e8400-e29b-41d4-a716-446655440000" + objs [{:attr uuid} + {:other "value"}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {:attr :multiple} result)))) + + (t/testing "returns value when missing key comes first and is not text" + (let [objs [{:other "value"} + {:attr "red"}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {:attr "red"} result)))) + + (t/testing "returns :multiple when some objects have the key and some don't" + (let [objs [{:attr "red"} + {:other "value"} + {:attr "blue"}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {:attr :multiple} result)))) + + (t/testing "returns :multiple when one object has nil and another is missing" + (let [objs [{:attr nil} + {:other "value"}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {:attr :multiple} result))))) + +(t/deftest get-attrs-multi-all-missing + (t/testing "all missing → attribute NOT included in result" + (let [objs [{:other "value"} + {:different "data"}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {} result) + "Attribute should not be in result when all objects are missing"))) + + (t/testing "all missing with empty maps → attribute NOT included" + (let [objs [{} {}] + result (attrs/get-attrs-multi objs [:attr] :multi)] + (t/is (= {} result) + "Attribute should not be in result")))) + +(t/deftest get-attrs-multi-multiple-attributes + (t/testing "handles multiple attributes with different merge results" + (let [objs [{:attr1 "red" :attr2 "blue"} + {:attr1 "red" :attr2 "green"} + {:attr1 "red"}] ; :attr2 missing + result (attrs/get-attrs-multi objs [:attr1 :attr2] :multi)] + (t/is (= {:attr1 "red" :attr2 :multiple} result)))) + + (t/testing "handles mixed scenarios: same, different, and missing" + (let [uuid #uuid "550e8400-e29b-41d4-a716-446655440000" + uuid2 #uuid "550e8400-e29b-41d4-a716-446655440001" + objs [{:id :a :ref uuid} + {:id :b :ref uuid2} + {:id :c}] ; :ref missing + result (attrs/get-attrs-multi objs [:id :ref] :multi)] + (t/is (= {:id :multiple :ref :multiple} result))))) + +(t/deftest get-attrs-multi-typography-ref-id-scenario + (t/testing "the specific bug scenario: typography-ref-id with UUID vs missing" + (let [uuid #uuid "550e8400-e29b-41d4-a716-446655440000" + ;; Shape 1 has typography-ref-id with a UUID + shape1 {:id :shape1 :typography-ref-id uuid} + ;; Shape 2 does NOT have typography-ref-id at all + shape2 {:id :shape2} + result (attrs/get-attrs-multi [shape1 shape2] [:typography-ref-id] :multi)] + (t/is (= {:typography-ref-id :multiple} result) + "Expected :multiple when one shape has the UUID and another is missing the key"))) + + (t/testing "both shapes missing → attribute NOT included in result" + (let [shape1 {:id :shape1} + shape2 {:id :shape2} + result (attrs/get-attrs-multi [shape1 shape2] [:typography-ref-id] :multi)] + (t/is (= {} result) + "Expected empty map when all shapes are missing the attribute")))) + +(t/deftest get-attrs-multi-bug-missing-vs-present + (t/testing "BUG FIXED: one shape has :typography-ref-id, other does NOT → returns :multiple" + (let [uuid #uuid "550e8400-e29b-41d4-a716-446655440000" + shape1 {:id :shape1 :typography-ref-id uuid} + shape2 {:id :shape2} + result (attrs/get-attrs-multi [shape1 shape2] [:typography-ref-id] :multi)] + (t/is (= {:typography-ref-id :multiple} result) + "Expected :multiple when one shape has ref and another doesn't"))) + + (t/testing "both missing → empty map (attribute not in result)" + (let [shape1 {:id :shape1} + shape2 {:id :shape2} + result (attrs/get-attrs-multi [shape1 shape2] [:typography-ref-id] :multi)] + (t/is (= {} result) + "Expected empty map when all shapes are missing the attribute"))) + + (t/testing "both equal values → return the value" + (let [uuid #uuid "550e8400-e29b-41d4-a716-446655440000" + shape1 {:id :shape1 :typography-ref-id uuid} + shape2 {:id :shape2 :typography-ref-id uuid} + result (attrs/get-attrs-multi [shape1 shape2] [:typography-ref-id] :multi-text)] + (t/is (= {:typography-ref-id uuid} result)))) + + (t/testing "different values → return :multiple" + (let [uuid1 #uuid "550e8400-e29b-41d4-a716-446655440000" + uuid2 #uuid "550e8400-e29b-41d4-a716-446655440001" + shape1 {:id :shape1 :typography-ref-id uuid1} + shape2 {:id :shape2 :typography-ref-id uuid2} + result (attrs/get-attrs-multi [shape1 shape2] [:typography-ref-id] :multi)] + (t/is (= {:typography-ref-id :multiple} result))))) + +(t/deftest get-attrs-multi-default-equal + (t/testing "numbers use close? for equality" + (let [objs [{:value 1.0} + {:value 1.0000001}] + result (attrs/get-attrs-multi objs [:value] :multi)] + (t/is (= {:value 1.0} result) + "Numbers within tolerance should be considered equal"))) + + (t/testing "different floating point positions beyond tolerance are :multiple" + (let [objs [{:x -26} + {:x -153}] + result (attrs/get-attrs-multi objs [:x] :multi)] + (t/is (= {:x :multiple} result) + "Different positions should be :multiple")))) diff --git a/common/test/common_tests/runner.cljc b/common/test/common_tests/runner.cljc index 1ba7242d58..e8fd6ac9a9 100644 --- a/common/test/common_tests/runner.cljc +++ b/common/test/common_tests/runner.cljc @@ -8,6 +8,7 @@ (:require #?(:clj [common-tests.fressian-test]) [clojure.test :as t] + [common-tests.attrs-test] [common-tests.buffer-test] [common-tests.colors-test] [common-tests.data-test] @@ -85,6 +86,7 @@ (defn -main [& args] (t/run-tests + 'common-tests.attrs-test 'common-tests.buffer-test 'common-tests.colors-test 'common-tests.data-test diff --git a/frontend/playwright/ui/specs/multiselection.spec.js b/frontend/playwright/ui/specs/multiselection.spec.js new file mode 100644 index 0000000000..d1d2c5e397 --- /dev/null +++ b/frontend/playwright/ui/specs/multiselection.spec.js @@ -0,0 +1,234 @@ +import { test, expect } from "@playwright/test"; +import { WasmWorkspacePage } from "../pages/WasmWorkspacePage"; + +test.beforeEach(async ({ page }) => { + await WasmWorkspacePage.init(page); +}); + +test("Multiselection - check multiple values in measures", async ({ page }) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(page); + await workspacePage.mockRPC( + /get\-file\?/, + "workspace/get-file-copy-paste.json", + ); + await workspacePage.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-copy-paste-fragment.json", + ); + + await workspacePage.goToWorkspace({ + fileId: "870f9f10-87b5-8137-8005-934804124660", + pageId: "870f9f10-87b5-8137-8005-934804124661", + }); + + // Select first shape (single selection first) + await page.getByTestId("layer-item").getByRole("button").first().click(); + await workspacePage.layers.getByTestId("layer-row").nth(0).click(); + + // === CHECK SINGLE SELECTION - ALL MEASURE FIELDS === + const measuresSection = workspacePage.rightSidebar.getByRole('region', { name: 'shape-measures-section' }); + await expect(measuresSection).toBeVisible(); + + // Width + const widthInput = measuresSection.getByTitle('Width', { exact: true }).getByRole('textbox'); + await expect(widthInput).toHaveValue("360"); + + // Height + const heightInput = measuresSection.getByTitle('Height', { exact: true }).getByRole('textbox'); + await expect(heightInput).toHaveValue("53"); + + // X Position (using "X axis" title) + const xPosInput = measuresSection.getByTitle('X axis', { exact: true }).getByRole('textbox'); + await expect(xPosInput).toHaveValue("1094"); + + // Y Position (using "Y axis" title) + const yPosInput = measuresSection.getByTitle('Y axis', { exact: true }).getByRole('textbox'); + await expect(yPosInput).toHaveValue("856"); + + // === CHECK MULTI-SELECTION - MIXED VALUES === + // Shift+click to add second layer to selection + await workspacePage.layers.getByTestId("layer-row").nth(1).click({ modifiers: ['Shift'] }); + + // All measure fields should show "Mixed" placeholder when values differ + await expect(widthInput).toHaveAttribute('placeholder', 'Mixed'); + await expect(heightInput).toHaveAttribute('placeholder', 'Mixed'); + await expect(xPosInput).toHaveAttribute('placeholder', 'Mixed'); + await expect(yPosInput).toHaveAttribute('placeholder', 'Mixed'); +}); + +test("Multiselection - check typography multiple values", async ({ page }) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(page); + await workspacePage.mockRPC( + /get\-file\?/, + "workspace/get-file-copy-paste.json", + ); + await workspacePage.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-copy-paste-fragment.json", + ); + + await workspacePage.goToWorkspace({ + fileId: "870f9f10-87b5-8137-8005-934804124660", + pageId: "870f9f10-87b5-8137-8005-934804124661", + }); + + await page.getByTestId("layer-item").getByRole("button").first().click(); + await workspacePage.layers.getByTestId("layer-row").nth(0).click(); + + // Text section + const textSection = workspacePage.rightSidebar.getByRole('region', { name: "Text section" }); + await expect(textSection).toBeVisible(); + + // Single selection - show typography name (not multiple) + await expect(textSection.getByText("Multiple typographies")).not.toBeVisible(); + + // Multi-selection + await workspacePage.layers.getByTestId("layer-row").nth(1).click({ modifiers: ['Shift'] }); + + // Should show "Multiple typographies" + await expect(textSection.getByText("Multiple typographies")).toBeVisible(); +}); + +test("Multiselection - check fill multiple values", async ({ page }) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(page); + await workspacePage.mockRPC( + /get\-file\?/, + "workspace/get-file-copy-paste.json", + ); + await workspacePage.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-copy-paste-fragment.json", + ); + + await workspacePage.goToWorkspace({ + fileId: "870f9f10-87b5-8137-8005-934804124660", + pageId: "870f9f10-87b5-8137-8005-934804124661", + }); + + await page.getByTestId("layer-item").getByRole("button").first().click(); + await workspacePage.layers.getByTestId("layer-row").nth(0).click(); + + // Fill section + const fillSection = workspacePage.rightSidebar.getByRole('region', { name: "Fill section" }); + await expect(fillSection).toBeVisible(); + + // Single selection - fill color should be visible (not "Mixed") + await expect(fillSection.getByText(/Mixed/i)).not.toBeVisible(); + + // Multi-selection with Shift+click + await workspacePage.layers.getByTestId("layer-row").nth(1).click({ modifiers: ['Shift'] }); + + // Should show "Mixed" for fills when shapes have different fill colors + await expect(fillSection.getByText('Mixed')).toBeVisible(); +}); + +test("Multiselection - check stroke multiple values", async ({ page }) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(page); + await workspacePage.mockRPC( + /get\-file\?/, + "workspace/get-file-copy-paste.json", + ); + await workspacePage.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-copy-paste-fragment.json", + ); + + await workspacePage.goToWorkspace({ + fileId: "870f9f10-87b5-8137-8005-934804124660", + pageId: "870f9f10-87b5-8137-8005-934804124661", + }); + + await page.getByTestId("layer-item").getByRole("button").first().click(); + await workspacePage.layers.getByTestId("layer-row").nth(0).click(); + + // Stroke section + const strokeSection = workspacePage.rightSidebar.getByRole('region', { name: "Stroke section" }); + await expect(strokeSection).toBeVisible(); + + // Single selection - stroke should be visible (not "Mixed") + await expect(strokeSection.getByText(/Mixed/i)).not.toBeVisible(); + + // Multi-selection + await workspacePage.layers.getByTestId("layer-row").nth(1).click({ modifiers: ['Shift'] }); + + // Should show "Mixed" for strokes when shapes have different stroke colors + await expect(strokeSection.getByText('Mixed')).toBeVisible(); +}); + +test("Multiselection - check rotation multiple values", async ({ page }) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(page); + await workspacePage.mockRPC( + /get\-file\?/, + "workspace/get-file-copy-paste.json", + ); + await workspacePage.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-copy-paste-fragment.json", + ); + + await workspacePage.goToWorkspace({ + fileId: "870f9f10-87b5-8137-8005-934804124660", + pageId: "870f9f10-87b5-8137-8005-934804124661", + }); + + await page.getByTestId("layer-item").getByRole("button").first().click(); + await workspacePage.layers.getByTestId("layer-row").nth(1).click(); + + // Measures section contains rotation + const measuresSection = workspacePage.rightSidebar.getByRole('region', { name: 'shape-measures-section' }); + await expect(measuresSection).toBeVisible(); + + // Rotation field exists + const rotationInput = measuresSection.getByTitle('Rotation', { exact: true }).getByRole('textbox'); + await expect(rotationInput).toBeVisible(); + + // Rotate that shape + await rotationInput.fill("45"); + await page.keyboard.press('Enter'); + await expect(rotationInput).toHaveValue("45"); // Rotation should be 45 + + // Multi-selection + await workspacePage.layers.getByTestId("layer-row").nth(0).click({ modifiers: ['Shift'] }); + + // Rotation should show "Mixed" placeholder + await expect(rotationInput).toHaveAttribute('placeholder', 'Mixed'); +}); + +test("Multiselection typographies in reverse order", async ({ page }) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(page); + await workspacePage.mockRPC( + /get\-file\?/, + "workspace/get-file-copy-paste.json", + ); + await workspacePage.mockRPC( + "get-file-fragment?file-id=*&fragment-id=*", + "workspace/get-file-copy-paste-fragment.json", + ); + + await workspacePage.goToWorkspace({ + fileId: "870f9f10-87b5-8137-8005-934804124660", + pageId: "870f9f10-87b5-8137-8005-934804124661", + }); + + await page.getByTestId("layer-item").getByRole("button").first().click(); + await workspacePage.layers.getByTestId("layer-row").nth(1).click(); + + // Text section + const textSection = workspacePage.rightSidebar.getByRole('region', { name: "Text section" }); + await expect(textSection).not.toBeVisible(); + + // Single selection - show typography name (not multiple) + await expect(textSection.getByText("Multiple typographies")).not.toBeVisible(); + + // Multi-selection + await workspacePage.layers.getByTestId("layer-row").nth(0).click({ modifiers: ['Shift'] }); + await expect(textSection).toBeVisible(); + // Should show "Multiple typographies" + await expect(textSection.getByText("Multiple typographies")).toBeVisible(); +}); \ No newline at end of file diff --git a/frontend/playwright/ui/specs/tokens/apply.spec.js b/frontend/playwright/ui/specs/tokens/apply.spec.js index b52de56e16..23a25f3669 100644 --- a/frontend/playwright/ui/specs/tokens/apply.spec.js +++ b/frontend/playwright/ui/specs/tokens/apply.spec.js @@ -738,7 +738,7 @@ test.describe("Tokens: Apply token", () => { // Check if token pill is visible on right sidebar const strokeSectionSidebar = rightSidebar.getByRole("region", { - name: "stroke-section", + name: "Stroke section", }); await expect(strokeSectionSidebar).toBeVisible(); const firstStrokeRow = strokeSectionSidebar.getByLabel("stroke-row-0"); diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 32dd2ae5e9..2ac8521761 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1016,7 +1016,7 @@ objects (dsh/lookup-page-objects state page-id) selected (dsh/lookup-selected state) selected-obj (-> (map #(get objects %) selected)) - multi (attrs/get-attrs-multi selected-obj [:proportion-lock]) + multi (attrs/get-attrs-multi selected-obj [:proportion-lock] :lock) multi? (= :multiple (:proportion-lock multi))] (if multi? (rx/of (dwsh/update-shapes selected #(assoc % :proportion-lock true))) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index eae231fe72..18c2312d1c 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -251,7 +251,7 @@ (-> (merge default-text-attrs node) (assoc :fills fills))) node))))] - (attrs/get-attrs-multi nodes attrs))) + (attrs/get-attrs-multi nodes attrs :text))) (defn current-root-values [{:keys [attrs shape]}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs index ba6ea893f2..67d6d1370d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs @@ -195,7 +195,8 @@ (dom/set-attribute! checkbox "indeterminate" true) (dom/remove-attribute! checkbox "indeterminate")))) - [:div {:class (stl/css :fill-section)} + [:section {:class (stl/css :fill-section) + :aria-label "Fill section"} [:div {:class (stl/css :fill-title)} [:> title-bar* {:collapsable has-fills? :collapsed (not open?) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs index b2dbd7cf30..b051c91226 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs @@ -89,7 +89,7 @@ open? (:open @state*) cells (hooks/use-equal-memo cells) - cell (or cell (attrs/get-attrs-multi cells cell-props)) + cell (or cell (attrs/get-attrs-multi cells cell-props :grid)) multiple? (= :multiple (:id cell)) cell-ids (if multiple? (->> cells (map :id)) [(:id cell)]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs index c35bac471e..bebcc3142f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs @@ -177,7 +177,7 @@ :shape-ids ids}))))] [:section {:class (stl/css :stroke-section) - :aria-label "stroke-section"} + :aria-label "Stroke section"} [:div {:class (stl/css :stroke-title)} [:> title-bar* {:collapsable has-strokes? :collapsed (not open?) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs index daf8f09e7e..56084dafa5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs @@ -314,7 +314,8 @@ expand-stream #(swap! state* assoc-in [:more-options] true)) - [:div {:class (stl/css :element-set)} + [:section {:class (stl/css :element-set) + :aria-label "Text section"} [:div {:class (stl/css :element-title)} [:> title-bar* {:collapsable true :collapsed (not main-menu-open?) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 85235ca0eb..9bf4ad78ae 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -208,9 +208,9 @@ merge-attrs (fn [v1 v2] (cond - (= attr-group :shadow) (attrs/get-attrs-multi [v1 v2] attrs shadow-eq shadow-sel) - (= attr-group :blur) (attrs/get-attrs-multi [v1 v2] attrs blur-eq blur-sel) - :else (attrs/get-attrs-multi [v1 v2] attrs))) + (= attr-group :shadow) (attrs/get-attrs-multi [v1 v2] attrs :shadow shadow-eq shadow-sel ) + (= attr-group :blur) (attrs/get-attrs-multi [v1 v2] attrs :blur blur-eq blur-sel) + :else (attrs/get-attrs-multi [v1 v2] attrs :multi))) merge-attr (fn [acc applied-tokens t-attr]