From ef17aea39eb0c56e66a506470ddc40e8a08efe9f Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Mon, 26 Oct 2026 10:33:11 +0100 Subject: [PATCH] :recycle: Use DS icon buttons in the viewer thumbnails panel (#12518) * :recycle: Use DS icon buttons in the viewer thumbnails panel The expand, close and scroll buttons of the viewer thumbnails panel become ghost DS icon buttons. They had no accessible name; they are now labelled, and the expand button says whether the panel is expanded. The rotated legacy arrow gives way to the DS arrow icons. The thumbnails stay raw buttons, since they are list items. New keys: labels.expand and viewer.thumbnails.scroll-left/right (en, es). A visual test covers the panel collapsed, on hover and expanded. AI-assisted-by: claude-opus-5-5 * :recycle: Move viewer thumbnails panel styles off the deprecated styles Spacing, sizes, borders, typographies and color aliases move to DS tokens and logical properties, and the file no longer imports refactor/. The scroll buttons keep their 60px height through --button-height and still show only on hover; their tooltip wrappers take the grid cells. The DS body-small typography differs from the old one only in a more compact line-height, so the counter and the board names sit 1px higher. The panel z-index (10) has no DS token and keeps a TODO. AI-assisted-by: claude-opus-5-5 --- .../ui/visual-specs/visual-viewer.spec.js | 39 ++++++ .../src/app/main/ui/viewer/thumbnails.cljs | 37 +++-- .../src/app/main/ui/viewer/thumbnails.scss | 127 ++++++++---------- frontend/translations/en.po | 12 ++ frontend/translations/es.po | 12 ++ 5 files changed, 145 insertions(+), 82 deletions(-) diff --git a/frontend/playwright/ui/visual-specs/visual-viewer.spec.js b/frontend/playwright/ui/visual-specs/visual-viewer.spec.js index 8361c263fb..9fb96ad4b3 100644 --- a/frontend/playwright/ui/visual-specs/visual-viewer.spec.js +++ b/frontend/playwright/ui/visual-specs/visual-viewer.spec.js @@ -151,3 +151,42 @@ test("User opens Share modal", async ({ page }) => { ).toBeVisible(); await expect(viewerPage.page).toHaveScreenshot(); }); + +test.describe("Thumbnails panel", () => { + test("Shows the thumbnails panel collapsed and expanded", async ({ + page, + }) => { + const viewerPage = new ViewerPage(page); + await viewerPage.setupLoggedInUser(); + await viewerPage.setupFileWithMultipleBoards(); + await viewerPage.goToViewer({ + fileId: "dd5cc0bb-91ff-81b9-8004-77df9cd3edb0", + pageId: "dd5cc0bb-91ff-81b9-8004-77df9cd3edb3", + }); + + await page.locator("#current-frame").click(); + const panel = page.locator("section").filter({ hasText: /boards/i }); + await expect(panel).toBeVisible(); + await page.mouse.move(640, 600); + // Collapsed: the summary with its expand and close buttons. + await expect(panel).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + // The scroll handlers only show on hover. + await panel.locator("button").nth(3).hover(); + await expect(panel).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await panel.locator("button").first().click(); + await page.mouse.move(640, 700); + // Expanded: the expand arrow points up. + await expect(panel).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); diff --git a/frontend/src/app/main/ui/viewer/thumbnails.cljs b/frontend/src/app/main/ui/viewer/thumbnails.cljs index d7af5b5d41..d23c5009b8 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.cljs +++ b/frontend/src/app/main/ui/viewer/thumbnails.cljs @@ -14,7 +14,8 @@ [app.main.data.viewer :as dv] [app.main.render :as render] [app.main.store :as st] - [app.main.ui.icons :as deprecated-icon] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] + [app.main.ui.ds.foundations.assets.icon :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.object :as obj] @@ -63,10 +64,18 @@ [:div {:class (stl/css :thumbnails-list-expanded)} children]] [:div {:class (stl/css :thumbnails-content)} - [:button {:class (stl/css :left-scroll-handler) - :on-click on-left-arrow-click} deprecated-icon/arrow] - [:button {:class (stl/css :right-scroll-handler) - :on-click on-right-arrow-click} deprecated-icon/arrow] + [:> icon-button* {:variant "ghost" + :class (stl/css :scroll-handler) + :tooltip-class (stl/css :left-scroll-handler) + :aria-label (tr "viewer.thumbnails.scroll-left") + :icon i/arrow-left + :on-click on-left-arrow-click}] + [:> icon-button* {:variant "ghost" + :class (stl/css :scroll-handler) + :tooltip-class (stl/css :right-scroll-handler) + :aria-label (tr "viewer.thumbnails.scroll-right") + :icon i/arrow-right + :on-click on-right-arrow-click}] [:div {:class (stl/css :thumbnails-list) :ref container @@ -76,15 +85,22 @@ children]]]))) (mf/defc thumbnails-summary* - [{:keys [on-toggle-expand on-close total]}] + [{:keys [on-toggle-expand on-close total is-expanded]}] [:div {:class (stl/css :thumbnails-summary)} [:span {:class (stl/css :counter)} (tr "labels.num-of-frames" (i18n/c total))] [:span {:class (stl/css :actions)} - [:button {:class (stl/css :expand-btn) - :on-click on-toggle-expand} deprecated-icon/arrow] - [:button {:class (stl/css :close-btn) - :on-click on-close} deprecated-icon/close]]]) + [:> icon-button* {:variant "ghost" + :aria-label (if is-expanded + (tr "labels.collapse") + (tr "labels.expand")) + :aria-expanded (boolean is-expanded) + :icon (if is-expanded i/arrow-up i/arrow-down) + :on-click on-toggle-expand}] + [:> icon-button* {:variant "ghost" + :aria-label (tr "labels.close") + :icon i/close + :on-click on-close}]]]) (mf/defc thumbnail-item* {::mf/wrap [mf/memo @@ -137,6 +153,7 @@ [:> thumbnails-summary* {:on-toggle-expand toggle-expand :on-close on-close + :is-expanded expanded? :total (count frames)}] [:> thumbnails-content* {:is-expanded expanded? :total (count frames)} diff --git a/frontend/src/app/main/ui/viewer/thumbnails.scss b/frontend/src/app/main/ui/viewer/thumbnails.scss index 63dc586a67..fdea0a3443 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.scss +++ b/frontend/src/app/main/ui/viewer/thumbnails.scss @@ -4,65 +4,52 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/mixins.scss" as *; +@use "ds/typography.scss" as *; .viewer-thumbnails { - background-color: var(--viewer-background-color); + background-color: var(--color-background-secondary); grid-row: 1 / span 1; grid-column: 1 / span 1; overflow: hidden; display: flex; flex-direction: column; - z-index: deprecated.$z-index-10; + + // TODO: no DS z-index token for 10; it keeps the panel above the + // viewer content (z-index 2). + z-index: 10; } .expanded { grid-row: 1 / span 2; - - .expand-btn svg { - transform: rotate(-90deg); - } } .thumbnails-summary { display: flex; justify-content: space-between; align-items: center; - height: deprecated.$s-32; - margin: deprecated.$s-24 deprecated.$s-24 0 deprecated.$s-24; + block-size: $sz-32; + margin: var(--sp-xxl) var(--sp-xxl) 0 var(--sp-xxl); } .counter { - @include deprecated.body-small-typography; + @include use-typography("body-small"); - color: var(--viewer-thumbnails-control-foreground-color); + color: var(--color-foreground-secondary); } .actions { - @include deprecated.flex-row; - - width: deprecated.$s-60; -} - -.expand-btn, -.close-btn { - @extend %button-tertiary; - - height: deprecated.$s-32; - width: deprecated.$s-28; - - svg { - @extend %button-icon; - } -} - -.expand-btn svg { - transform: rotate(90deg); + display: flex; + align-items: center; + gap: var(--sp-xs); } .thumbnails-content { display: grid; - grid-template-columns: deprecated.$s-40 auto deprecated.$s-40; + grid-template-columns: $sz-40 auto $sz-40; grid-template-rows: auto; } @@ -74,36 +61,28 @@ overflow: hidden; } +// Tooltip wrappers of the scroll buttons: they take the grid cell. .right-scroll-handler, .left-scroll-handler { - @extend %button-tertiary; - @include deprecated.flex-center; - grid-column: 3 / span 1; grid-row: 1 / span 1; - width: deprecated.$s-32; - height: deprecated.$s-60; margin: auto 0; - z-index: deprecated.$z-index-10; - opacity: 0; - - &:hover { - opacity: 1; - } - - svg { - @extend %button-icon; - - stroke: var(--icon-foreground); - } + z-index: 10; } .left-scroll-handler { grid-column: 1 / span 1; - grid-row: 1 / span 1; +} - svg { - transform: rotate(180deg); +// The scroll buttons are as tall as a thumbnail row and only show on hover. +.thumbnails-content .scroll-handler { + --button-height: #{px2rem(60)}; + + opacity: 0; + + &:hover, + &:focus-visible { + opacity: 1; } } @@ -120,45 +99,49 @@ position: relative; } +// A list item, not a DS button. .thumbnail-item { - @include deprecated.button-style; - display: flex; flex-direction: column; - padding: deprecated.$s-16; + padding: var(--sp-l); + border: none; + background: none; + cursor: pointer; } .thumbnail-preview { - @include deprecated.flex-center; - - width: deprecated.$s-132; - min-height: deprecated.$s-132; - height: deprecated.$s-132; - padding: deprecated.$s-4; + display: flex; + justify-content: center; + align-items: center; + inline-size: px2rem(132); + min-block-size: px2rem(132); + block-size: px2rem(132); + padding: var(--sp-xs); + // The board preview markup has no class of its own. svg { - width: 100%; - height: 100%; + inline-size: 100%; + block-size: 100%; } &.selected { - background-color: var(--viewer-thumbnail-background-color-selected); - border-radius: deprecated.$br-8; + background-color: var(--color-accent-primary-muted); + border-radius: $br-8; } &:hover { - border: deprecated.$s-1 solid var(--viewer-thumbnail-border-color); - border-radius: deprecated.$br-8; + border: $b-1 solid var(--color-accent-primary); + border-radius: $br-8; } } .thumbnail-info { - @include deprecated.body-small-typography; - @include deprecated.text-ellipsis; + @include use-typography("body-small"); + @include text-ellipsis; text-align: center; - color: var(--viewer-thumbnails-control-foreground-color); - padding: deprecated.$s-8 0; - width: 100%; - max-width: deprecated.$s-132; + color: var(--color-foreground-secondary); + padding: var(--sp-s) 0; + inline-size: 100%; + max-inline-size: px2rem(132); } diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 4d87a2c487..577cc740a1 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2948,6 +2948,10 @@ msgstr "Error" msgid "labels.exit" msgstr "Exit" +#: src/app/main/ui/viewer/thumbnails.cljs:96 +msgid "labels.expand" +msgstr "Expand" + #: src/app/main/ui/dashboard/team.cljs:782 msgid "labels.expired-invitation" msgstr "Expired" @@ -6127,6 +6131,14 @@ msgstr "Show interactions on click" msgid "viewer.header.sitemap" msgstr "Sitemap" +#: src/app/main/ui/viewer/thumbnails.cljs:70 +msgid "viewer.thumbnails.scroll-left" +msgstr "Scroll left" + +#: src/app/main/ui/viewer/thumbnails.cljs:76 +msgid "viewer.thumbnails.scroll-right" +msgstr "Scroll right" + #: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:76 msgid "webgl.modals.webgl-unavailable.cta-dashboard" msgstr "Go to dashboard" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index d40ca22260..434c88d4ba 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -2986,6 +2986,10 @@ msgstr "Error" msgid "labels.exit" msgstr "Salir" +#: src/app/main/ui/viewer/thumbnails.cljs:96 +msgid "labels.expand" +msgstr "Expandir" + #: src/app/main/ui/dashboard/team.cljs:782 msgid "labels.expired-invitation" msgstr "Expirada" @@ -6208,6 +6212,14 @@ msgstr "Mostrar interacciones al hacer click" msgid "viewer.header.sitemap" msgstr "Mapa del sitio" +#: src/app/main/ui/viewer/thumbnails.cljs:70 +msgid "viewer.thumbnails.scroll-left" +msgstr "Desplazar a la izquierda" + +#: src/app/main/ui/viewer/thumbnails.cljs:76 +msgid "viewer.thumbnails.scroll-right" +msgstr "Desplazar a la derecha" + #: src/app/main/ui/workspace/webgl_unavailable_modal.cljs:76 msgid "webgl.modals.webgl-unavailable.cta-dashboard" msgstr "Ir al panel"