diff --git a/frontend/playwright/ui/specs/tokens/remapping.spec.js b/frontend/playwright/ui/specs/tokens/remapping.spec.js index 9a1f642f48..96aabbdb26 100644 --- a/frontend/playwright/ui/specs/tokens/remapping.spec.js +++ b/frontend/playwright/ui/specs/tokens/remapping.spec.js @@ -12,88 +12,118 @@ test.beforeEach(async ({ page }) => { await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-tokens.json"); }); -test.describe("Tokens: Remapping Feature", () => { +const createToken = async (page, type, name, textFieldName, value) => { + const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + + const { tokensUpdateCreateModal } = await setupTokensFile(page, { + flags: ["enable-token-shadow"], + }); + + // Create base token + await tokensTabPanel + .getByRole("button", { name: `Add Token: ${type}` }) + .click(); + await expect(tokensUpdateCreateModal).toBeVisible(); + + const nameField = tokensUpdateCreateModal.getByLabel("Name"); + await nameField.fill(name); + + const colorField = tokensUpdateCreateModal.getByRole("textbox", { + name: textFieldName, + }); + await colorField.fill(value); + + const submitButton = tokensUpdateCreateModal.getByRole("button", { + name: "Save", + }); + await submitButton.click(); + await expect(tokensUpdateCreateModal).not.toBeVisible(); +}; + +const renameToken = async (page, oldName, newName) => { + const { tokensUpdateCreateModal, tokensSidebar, tokenContextMenuForToken } = + await setupTokensFile(page, { flags: ["enable-token-shadow"] }); + + const baseToken = tokensSidebar.getByRole("button", { + name: oldName, + }); + await baseToken.click({ button: "right" }); + await tokenContextMenuForToken.getByText("Edit token").click(); + + await expect(tokensUpdateCreateModal).toBeVisible(); + + const nameField = tokensUpdateCreateModal.getByLabel("Name"); + await nameField.fill(newName); + + const submitButton = tokensUpdateCreateModal.getByRole("button", { + name: "Save", + }); + await submitButton.click(); +}; + +const createCompositeDerivedToken = async (page, type, name, reference) => { + const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + + const { tokensUpdateCreateModal } = await setupTokensFile(page, { + flags: ["enable-token-shadow"], + }); + + await tokensTabPanel + .getByRole("button", { name: `Add Token: ${type}` }) + .click(); + await expect(tokensUpdateCreateModal).toBeVisible(); + + const nameField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Name", + }); + await nameField.fill(name); + + const referenceToggle = tokensUpdateCreateModal.getByTestId("reference-opt"); + await referenceToggle.click(); + + const referenceField = tokensUpdateCreateModal.getByRole("textbox", { + name: "Reference", + }); + await referenceField.fill(reference); + + const submitButton = tokensUpdateCreateModal.getByRole("button", { + name: "Save", + }); + await submitButton.click(); + await expect(tokensUpdateCreateModal).not.toBeVisible(); +}; + +test.describe("Remapping Tokens", () => { test.describe("Box Shadow Token Remapping", () => { test("User renames box shadow token with alias references", async ({ page, }) => { - const { - tokensUpdateCreateModal, - tokensSidebar, - tokenContextMenuForToken, - } = await setupTokensFile(page, { flags: ["enable-token-shadow"] }); - - const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + const { tokensSidebar } = await setupTokensFile(page, { + flags: ["enable-token-shadow"], + }); // Create base shadow token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Shadow" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - let nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("base-shadow"); - - const colorField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Color", - }); - await colorField.fill("#000000"); - - let submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createToken(page, "Shadow", "base-shadow", "Color", "#000000"); // Create derived shadow token that references base-shadow - await tokensTabPanel - .getByRole("button", { name: "Add Token: Shadow" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - nameField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Name", - }); - await nameField.fill("derived-shadow"); - - const referenceToggle = - tokensUpdateCreateModal.getByTestId("reference-opt"); - await referenceToggle.click(); - - const referenceField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Reference", - }); - await referenceField.fill("{base-shadow}"); - - submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createCompositeDerivedToken( + page, + "Shadow", + "derived-shadow", + "{base-shadow}", + ); // Rename base-shadow token - const baseToken = tokensSidebar.getByRole("button", { - name: "base-shadow", - }); - await baseToken.click({ button: "right" }); - await tokenContextMenuForToken.getByText("Edit token").click(); - - await expect(tokensUpdateCreateModal).toBeVisible(); - nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("foundation-shadow"); - - submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); + await renameToken(page, "base-shadow", "foundation-shadow"); // Check for remapping modal const remappingModal = page.getByTestId("token-remapping-modal"); await expect(remappingModal).toBeVisible({ timeout: 5000 }); - await expect(remappingModal).toContainText("1"); + await expect(remappingModal).toContainText("base-shadow"); + await expect(remappingModal).toContainText("foundation-shadow"); const confirmButton = remappingModal.getByRole("button", { - name: /remap/i, + name: "remap tokens", }); await confirmButton.click(); @@ -116,51 +146,16 @@ test.describe("Tokens: Remapping Feature", () => { workspacePage, } = await setupTokensFile(page, { flags: ["enable-token-shadow"] }); - const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); - // Create base shadow token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Shadow" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - let nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("primary-shadow"); - - let colorField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Color", - }); - await colorField.fill("#000000"); - - let submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createToken(page, "Shadow", "primary-shadow", "Color", "#000000"); // Create derived shadow token that references base - await tokensTabPanel - .getByRole("button", { name: "Add Token: Shadow" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("card-shadow"); - - const referenceToggle = - tokensUpdateCreateModal.getByTestId("reference-opt"); - await referenceToggle.click(); - - const referenceField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Reference", - }); - await referenceField.fill("{primary-shadow}"); - - submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createCompositeDerivedToken( + page, + "Shadow", + "card-shadow", + "{primary-shadow}", + ); // Apply the referenced token to a shape await page.getByRole("tab", { name: "Layers" }).click(); @@ -183,16 +178,16 @@ test.describe("Tokens: Remapping Feature", () => { await tokenContextMenuForToken.getByText("Edit token").click(); await expect(tokensUpdateCreateModal).toBeVisible(); - nameField = tokensUpdateCreateModal.getByLabel("Name"); + const nameField = tokensUpdateCreateModal.getByLabel("Name"); await nameField.fill("main-shadow"); // Update the color value - colorField = tokensUpdateCreateModal.getByRole("textbox", { + const colorField = tokensUpdateCreateModal.getByRole("textbox", { name: "Color", }); await colorField.fill("#FF0000"); - submitButton = tokensUpdateCreateModal.getByRole("button", { + const submitButton = tokensUpdateCreateModal.getByRole("button", { name: "Save", }); await submitButton.click(); @@ -202,7 +197,7 @@ test.describe("Tokens: Remapping Feature", () => { await expect(remappingModal).toBeVisible({ timeout: 5000 }); const confirmButton = remappingModal.getByRole("button", { - name: /remap/i, + name: "remap tokens", }); await confirmButton.click(); @@ -259,73 +254,25 @@ test.describe("Tokens: Remapping Feature", () => { const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); // Create base typography token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Typography" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - let nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("base-text"); - - const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Font size", - }); - await fontSizeField.fill("16"); - - let submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createToken(page, "Typography", "base-text", "Font size", "16"); // Create derived typography token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Typography" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - nameField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Name", - }); - await nameField.fill("body-text"); - - const referenceToggle = - tokensUpdateCreateModal.getByTestId("reference-opt"); - await referenceToggle.click(); - - const referenceField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Reference", - }); - await referenceField.fill("{base-text}"); - - submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createCompositeDerivedToken( + page, + "Typography", + "body-text", + "{base-text}", + ); // Rename base token - const baseToken = tokensSidebar.getByRole("button", { - name: "base-text", - }); - await baseToken.click({ button: "right" }); - await tokenContextMenuForToken.getByText("Edit token").click(); - - await expect(tokensUpdateCreateModal).toBeVisible(); - nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("default-text"); - - submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); + await renameToken(page, "base-text", "default-text"); // Check for remapping modal const remappingModal = page.getByTestId("token-remapping-modal"); await expect(remappingModal).toBeVisible({ timeout: 5000 }); const confirmButton = remappingModal.getByRole("button", { - name: /remap/i, + name: "remap tokens", }); await confirmButton.click(); @@ -351,24 +298,7 @@ test.describe("Tokens: Remapping Feature", () => { const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); // Create base typography token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Typography" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - let nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("body-style"); - - let fontSizeField = tokensUpdateCreateModal.getByRole("textbox", { - name: "Font size", - }); - await fontSizeField.fill("16"); - - let submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createToken(page, "Typography", "body-style", "Font size", "16"); // Create derived typography token await tokensTabPanel @@ -376,7 +306,7 @@ test.describe("Tokens: Remapping Feature", () => { .click(); await expect(tokensUpdateCreateModal).toBeVisible(); - nameField = tokensUpdateCreateModal.getByRole("textbox", { + let nameField = tokensUpdateCreateModal.getByRole("textbox", { name: "Name", }); await nameField.fill("paragraph-style"); @@ -390,7 +320,7 @@ test.describe("Tokens: Remapping Feature", () => { }); await referenceField.fill("{body-style}"); - submitButton = tokensUpdateCreateModal.getByRole("button", { + let submitButton = tokensUpdateCreateModal.getByRole("button", { name: "Save", }); await submitButton.click(); @@ -421,7 +351,7 @@ test.describe("Tokens: Remapping Feature", () => { await nameField.fill("text-base"); // Update the font size value - fontSizeField = tokensUpdateCreateModal.getByRole("textbox", { + const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", { name: "Font size", }); await fontSizeField.fill("18"); @@ -436,7 +366,7 @@ test.describe("Tokens: Remapping Feature", () => { await expect(remappingModal).toBeVisible({ timeout: 5000 }); const confirmButton = remappingModal.getByRole("button", { - name: /remap/i, + name: "remap tokens", }); await confirmButton.click(); @@ -471,72 +401,29 @@ test.describe("Tokens: Remapping Feature", () => { test("User renames border radius token with alias references", async ({ page, }) => { - const { - tokensUpdateCreateModal, - tokensSidebar, - tokenContextMenuForToken, - } = await setupTokensFile(page); - - const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); + const { tokensSidebar } = await setupTokensFile(page); // Create base border radius token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Border Radius" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - let nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("base-radius"); - - const valueField = tokensUpdateCreateModal.getByLabel("Value"); - await valueField.fill("4"); - - let submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createToken(page, "Border Radius", "base-radius", "Value", "4"); // Create derived border radius token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Border Radius" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("card-radius"); - - const valueField2 = tokensUpdateCreateModal.getByLabel("Value"); - await valueField2.fill("{base-radius}"); - - submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createToken( + page, + "Border Radius", + "card-radius", + "Value", + "{base-radius}", + ); // Rename base token - const baseToken = tokensSidebar.getByRole("button", { - name: "base-radius", - }); - await baseToken.click({ button: "right" }); - await tokenContextMenuForToken.getByText("Edit token").click(); - - await expect(tokensUpdateCreateModal).toBeVisible(); - nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("primary-radius"); - - submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); + await renameToken(page, "base-radius", "primary-radius"); // Check for remapping modal const remappingModal = page.getByTestId("token-remapping-modal"); await expect(remappingModal).toBeVisible({ timeout: 5000 }); const confirmButton = remappingModal.getByRole("button", { - name: /remap/i, + name: "remap tokens", }); await confirmButton.click(); @@ -558,43 +445,17 @@ test.describe("Tokens: Remapping Feature", () => { tokenContextMenuForToken, } = await setupTokensFile(page); - const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); - // Create base border radius token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Border Radius" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - let nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("radius-sm"); - - let valueField = tokensUpdateCreateModal.getByLabel("Value"); - await valueField.fill("4"); - - let submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createToken(page, "Border Radius", "radius-sm", "Value", "4"); // Create derived border radius token - await tokensTabPanel - .getByRole("button", { name: "Add Token: Border Radius" }) - .click(); - await expect(tokensUpdateCreateModal).toBeVisible(); - - nameField = tokensUpdateCreateModal.getByLabel("Name"); - await nameField.fill("button-radius"); - - const valueField2 = tokensUpdateCreateModal.getByLabel("Value"); - await valueField2.fill("{radius-sm}"); - - submitButton = tokensUpdateCreateModal.getByRole("button", { - name: "Save", - }); - await submitButton.click(); - await expect(tokensUpdateCreateModal).not.toBeVisible(); + await createToken( + page, + "Border Radius", + "button-radius", + "Value", + "{radius-sm}", + ); // Rename and update value of base token const radiusToken = tokensSidebar.getByRole("button", { @@ -604,14 +465,14 @@ test.describe("Tokens: Remapping Feature", () => { await tokenContextMenuForToken.getByText("Edit token").click(); await expect(tokensUpdateCreateModal).toBeVisible(); - nameField = tokensUpdateCreateModal.getByLabel("Name"); + const nameField = tokensUpdateCreateModal.getByLabel("Name"); await nameField.fill("radius-base"); // Update the value - valueField = tokensUpdateCreateModal.getByLabel("Value"); + const valueField = tokensUpdateCreateModal.getByLabel("Value"); await valueField.fill("8"); - submitButton = tokensUpdateCreateModal.getByRole("button", { + const submitButton = tokensUpdateCreateModal.getByRole("button", { name: "Save", }); await submitButton.click(); @@ -621,7 +482,7 @@ test.describe("Tokens: Remapping Feature", () => { await expect(remappingModal).toBeVisible({ timeout: 5000 }); const confirmButton = remappingModal.getByRole("button", { - name: /remap/i, + name: "remap tokens", }); await confirmButton.click(); @@ -648,4 +509,82 @@ test.describe("Tokens: Remapping Feature", () => { await expect(currentValue).toHaveValue("{radius-base}"); }); }); + + test.describe("Cancel remap", () => { + test("Only rename - breaks reference", async ({ page }) => { + const { tokensSidebar } = await setupTokensFile(page, { + flags: ["enable-token-shadow"], + }); + + // Create base shadow token + await createToken(page, "Shadow", "base-shadow", "Color", "#000000"); + + // Create derived shadow token that references base-shadow + await createCompositeDerivedToken( + page, + "Shadow", + "derived-shadow", + "{base-shadow}", + ); + + // Rename base-shadow token + await renameToken(page, "base-shadow", "foundation-shadow"); + + // Check for remapping modal + const remappingModal = page.getByTestId("token-remapping-modal"); + await expect(remappingModal).toBeVisible({ timeout: 5000 }); + + const cancelButton = remappingModal.getByRole("button", { + name: "don't remap", + }); + await cancelButton.click(); + + // Verify token was renamed + await expect( + tokensSidebar.getByRole("button", { + name: "foundation-shadow", + }), + ).toBeVisible(); + await expect( + tokensSidebar.locator('[aria-label="Missing reference"]'), + ).toBeVisible(); + }); + + test("Cancel process - no changes applied", async ({ page }) => { + const { tokensSidebar } = await setupTokensFile(page, { + flags: ["enable-token-shadow"], + }); + + // Create base shadow token + await createToken(page, "Shadow", "base-shadow", "Color", "#000000"); + + // Create derived shadow token that references base-shadow + await createCompositeDerivedToken( + page, + "Shadow", + "derived-shadow", + "{base-shadow}", + ); + + // Rename base-shadow token + await renameToken(page, "base-shadow", "foundation-shadow"); + + // Check for remapping modal + const remappingModal = page.getByTestId("token-remapping-modal"); + await expect(remappingModal).toBeVisible({ timeout: 5000 }); + + const closeButton = remappingModal.getByRole("button", { + name: "close", + }); + await closeButton.click(); + + // Verify original token name still exists + await expect( + tokensSidebar.getByRole("button", { name: "base-shadow" }), + ).toBeVisible(); + await expect( + tokensSidebar.getByRole("button", { name: "derived-shadow" }), + ).toBeVisible(); + }); + }); }); diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs b/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs index b1299f4bdb..eb602911fa 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs @@ -28,7 +28,6 @@ [app.main.ui.forms :as fc] [app.main.ui.workspace.tokens.management.forms.controls :as token.controls] [app.main.ui.workspace.tokens.management.forms.validators :refer [default-validate-token]] - [app.main.ui.workspace.tokens.remapping-modal :as remapping-modal] [app.util.dom :as dom] [app.util.forms :as fm] [app.util.i18n :refer [tr]] @@ -182,9 +181,33 @@ (when (or (k/enter? e) (k/space? e)) (on-cancel e)))) + on-remap-token + (mf/use-fn + (mf/deps token) + (fn [valid-token name old-name description] + (st/emit! + (dwtl/update-token (:id token) + {:name name + :value (:value valid-token) + :description description}) + (remap/remap-tokens old-name name) + (dwtp/propagate-workspace-tokens) + (modal/hide!)))) + + on-rename-token + (mf/use-fn + (mf/deps token) + (fn [valid-token name description] + (st/emit! + (dwtl/update-token (:id token) + {:name name + :value (:value valid-token) + :description description}) + (modal/hide!)))) + on-submit (mf/use-fn - (mf/deps validate-token token tokens token-type value-subfield type active-tab) + (mf/deps validate-token token tokens token-type value-subfield type active-tab on-remap-token on-rename-token is-create) (fn [form _event] (let [name (get-in @form [:clean-data :name]) path (str (d/name token-type) "." name) @@ -202,22 +225,15 @@ file-data (dh/lookup-file-data state) old-name (:name token) is-rename (and (= action "edit") (not= name old-name)) - references-count (remap/count-token-references file-data old-name)] + references-count (remap/count-token-references file-data old-name) + on-remap #(on-remap-token valid-token name old-name description) + on-rename #(on-rename-token valid-token name description)] (if (and is-rename (> references-count 0)) - (remapping-modal/show-remapping-modal - {:old-token-name old-name - :new-token-name name - :references-count references-count - :on-confirm (fn [] - (st/emit! - (dwtl/update-token (:id token) - {:name name - :value (:value valid-token) - :description description}) - (remap/remap-tokens old-name name) - (dwtp/propagate-workspace-tokens) - (modal/hide!))) - :on-cancel #(modal/hide!)}) + (st/emit! (modal/show :tokens/remapping-confirmation {:old-token-name old-name + :new-token-name name + :references-count references-count + :on-remap on-remap + :on-rename on-rename})) (st/emit! (if is-create (dwtl/create-token (ctob/make-token {:name name diff --git a/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs b/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs index bfb1a1f0a3..2555c3fe4c 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/management/token_pill.cljs @@ -295,7 +295,8 @@ errors? [:> icon* {:icon-id i/broken-link - :class (stl/css :token-pill-icon)}] + :class (stl/css :token-pill-icon) + :aria-label (tr "workspace.tokens.missing-reference")}] color [:> swatch* {:background color diff --git a/frontend/src/app/main/ui/workspace/tokens/remapping_modal.cljs b/frontend/src/app/main/ui/workspace/tokens/remapping_modal.cljs index 58766ebd29..198972a193 100644 --- a/frontend/src/app/main/ui/workspace/tokens/remapping_modal.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/remapping_modal.cljs @@ -11,22 +11,15 @@ [app.main.data.modal :as modal] [app.main.store :as st] [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.typography :as t] [app.main.ui.ds.foundations.typography.heading :refer [heading*]] - [app.main.ui.ds.notifications.context-notification :refer [context-notification*]] - [app.util.dom :as dom] + [app.main.ui.ds.foundations.typography.text :refer [text*]] [app.util.i18n :refer [tr]] + [app.util.keyboard :as kbd] [rumext.v2 :as mf])) -(defn show-remapping-modal - "Show the token remapping confirmation modal" - [{:keys [old-token-name new-token-name references-count on-confirm on-cancel]}] - (let [props {:old-token-name old-token-name - :new-token-name new-token-name - :references-count references-count - :on-confirm on-confirm - :on-cancel on-cancel}] - (st/emit! (modal/show :tokens/remapping-confirmation props)))) - (defn hide-remapping-modal "Hide the token remapping confirmation modal" [] @@ -34,73 +27,73 @@ ;; Remapping Modal Component (mf/defc token-remapping-modal - {::mf/wrap-props false - ::mf/register modal/components + {::mf/register modal/components ::mf/register-as :tokens/remapping-confirmation} - [{:keys [old-token-name new-token-name references-count on-confirm on-cancel]}] - (let [remapping-in-progress* (mf/use-state false) - remapping-in-progress? (deref remapping-in-progress*) + [{:keys [old-token-name new-token-name on-remap on-rename]}] + (let [remap-modal (get @st/state :remap-modal) ;; Remap logic on confirm - on-confirm-remap + confirm-remap (mf/use-fn - (mf/deps on-confirm remapping-in-progress*) - (fn [e] - (dom/prevent-default e) - (dom/stop-propagation e) - (reset! remapping-in-progress* true) + (mf/deps on-remap remap-modal) + (fn [] ;; Call shared remapping logic - (let [state @st/state - remap-modal (:remap-modal state) - old-token-name (:old-token-name remap-modal) + (let [old-token-name (:old-token-name remap-modal) new-token-name (:new-token-name remap-modal)] (st/emit! [:tokens/remap-tokens old-token-name new-token-name])) - (when (fn? on-confirm) - (on-confirm)))) + (when (fn? on-remap) + (on-remap)))) - on-cancel-remap + rename-token (mf/use-fn - (mf/deps on-cancel) - (fn [e] - (dom/prevent-default e) - (dom/stop-propagation e) - (modal/hide!) - (when (fn? on-cancel) - (on-cancel))))] + (mf/deps on-rename) + (fn [] + (when (fn? on-rename) + (on-rename)))) + + cancel-action + (mf/use-fn + (fn [] + (hide-remapping-modal))) + + ;; Close modal on Escape key if not in progress + on-key-down + (mf/use-fn + (mf/deps cancel-action) + (fn [event] + (when (kbd/enter? event) + (cancel-action))))] + + [:div {:class (stl/css :modal-overlay) + :on-key-down on-key-down + :role "alertdialog" + :aria-modal "true" + :aria-labelledby "modal-title"} - [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog) :data-testid "token-remapping-modal"} + [:> icon-button* {:on-click cancel-action + :class (stl/css :close-btn) + :icon i/close + :variant "action" + :aria-label (tr "labels.close")}] + [:div {:class (stl/css :modal-header)} [:> heading* {:level 2 - :typography "headline-medium" + :id "modal-title" + :typography "headline-large" :class (stl/css :modal-title)} - (tr "workspace.tokens.remap-token-references")]] + (tr "workspace.tokens.remap-token-references-title" old-token-name new-token-name)]] [:div {:class (stl/css :modal-content)} - [:> heading* {:level 3 - :typography "title-medium" - :class (stl/css :modal-msg)} - (tr "workspace.tokens.renaming-token-from-to" old-token-name new-token-name)] - [:div {:class (stl/css :modal-scd-msg)} - (if (> references-count 0) - (tr "workspace.tokens.references-found" references-count) - (tr "workspace.tokens.no-references-found"))] - (when remapping-in-progress? - [:> context-notification* - {:level :info - :appearance :ghost} - (tr "workspace.tokens.remapping-in-progress")])] + [:> text* {:as "p" :typography t/body-medium} (tr "workspace.tokens.remap-warning-effects")] + [:> text* {:as "p" :typography t/body-medium} (tr "workspace.tokens.remap-warning-time")]] [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} - [:> button* {:on-click on-cancel-remap + [:> button* {:on-click rename-token :type "button" - :variant "secondary" - :disabled remapping-in-progress?} - (tr "labels.cancel")] - [:> button* {:on-click on-confirm-remap + :variant "secondary"} + (tr "workspace.tokens.not-remap")] + [:> button* {:on-click confirm-remap :type "button" - :variant "primary" - :disabled remapping-in-progress?} - (if (> references-count 0) - (tr "workspace.tokens.remap-and-rename") - (tr "workspace.tokens.rename-only"))]]]]])) + :variant "primary"} + (tr "workspace.tokens.remap")]]]]])) diff --git a/frontend/src/app/main/ui/workspace/tokens/remapping_modal.scss b/frontend/src/app/main/ui/workspace/tokens/remapping_modal.scss index 5f97bf970f..3fda1b8f45 100644 --- a/frontend/src/app/main/ui/workspace/tokens/remapping_modal.scss +++ b/frontend/src/app/main/ui/workspace/tokens/remapping_modal.scss @@ -10,6 +10,9 @@ @use "refactor/common-refactor.scss" as deprecated; .modal-overlay { + --modal-title-foreground-color: var(--color-foreground-primary); + --modal-text-foreground-color: var(--color-foreground-secondary); + @extend .modal-overlay-base; display: flex; justify-content: center; @@ -17,16 +20,22 @@ position: fixed; inset-inline-start: 0; inset-block-start: 0; - height: 100%; - width: 100%; + block-size: 100%; + inline-size: 100%; background-color: var(--overlay-color); } +.close-btn { + position: absolute; + inset-block-start: $sz-6; + inset-inline-end: $sz-6; +} + .modal-dialog { @extend .modal-container-base; - width: 100%; - max-width: 32rem; - max-height: unset; + block-size: 100%; + max-inline-size: 32rem; + max-block-size: unset; user-select: none; position: relative; } @@ -45,11 +54,7 @@ .modal-content { @include t.use-typography("body-large"); - margin-block-end: var(--sp-xxl); - padding: var(--sp-xxl) 0; - display: flex; - flex-direction: column; - gap: var(--sp-l); + color: var(--modal-text-foreground-color); } .modal-footer { diff --git a/frontend/translations/en.po b/frontend/translations/en.po index db993ec3f8..357f718f09 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -8072,6 +8072,10 @@ msgid "workspace.tokens.missing-references" msgstr "Missing token references: " #: src/app/main/ui/workspace/tokens/management/token_pill.cljs:123 +msgid "workspace.tokens.missing-reference" +msgstr "Missing reference" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:299 msgid "workspace.tokens.more-options" msgstr "Right click to see options" @@ -8162,36 +8166,29 @@ msgstr "Enter a token shadow alias" msgid "workspace.tokens.reference-error" msgstr "Reference Errors: " -#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:86 -msgid "workspace.tokens.references-found" -msgstr "%s references found in your design" - -#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:105 -msgid "workspace.tokens.remap-and-rename" -msgstr "Remap & Rename" - -#: src/app/main/ui/workspace/tokens/remapping_modal.cljs -#, unused -msgid "workspace.tokens.remap-explanation" -msgstr "" -"All references to this token will be automatically updated to use the new " -"name." +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:78 +msgid "workspace.tokens.remap-token-references-title" +msgstr "Remap all tokens that use `%s` to `%s`?" #: src/app/main/ui/workspace/tokens/remapping_modal.cljs:78 -msgid "workspace.tokens.remap-token-references" -msgstr "Remap Token References" +msgid "workspace.tokens.remap-warning-effects" +msgstr "This will change all layers and references that use the old token name." + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:78 +msgid "workspace.tokens.remap-warning-time" +msgstr "This action could take a while." #: src/app/main/ui/workspace/tokens/remapping_modal.cljs:92 msgid "workspace.tokens.remapping-in-progress" msgstr "Remapping token references..." #: src/app/main/ui/workspace/tokens/remapping_modal.cljs:106 -msgid "workspace.tokens.rename-only" -msgstr "Rename" +msgid "workspace.tokens.not-remap" +msgstr "Don't remap" -#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:83 -msgid "workspace.tokens.renaming-token-from-to" -msgstr "Renaming token from '%s' to '%s'" +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:105 +msgid "workspace.tokens.remap" +msgstr "Remap tokens" #: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:56, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:84, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:103, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:271, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:445, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:169, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:304, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:225, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:332, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:432, src/app/main/ui/workspace/tokens/management/token_pill.cljs:121 #, fuzzy diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 3a0f137e87..8e2ce1a5a3 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -7994,6 +7994,10 @@ msgstr "Line height (multiplicador, px o %) o {alias}" msgid "workspace.tokens.missing-references" msgstr "Referencias de tokens no encontradas: " +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:123 +msgid "workspace.tokens.missing-reference" +msgstr "Referencia no encontrada" + #: src/app/main/ui/workspace/tokens/management/token_pill.cljs:123 msgid "workspace.tokens.more-options" msgstr "Click derecho para ver opciones" @@ -8063,36 +8067,29 @@ msgstr "La referencia no es válida o no se encuentra en ningún set activo." msgid "workspace.tokens.reference-error" msgstr "Errores en referencias: " -#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:86 -msgid "workspace.tokens.references-found" -msgstr "%s referencias encontradas en tu diseño" - -#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:105 -msgid "workspace.tokens.remap-and-rename" -msgstr "Actualizar referencias y renombrar" - -#: src/app/main/ui/workspace/tokens/remapping_modal.cljs -#, unused -msgid "workspace.tokens.remap-explanation" -msgstr "" -"Todas las referencias a este token se actualizarán automáticamente para " -"usar el nuevo nombre." +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:78 +msgid "workspace.tokens.remap-token-references-title" +msgstr "¿Actualizar todas las referencias de `%s` a `%s`?" #: src/app/main/ui/workspace/tokens/remapping_modal.cljs:78 -msgid "workspace.tokens.remap-token-references" -msgstr "Actualizar referencias de token" +msgid "workspace.tokens.remap-warning-effects" +msgstr "Esta acción actualizará todas las capas y referencias que usen el token antiguo" + +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:78 +msgid "workspace.tokens.remap-warning-time" +msgstr "Este proceso puede durar un poco" #: src/app/main/ui/workspace/tokens/remapping_modal.cljs:92 msgid "workspace.tokens.remapping-in-progress" msgstr "Actualizando referencias de token..." #: src/app/main/ui/workspace/tokens/remapping_modal.cljs:106 -msgid "workspace.tokens.rename-only" -msgstr "Renombrar" +msgid "workspace.tokens.not-remap" +msgstr "No actualizar" -#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:83 -msgid "workspace.tokens.renaming-token-from-to" -msgstr "Renombrando el token de '%s' a '%s'" +#: src/app/main/ui/workspace/tokens/remapping_modal.cljs:105 +msgid "workspace.tokens.remap" +msgstr "Actualizar tokens" #: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:56, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:84, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:103, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:271, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:445, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:169, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:304, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:225, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:332, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:432, src/app/main/ui/workspace/tokens/management/token_pill.cljs:121 #, fuzzy