diff --git a/CHANGES.md b/CHANGES.md index 0e46a9c0da..702384e36e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ - Add copy as image to clipboard option to workspace context menu (by @dfelinto) [Github #8313](https://github.com/penpot/penpot/pull/8313) - Add Tab/Shift+Tab navigation to rename layers sequentially (by @bittoby) [Github #8474](https://github.com/penpot/penpot/pull/8474) - Rename token group [Taiga #13137](https://tree.taiga.io/project/penpot/us/13137) +- Copy token name from contextual menu [Taiga #13568](https://tree.taiga.io/project/penpot/issue/13568) ### :bug: Bugs fixed diff --git a/frontend/playwright/ui/specs/tokens/apply.spec.js b/frontend/playwright/ui/specs/tokens/apply.spec.js index b52de56e16..a8401e2d51 100644 --- a/frontend/playwright/ui/specs/tokens/apply.spec.js +++ b/frontend/playwright/ui/specs/tokens/apply.spec.js @@ -84,8 +84,9 @@ test.describe("Tokens: Apply token", () => { await brTokenPillSM.click(); // Change token from dropdown - const brTokenOptionXl = borderRadiusSection - .getByRole("option", { name: "borderRadius.xl" }); + const brTokenOptionXl = borderRadiusSection.getByRole("option", { + name: "borderRadius.xl", + }); await expect(brTokenOptionXl).toBeVisible(); await brTokenOptionXl.click(); @@ -151,7 +152,9 @@ test.describe("Tokens: Apply token", () => { await detachButton.click(); // Open dropdown from input - const dropdownBtn = layerMenuSection.getByRole('button', { name: 'Open token list' }) + const dropdownBtn = layerMenuSection.getByRole("button", { + name: "Open token list", + }); await expect(dropdownBtn).toBeVisible(); await dropdownBtn.click(); @@ -227,8 +230,12 @@ test.describe("Tokens: Apply token", () => { await expect(firstShadowFields).toBeVisible(); // Fill in the shadow values - const offsetXInput = firstShadowFields.getByRole('textbox', { name: 'X' }); - const offsetYInput = firstShadowFields.getByRole('textbox', { name: 'Y' }); + const offsetXInput = firstShadowFields.getByRole("textbox", { + name: "X", + }); + const offsetYInput = firstShadowFields.getByRole("textbox", { + name: "Y", + }); const blurInput = firstShadowFields.getByRole("textbox", { name: "Blur", }); @@ -301,8 +308,12 @@ test.describe("Tokens: Apply token", () => { await expect(thirdShadowFields).toBeVisible(); // User adds values for the third shadow - const thirdOffsetXInput = thirdShadowFields.getByRole('textbox', { name: 'X' }); - const thirdOffsetYInput = thirdShadowFields.getByRole('textbox', { name: 'Y' }); + const thirdOffsetXInput = thirdShadowFields.getByRole("textbox", { + name: "X", + }); + const thirdOffsetYInput = thirdShadowFields.getByRole("textbox", { + name: "Y", + }); const thirdBlurInput = thirdShadowFields.getByRole("textbox", { name: "Blur", }); @@ -330,10 +341,10 @@ test.describe("Tokens: Apply token", () => { // Verify that the first shadow kept its values const firstOffsetXValue = await firstShadowFields - .getByRole('textbox', { name: 'X' }) + .getByRole("textbox", { name: "X" }) .inputValue(); const firstOffsetYValue = await firstShadowFields - .getByRole('textbox', { name: 'Y' }) + .getByRole("textbox", { name: "Y" }) .inputValue(); const firstBlurValue = await firstShadowFields .getByRole("textbox", { name: "Blur" }) @@ -359,10 +370,10 @@ test.describe("Tokens: Apply token", () => { await expect(newSecondShadowFields).toBeVisible(); const secondOffsetXValue = await newSecondShadowFields - .getByRole('textbox', { name: 'X' }) + .getByRole("textbox", { name: "X" }) .inputValue(); const secondOffsetYValue = await newSecondShadowFields - .getByRole('textbox', { name: 'Y' }) + .getByRole("textbox", { name: "Y" }) .inputValue(); const secondBlurValue = await newSecondShadowFields .getByRole("textbox", { name: "Blur" }) @@ -412,10 +423,10 @@ test.describe("Tokens: Apply token", () => { // Verify first shadow values are still there const restoredFirstOffsetX = await firstShadowFields - .getByRole('textbox', { name: 'X' }) + .getByRole("textbox", { name: "X" }) .inputValue(); const restoredFirstOffsetY = await firstShadowFields - .getByRole('textbox', { name: 'Y' }) + .getByRole("textbox", { name: "Y" }) .inputValue(); const restoredFirstBlur = await firstShadowFields .getByRole("textbox", { name: "Blur" }) @@ -435,10 +446,10 @@ test.describe("Tokens: Apply token", () => { // Verify second shadow values are still there const restoredSecondOffsetX = await newSecondShadowFields - .getByRole('textbox', { name: 'X' }) + .getByRole("textbox", { name: "X" }) .inputValue(); const restoredSecondOffsetY = await newSecondShadowFields - .getByRole('textbox', { name: 'Y' }) + .getByRole("textbox", { name: "Y" }) .inputValue(); const restoredSecondBlur = await newSecondShadowFields .getByRole("textbox", { name: "Blur" }) @@ -616,7 +627,7 @@ test.describe("Tokens: Apply token", () => { await tokensSidebar .getByRole("button", { name: "dimension.sm" }) .click({ button: "right" }); - await tokenContextMenuForToken.getByText("Y").click(); + await tokenContextMenuForToken.getByText("Y", { exact: true }).click(); // Check if measures sections is visible on right sidebar const measuresSection = page.getByRole("region", { diff --git a/frontend/src/app/main/ui/workspace/tokens/management/context_menu.cljs b/frontend/src/app/main/ui/workspace/tokens/management/context_menu.cljs index bcb44b83c5..621f8f2f4e 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/context_menu.cljs @@ -20,6 +20,7 @@ [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] + [app.util.clipboard :as clipboard] [app.util.dom :as dom] [app.util.i18n :refer [tr]] [app.util.timers :as timers] @@ -333,6 +334,7 @@ (defn default-actions [{:keys [token selected-token-set-id on-delete-token]}] (let [{:keys [modal]} (dwta/get-token-properties token) + on-copy-name #(clipboard/to-clipboard (:name token)) on-duplicate-token #(st/emit! (dwtl/duplicate-token (:id token)))] [{:title (tr "workspace.tokens.edit") :no-selectable true @@ -350,6 +352,9 @@ {:title (tr "workspace.tokens.duplicate") :no-selectable true :action on-duplicate-token} + {:title (tr "workspace.tokens.copy-name") + :no-selectable true + :action on-copy-name} {:title (tr "workspace.tokens.delete") :no-selectable true :action #(on-delete-token token)}])) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 6b6516709e..4a7eb227f9 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -7955,6 +7955,10 @@ msgstr "Delete theme" msgid "workspace.tokens.duplicate" msgstr "Duplicate token" +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:350 +msgid "workspace.tokens.copy-name" +msgstr "Copy token path" + #: src/app/main/data/workspace/tokens/library_edit.cljs:240, src/app/main/data/workspace/tokens/library_edit.cljs:509 msgid "workspace.tokens.duplicate-suffix" msgstr "copy" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 69e1aeb0bf..50678fd0c8 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -7866,6 +7866,10 @@ msgstr "Borrar theme" msgid "workspace.tokens.duplicate" msgstr "Duplicar token" +#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:350 +msgid "workspace.tokens.copy-name" +msgstr "Copiar ruta de token" + #: src/app/main/data/workspace/tokens/library_edit.cljs:240, src/app/main/data/workspace/tokens/library_edit.cljs:509 msgid "workspace.tokens.duplicate-suffix" msgstr "copiar"