diff --git a/frontend/playwright/ui/visual-specs/workspace.spec.js b/frontend/playwright/ui/visual-specs/workspace.spec.js index 99a79bef99..2d0752bbad 100644 --- a/frontend/playwright/ui/visual-specs/workspace.spec.js +++ b/frontend/playwright/ui/visual-specs/workspace.spec.js @@ -481,6 +481,40 @@ test.describe("Left header", () => { }); }); +test.describe("Right header", () => { + test("Shows the header actions and the zoom menu", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.goToWorkspace(); + + const header = page.locator('[class*="workspace-header-right"]'); + await expect(header).toBeVisible(); + await page.mouse.move(640, 300); + // The buttons are a small part of the header: compare every pixel. + await expect(header).toHaveScreenshot("right-header.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await header.getByRole("button", { name: /Comments/ }).click(); + await page.mouse.move(640, 300); + await expect(header).toHaveScreenshot("right-header-comments.png", { + maxDiffPixelRatio: 0, + threshold: 0, + }); + await page.keyboard.press("Escape"); + + await header.getByTitle("Zoom").click(); + await expect(page.getByText("Reset")).toBeVisible(); + await page.mouse.move(640, 300); + await expect(page).toHaveScreenshot("right-header-zoom.png", { + clip: { x: 900, y: 0, width: 380, height: 260 }, + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + test.describe("Palette", () => { test("Shows the bottom palette expanded and collapsed", async ({ page }) => { const workspace = new WorkspacePage(page); diff --git a/frontend/src/app/main/ui/ds/buttons/icon_button.scss b/frontend/src/app/main/ui/ds/buttons/icon_button.scss index 291c352081..85cda9703e 100644 --- a/frontend/src/app/main/ui/ds/buttons/icon_button.scss +++ b/frontend/src/app/main/ui/ds/buttons/icon_button.scss @@ -41,6 +41,7 @@ --button-hover-bg-color: transparent; --button-hover-fg-color: var(--color-accent-primary); --button-active-bg-color: var(--color-background-quaternary); + --button-active-fg-color: var(--color-accent-primary); --button-disabled-bg-color: transparent; --button-disabled-fg-color: var(--color-accent-primary-muted); --button-focus-bg-color: transparent; @@ -49,6 +50,12 @@ --button-focus-outer-ring-color: var(--color-accent-primary); --button-width: #{$sz-24}; --button-height: #{$sz-24}; + + // A toggled-on action button shows only the accent icon; the background + // stays for the click feedback (:active). + &[aria-pressed="true"] { + --button-active-bg-color: transparent; + } } .flyout-indicator { diff --git a/frontend/src/app/main/ui/workspace/right_header.cljs b/frontend/src/app/main/ui/workspace/right_header.cljs index 28233e3204..0b2f9a9b0e 100644 --- a/frontend/src/app/main/ui/workspace/right_header.cljs +++ b/frontend/src/app/main/ui/workspace/right_header.cljs @@ -20,6 +20,7 @@ [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.context :as ctx] [app.main.ui.dashboard.team] + [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.exports.assets :refer [progress-widget]] @@ -44,11 +45,11 @@ custom-shortcuts (mf/deref refs/custom-shortcuts) get-tt #(sc/get-effective-tooltip % custom-shortcuts) - open-dropdown + toggle-dropdown (mf/use-fn (fn [event] (dom/stop-propagation event) - (reset! open* true))) + (swap! open* not))) close-dropdown (mf/use-fn @@ -73,10 +74,12 @@ zoom (fmt/format-percent zoom {:precision 0})] [:* - [:div {:on-click open-dropdown - :class (stl/css-case :zoom-widget true - :selected open?) - :title (tr "workspace.header.zoom")} + [:button {:type "button" + :on-click toggle-dropdown + :class (stl/css-case :zoom-widget true + :selected open?) + :title (tr "workspace.header.zoom") + :aria-expanded open?} [:span {:class (stl/css :label)} zoom]] [:& dropdown {:show open? :on-close close-dropdown} [:ul {:class (stl/css :dropdown)} @@ -91,8 +94,8 @@ :aria-label (tr "shortcuts.increase-zoom") :on-click on-increase :icon i/add}]] - [:button {:class (stl/css :reset-btn) - :on-click on-zoom-reset} + [:> button* {:variant "ghost" + :on-click on-zoom-reset} (tr "workspace.header.reset-zoom")]] [:li {:class (stl/css :zoom-option) :on-click on-zoom-fit} @@ -215,35 +218,35 @@ :on-zoom-selected on-zoom-selected}]] [:div {:class (stl/css :comments-section)} - [:button {:title (tr "workspace.toolbar.comments" (get-tt :add-comment)) - :aria-label (tr "workspace.toolbar.comments" (get-tt :add-comment)) - :class (stl/css-case :comments-btn true - :selected (= selected-drawtool :comments)) - :on-click toggle-comments - :data-tool "comments" - :style {:position "relative"}} - deprecated-icon/comments + [:> icon-button* {:variant "action" + :class (stl/css :comments-btn) + :tooltip-placement "bottom-left" + :aria-pressed (= selected-drawtool :comments) + :aria-label (tr "workspace.toolbar.comments" (get-tt :add-comment)) + :icon i/comments + :on-click toggle-comments + :data-tool "comments"} (when ^boolean has-unread-comments? [:div {:class (stl/css :unread)}])]] (when-not ^boolean read-only? [:div {:class (stl/css :history-section)} - [:button - {:title (tr "workspace.sidebar.history") - :aria-label (tr "workspace.sidebar.history") - :class (stl/css-case :selected (contains? layout :document-history) - :history-button true) - :on-click toggle-history} - deprecated-icon/history]]) + [:> icon-button* {:variant "action" + :tooltip-placement "bottom-left" + :aria-pressed (contains? layout :document-history) + :aria-label (tr "workspace.sidebar.history") + :icon i/history + :on-click toggle-history}]]) (when display-share-button? - [:a {:class (stl/css :viewer-btn) + [:a {:class (stl/css :share-btn) :title (tr "workspace.header.share") :on-click open-share-dialog} deprecated-icon/share]) - [:a {:class (stl/css :viewer-btn) - :title (tr "workspace.header.viewer" (get-tt :open-viewer)) - :on-click nav-to-viewer} - deprecated-icon/play]])) + [:> icon-button* {:variant "action" + :tooltip-placement "bottom-left" + :aria-label (tr "workspace.header.viewer" (get-tt :open-viewer)) + :icon i/play + :on-click nav-to-viewer}]])) diff --git a/frontend/src/app/main/ui/workspace/right_header.scss b/frontend/src/app/main/ui/workspace/right_header.scss index 2b338659aa..040b270490 100644 --- a/frontend/src/app/main/ui/workspace/right_header.scss +++ b/frontend/src/app/main/ui/workspace/right_header.scss @@ -4,23 +4,30 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; + +// Only for the shared menu placeholders, and the button placeholders of +// the share link until the DS has a share icon. @use "refactor/common-refactor.scss" as deprecated; .workspace-header-right { display: flex; justify-content: space-between; align-items: center; - min-width: deprecated.$s-256; - padding: deprecated.$s-8; - gap: deprecated.$s-8; - background-color: var(--panel-background-color); + min-inline-size: px2rem(256); + padding: var(--sp-s); + gap: var(--sp-m); + background-color: var(--color-background-primary); } .users-section { position: relative; - min-width: deprecated.$s-32; - max-width: deprecated.$s-72; - padding: deprecated.$s-4 deprecated.$s-6; + min-inline-size: $sz-32; + max-inline-size: px2rem(72); + padding: var(--sp-xs) px2rem(6); } .separator { @@ -28,49 +35,46 @@ } .zoom-widget { - @include deprecated.button-style; + --zoom-widget-foreground-color: var(--color-foreground-secondary); display: flex; align-items: center; justify-content: center; - height: deprecated.$s-28; - max-width: deprecated.$s-48; - width: deprecated.$s-48; - border-radius: deprecated.$br-8; + block-size: $sz-28; + max-inline-size: $sz-48; + inline-size: $sz-48; + padding: 0; + border: none; + border-radius: $br-8; + background: none; + cursor: pointer; .label { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - height: 100%; - padding: deprecated.$s-8 0; - color: var(--button-tertiary-foreground-color-rest); - } - - &:hover { - .label { - color: var(--button-tertiary-foreground-color-focus); - } + block-size: 100%; + padding: var(--sp-s) 0; + color: var(--zoom-widget-foreground-color); } + &:hover, &.selected { - .label { - color: var(--button-tertiary-foreground-color-focus); - } + --zoom-widget-foreground-color: var(--color-foreground-primary); } } .dropdown { @extend %menu-dropdown; - right: deprecated.$s-2; - top: calc(deprecated.$s-2 + deprecated.$s-48); - width: deprecated.$s-272; + inset-inline-end: px2rem(2); + inset-block-start: px2rem(50); + inline-size: px2rem(272); } .basic-zoom-bar { display: flex; justify-content: space-between; - padding: deprecated.$s-6; + padding: px2rem(6); cursor: auto; } @@ -79,21 +83,14 @@ } .zoom-text { - @include deprecated.flex-center; - - height: 100%; - min-width: deprecated.$s-48; + display: flex; + justify-content: center; + align-items: center; + block-size: 100%; + min-inline-size: $sz-48; padding: 0; - margin: 0 deprecated.$s-2; - color: var(--modal-title-foreground-color); -} - -.reset-btn { - @extend %button-tertiary; - - color: var(--button-tertiary-foreground-color-hover); - height: deprecated.$s-28; - border-radius: deprecated.$br-8; + margin: 0 px2rem(2); + color: var(--color-foreground-primary); } .zoom-option { @@ -108,137 +105,38 @@ } &:hover { - color: var(--menu-foreground-color-hover); + color: var(--color-foreground-primary); .shortcuts { .shortcut-key { - color: var(--menu-foreground-color-hover); + color: var(--color-foreground-primary); } } } } +// Holds the unread dot. .comments-btn { + position: relative; +} + +// Raw link until the DS has a share icon. +.share-btn { @extend %button-tertiary; - border-radius: deprecated.$br-8; + border-radius: $br-8; margin: 0; - height: deprecated.$s-28; - width: deprecated.$s-28; + inline-size: $sz-28; + block-size: $sz-28; border: none; + // The legacy share icon has no class. svg { @extend %button-icon; - stroke: var(--icon-foreground); - height: deprecated.$s-16; - width: deprecated.$s-16; - } - - &:hover { - background-color: transparent; - border: none; - } - - &.selected { - background-color: var(--button-tertiary-background-color-selected); - - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } -} - -.history-button { - @extend %button-tertiary; - - border-radius: deprecated.$br-8; - margin: 0; - height: deprecated.$s-28; - width: deprecated.$s-28; - border: none; - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - height: deprecated.$s-16; - width: deprecated.$s-16; - } - - &:hover { - background-color: transparent; - border: none; - } - - &.selected { - background-color: var(--button-tertiary-background-color-selected); - - svg { - stroke: var(--button-tertiary-foreground-color-active); - } - } -} - -.persistence-status-widget { - @include deprecated.flex-center; - - width: deprecated.$s-28; - height: deprecated.$s-28; -} - -.status-icon { - @include deprecated.flex-center; - - width: deprecated.$s-24; - height: deprecated.$s-24; - margin: 0; - border-radius: deprecated.$br-circle; - - svg { - @extend %button-icon; - - stroke: var(--status-widget-icon-foreground-color); - } -} - -.pending-status { - background-color: var(--status-widget-background-color-warning); -} - -.saving-status { - background-color: var(--status-widget-background-color-pending); - - svg { - animation: spin-animation 1s infinite; - animation-timing-function: linear; - } -} - -.saved-status { - background-color: var(--status-widget-background-color-success); -} - -.error-status { - background-color: var(--status-widget-background-color-error); -} - -.share-btn, -.viewer-btn { - @extend %button-tertiary; - - border-radius: deprecated.$br-8; - margin: 0; - width: deprecated.$s-28; - height: deprecated.$s-28; - border: none; - - svg { - @extend %button-icon; - - height: deprecated.$s-16; - width: deprecated.$s-16; - stroke: var(--icon-foreground); + block-size: $sz-16; + inline-size: $sz-16; + stroke: var(--color-foreground-secondary); } &:hover { @@ -249,11 +147,13 @@ .unread { position: absolute; - width: 8px; - height: 8px; - border: 2px solid var(--color-background-tertiary); - border-radius: 50%; + inline-size: px2rem(8); + block-size: px2rem(8); + border: $b-2 solid var(--color-background-tertiary); + border-radius: $br-circle; background: var(--color-foreground-error); - top: 6px; - right: 6px; + + // Over the top right corner of the icon, 4px from the 24px button edge. + inset-block-start: var(--sp-xs); + inset-inline-end: var(--sp-xs); }