From 411f661bdbcf7d5a1e6ba8b63efca3ccc8d1c4f5 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Tue, 27 Oct 2026 10:20:11 +0100 Subject: [PATCH] :recycle: Use DS buttons in the delete shared libraries modal (#12554) * :recycle: Use DS buttons in the delete shared libraries modal The delete shared libraries modal (unpublish a library, delete a shared file or project) gets a ghost DS icon button named "Close", placed on the tooltip wrapper, and DS buttons for cancel (secondary) and accept (destructive for the danger style, primary otherwise) instead of raw inputs. The buttons are narrower and the accept button takes the DS destructive red. A new empty library references fixture and a visual test cover the unpublish library modal from the workspace main menu. AI-assisted-by: claude-opus-5-5 * :recycle: Move delete shared libraries modal styles closer to the DS The delete shared libraries stylesheet resolves its legacy modal color aliases to DS tokens, uses a logical property and the DS typography module without a namespace. refactor/ stays only for the modal placeholders (%modal-*), which move with the modals refactor. Two unused selectors are commented out with a TODO. No visual change. AI-assisted-by: claude-opus-5-5 * :fire: Remove unused styles from the delete shared libraries modal The SCSS refactor of the delete shared libraries modal left rules commented out with a TODO because the module does not use them, or because they target an svg inside a DS icon that is the svg itself. CSS modules scope the classes to this module and no other source uses them, so they go: &.transparent, .list-wrapper. No visual change. AI-assisted-by: claude-opus-5-5 --- .../get-library-file-references-empty.json | 1 + .../ui/visual-specs/workspace.spec.js | 29 ++++++++++++ frontend/src/app/main/ui/delete_shared.cljs | 29 ++++++------ frontend/src/app/main/ui/delete_shared.scss | 45 ++++++------------- 4 files changed, 60 insertions(+), 44 deletions(-) create mode 100644 frontend/playwright/data/workspace/get-library-file-references-empty.json diff --git a/frontend/playwright/data/workspace/get-library-file-references-empty.json b/frontend/playwright/data/workspace/get-library-file-references-empty.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/frontend/playwright/data/workspace/get-library-file-references-empty.json @@ -0,0 +1 @@ +[] diff --git a/frontend/playwright/ui/visual-specs/workspace.spec.js b/frontend/playwright/ui/visual-specs/workspace.spec.js index d5621b77ee..91dc893850 100644 --- a/frontend/playwright/ui/visual-specs/workspace.spec.js +++ b/frontend/playwright/ui/visual-specs/workspace.spec.js @@ -1077,3 +1077,32 @@ test.describe("Export modal", () => { }); }); }); + +test.describe("Unpublish library modal", () => { + test("Shows the close, cancel and unpublish buttons", async ({ page }) => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-library.json"); + await workspace.mockRPC( + "get-library-file-references?file-id=*", + "workspace/get-library-file-references-empty.json", + ); + await workspace.goToWorkspace({ + fileId: "c1249a66-fce0-8175-8004-7433fe4be8bc", + pageId: "c1249a66-fce0-8175-8004-7433fe4be8bd", + }); + + await page.getByRole("button", { name: "Main menu" }).click(); + await page.locator("#file-menu-file").click(); + await page.locator("#file-menu-remove-shared").click(); + + const modal = page.locator("[class*='delete_shared__modal-container']"); + await expect(modal).toBeVisible(); + await page.mouse.move(10, 700); + await expect(modal).toHaveScreenshot(shot); + + await modal.locator("[class*='modal-header'] button").hover(); + await expect(modal).toHaveScreenshot(shot); + }); +}); diff --git a/frontend/src/app/main/ui/delete_shared.cljs b/frontend/src/app/main/ui/delete_shared.cljs index 6e88fd41ab..4acd9b066d 100644 --- a/frontend/src/app/main/ui/delete_shared.cljs +++ b/frontend/src/app/main/ui/delete_shared.cljs @@ -11,8 +11,10 @@ [app.main.data.modal :as modal] [app.main.repo :as rp] [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.notifications.context-notification :refer [context-notification*]] - [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as k] @@ -97,8 +99,11 @@ [:div {:class (stl/css :modal-container)} [:div {:class (stl/css :modal-header)} [:h2 {:class (stl/css :modal-title)} title] - [:button {:class (stl/css :modal-close-btn) - :on-click cancel-fn} deprecated-icon/close]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :modal-close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click cancel-fn}]] [:div {:class (stl/css :modal-content)} (when (and (string? subtitle) (not= subtitle "")) @@ -124,14 +129,12 @@ [:div {:class (stl/css :modal-footer)} [:div {:class (stl/css :action-buttons)} (when-not (= cancel-label :omit) - [:input {:class (stl/css :cancel-button) - :type "button" - :value cancel-label - :on-click cancel-fn}]) + [:> button* {:variant "secondary" + :type "button" + :on-click cancel-fn} + cancel-label]) - [:input {:class (stl/css-case :accept-btn true - :danger (= accept-style :danger) - :primary (= accept-style :primary)) - :type "button" - :value accept-label - :on-click accept-fn}]]]]])) + [:> button* {:variant (if (= accept-style :danger) "destructive" "primary") + :type "button" + :on-click accept-fn} + accept-label]]]]])) diff --git a/frontend/src/app/main/ui/delete_shared.scss b/frontend/src/app/main/ui/delete_shared.scss index ab77bb5fd1..5851074638 100644 --- a/frontend/src/app/main/ui/delete_shared.scss +++ b/frontend/src/app/main/ui/delete_shared.scss @@ -4,15 +4,13 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*), which move with +// the modals refactor. @use "refactor/basic-rules.scss" as *; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; .modal-overlay { @extend %modal-overlay-base; - - &.transparent { - background-color: transparent; - } } .modal-container { @@ -23,33 +21,30 @@ grid-template-rows: auto minmax(0, 1fr) auto; } -.list-wrapper { - display: grid; - grid-template-rows: auto 1fr auto; - max-height: 100%; -} - .modal-title { - @include t.use-typography("headline-medium"); + @include use-typography("headline-medium"); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); } +// Tooltip wrapper of the close button: it places it in the corner. .modal-close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-content { - @include t.use-typography("body-small"); + @include use-typography("body-small"); display: grid; gap: var(--sp-s); } .element-list { - @include t.use-typography("body-large"); + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); overflow-y: auto; margin-block: 0; } @@ -58,18 +53,6 @@ @extend %modal-action-btns; } -.cancel-button { - @extend %modal-cancel-btn; -} - -.accept-btn { - @extend %modal-accept-btn; - - &.danger { - @extend %modal-danger-btn; - } -} - .modal-scd-msg { margin-block: 0; } @@ -77,8 +60,8 @@ .modal-scd-msg, .modal-subtitle, .modal-msg { - @include t.use-typography("body-large"); + @include use-typography("body-large"); - color: var(--modal-text-foreground-color); + color: var(--color-foreground-secondary); line-height: 1.5; }