♻️ Use DS icon buttons in the viewer thumbnails panel (#12518)

* ♻️ 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

* ♻️ 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
This commit is contained in:
Eva Marco 2026-10-26 10:33:11 +01:00 committed by GitHub
parent 9911f82087
commit ef17aea39e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 145 additions and 82 deletions

View File

@ -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,
});
});
});

View File

@ -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)}

View File

@ -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);
}

View File

@ -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"

View File

@ -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"