♻️ Use DS icons in the dashboard sidebar (#12534)

* ♻️ Use DS icons in the dashboard sidebar

The search, clear search, pinned projects placeholder and logout icons
of the dashboard sidebar use the DS icons, and they get their color
from the color property. The two search buttons had untranslated test
names as their labels ("dashboard-search"); they are now named "Search"
and "Clear search". The pin keeps its place: the DS small icon draws
12px inside a 16px box, so its margins shrink by 2px.

New key: dashboard.clear-search (en, es). The dashboard page object
looks the search button up by its new name. A visual test covers the
search box with and without a term, the pinned projects placeholder and
the profile menu.

AI-assisted-by: claude-opus-5-5

* ♻️ Move dashboard sidebar styles off the deprecated styles

Legacy color aliases resolve to their DS tokens, deprecated mixins,
sizes and font variables are inlined with DS values, physical
properties become logical ones and the DS typography module is used
without a namespace. An invalid max-width with two values, which the
browser ignored, is gone.

Two rules that match nothing are commented out with a TODO: the profile
menu arrow svg rule (the arrow is the DS icon svg itself) and the
upgrade plan section, which sidebar.cljs does not use.

refactor/ stays only for the profile menu placeholders (%menu-dropdown,
%menu-item-base) and the shared dashboard header styles
(common-dashboard). No visual change.

AI-assisted-by: claude-opus-5-5

* 🔥 Remove unused styles from the dashboard sidebar

The SCSS refactor of the dashboard sidebar 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:
.profile-dropdown-item .open-arrow svg, .upgrade-plan-section,
.penpot-free, .no-limits, .power-up. No visual change.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Eva Marco 2026-10-27 11:41:19 +01:00 committed by GitHub
parent afd838ea5b
commit 33d304c0af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 145 additions and 140 deletions

View File

@ -82,7 +82,10 @@ export class DashboardPage extends BaseWebSocketPage {
this.fontsLink = this.sidebar.getByText("Fonts");
this.librariesLink = this.sidebar.getByText("Libraries");
this.searchButton = page.getByRole("button", { name: "dashboard-search" });
this.searchButton = page.getByRole("button", {
name: "Search",
exact: true,
});
this.searchInput = page.getByPlaceholder("Search…");
this.teamDropdown = this.sidebar.getByRole("button", {

View File

@ -486,3 +486,31 @@ test.describe("Team modals controls", () => {
await expect(modal).toHaveScreenshot(shot);
});
});
test.describe("Dashboard sidebar controls", () => {
const shot = { maxDiffPixelRatio: 0, threshold: 0 };
test("Search box, pinned placeholder and profile menu", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
const search = page.locator("[class*='sidebar-search']");
await expect(search).toBeVisible();
await expect(search).toHaveScreenshot(shot);
await expect(page.getByTestId("pinned-projects")).toHaveScreenshot(shot);
// A search term swaps the search icon for the clear button.
await page.locator("#search-input").fill("test");
await expect(page).toHaveURL(/search/);
const clear = search.locator("button");
await clear.hover();
await expect(search).toHaveScreenshot(shot);
await page.getByTestId("profile-btn").click();
await expect(page.getByTestId("logout-profile-opt")).toBeVisible();
await expect(
page.getByTestId("logout-profile-opt").locator("xpath=.."),
).toHaveScreenshot(shot);
});
});

View File

@ -37,7 +37,6 @@
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.ds.layout.menu :refer [context-menu*]]
[app.main.ui.hooks :refer [use-focus-timer-ref]]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.nitrate.nitrate-form]
[app.util.dom :as dom]
[app.util.dom.dnd :as dnd]
@ -48,18 +47,6 @@
[goog.functions :as f]
[rumext.v2 :as mf]))
(def ^:private clear-search-icon
(deprecated-icon/icon-xref :delete-text (stl/css :clear-search-icon)))
(def ^:private search-icon
(deprecated-icon/icon-xref :search (stl/css :search-icon)))
(def ^:private pin-icon
(deprecated-icon/icon-xref :pin (stl/css :pin-icon)))
(def ^:private exit-icon
(deprecated-icon/icon-xref :exit (stl/css :exit-icon)))
(defn schedule-focus-by-id!
[ref element-id]
(when-let [h (mf/ref-val ref)]
@ -101,7 +88,6 @@
(schedule-focus-by-id! focus-timer-ref (str project-id))
(st/emit! (dcm/go-to-dashboard-files :project-id project-id)))))
on-edit-open
(mf/use-fn #(swap! local* assoc :edition? true))
@ -239,15 +225,19 @@
(if (or @focused? (seq search-term))
[:button {:class (stl/css :search-btn :clear-search-btn)
:tab-index "0"
:aria-label "dashboard-clear-search"
:aria-label (tr "dashboard.clear-search")
:on-click on-clear-click
:on-key-down handle-clear-search}
clear-search-icon]
[:> icon* {:icon-id i/delete-text
:class (stl/css :search-icon)
:aria-hidden true}]]
[:button {:class (stl/css :search-btn)
:aria-label "dashboard-search"
:aria-label (tr "labels.search")
:on-click on-clear-click}
search-icon])]))
[:> icon* {:icon-id i/search
:class (stl/css :search-icon)
:aria-hidden true}]])]))
(mf/defc sidebar-content*
{::mf/private true}
@ -364,7 +354,6 @@
:keyboard-action go-drafts-with-key}
[:span {:class (stl/css :element-title)} (tr "labels.drafts")]]]]]
[:div {:class (stl/css :sidebar-content-section)}
[:div {:class (stl/css :sidebar-section-title)}
(tr "labels.sources")]
@ -384,7 +373,6 @@
:keyboard-action go-libs-with-key}
[:span {:class (stl/css :element-title)} (tr "labels.shared-libraries")]]]]]
[:div {:class (stl/css :sidebar-content-section)
:data-testid "pinned-projects"}
[:div {:class (stl/css :sidebar-section-title)}
@ -399,7 +387,9 @@
:team-id (:id team)
:is-selected (= (:id item) (:id project))}])]
[:div {:class (stl/css :sidebar-empty-placeholder)}
pin-icon
[:> icon* {:icon-id i/pin
:size "s"
:class (stl/css :pin-icon)}]
[:span {:class (stl/css :empty-text)} (tr "dashboard.no-projects-placeholder")]])]]
[:div {:class (stl/css-case :separator true :overflow-separator overflow?)}]]]))
@ -673,7 +663,6 @@
[:> dashboard-cta* {:profile profile}]
[:> subscription-sidebar* {:profile profile}])))
(when (and team profile)
[:& comments-section
{:profile profile
@ -704,7 +693,6 @@
[:li {:class (stl/css :profile-separator)}]
[:> dropdown-menu-item* {:class (stl/css-case :profile-dropdown-item true)
:on-click on-menu-click
:on-key-down (fn [event]
@ -749,7 +737,8 @@
[:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item :item-with-icon)
:on-click handle-logout-click
:data-testid "logout-profile-opt"}
exit-icon
[:> icon* {:icon-id i/exit
:class (stl/css :exit-icon)}]
(tr "labels.logout")]]
(when (and team profile)

View File

@ -4,14 +4,21 @@
//
// Copyright (c) KALEIDOS SUBSIDIARY SL
@use "ds/typography.scss" as t;
@use "ds/typography.scss" as *;
@use "ds/colors.scss" as *;
@use "ds/spacing.scss" as *;
@use "ds/_borders.scss" as *;
@use "ds/_sizes.scss" as *;
@use "ds/_utils.scss" as *;
@use "ds/z-index.scss" as *;
@use "ds/mixins.scss" as *;
// The shared dashboard header styles move in their own change: they are
// used by ten dashboard modules.
@use "common/refactor/common-dashboard";
// refactor/ stays for the menu placeholders (%menu-dropdown,
// %menu-item-base) of the profile menu and its submenus.
@use "common/refactor/common-refactor.scss" as deprecated;
// SIDEBAR COMPONENT
@ -20,19 +27,19 @@
grid-row: 1 / span 2;
grid-column: 1 / span 2;
grid-template-rows: auto 1fr auto;
height: 100%;
width: 100%;
block-size: 100%;
inline-size: 100%;
margin: 0 var(--sp-l) 0 0;
border-right: $b-1 solid var(--panel-border-color);
background-color: var(--panel-background-color);
border-inline-end: $b-1 solid var(--color-background-quaternary);
background-color: var(--color-background-primary);
}
// SIDEBAR CONTENT COMPONENT
.sidebar-content-wrapper {
display: flex;
flex-direction: column;
min-height: 0;
height: 100%;
min-block-size: 0;
block-size: 100%;
overflow: hidden;
}
@ -41,7 +48,7 @@
grid-template-rows: auto auto auto auto 1fr;
gap: var(--sp-xxl);
flex: 1;
min-height: 0;
min-block-size: 0;
padding: 0;
overflow: hidden auto;
}
@ -52,17 +59,17 @@
}
.separator {
height: var(--sp-xxs);
width: 94%;
margin-left: 3%;
block-size: var(--sp-xxs);
inline-size: 94%;
margin-inline-start: 3%;
}
.overflow-separator {
border-bottom: $b-1 solid var(--color-background-quaternary);
border-block-end: $b-1 solid var(--color-background-quaternary);
}
.sidebar-section-title {
@include t.use-typography("headline-small");
@include use-typography("headline-small");
padding: 0 var(--sp-s) var(--sp-s) var(--sp-xxl);
color: var(--color-foreground-secondary);
@ -82,31 +89,33 @@
cursor: pointer;
&:hover {
background-color: var(--sidebar-element-background-color-hover);
background-color: var(--color-background-secondary);
// The item labels have no class.
span {
color: var(--sidebar-element-foreground-color-hover);
color: var(--color-accent-primary);
}
}
&.current {
background-color: var(--sidebar-element-background-color-selected);
background-color: var(--color-background-quaternary);
.element-title {
color: var(--sidebar-element-foreground-color-selected);
color: var(--color-accent-primary);
}
}
}
// The icon has no class.
.recent-projects svg {
stroke: var(--main-icon-foreground);
stroke: var(--color-foreground-primary);
}
.sidebar-link {
display: block;
padding: var(--sp-s) var(--sp-s) var(--sp-s) var(--sp-xxl);
font-weight: deprecated.$fw400;
width: 100%;
font-weight: 400;
inline-size: 100%;
&:hover {
text-decoration: none;
@ -118,31 +127,33 @@
}
.element-title {
@include deprecated.text-ellipsis;
@include text-ellipsis;
width: deprecated.$s-256;
inline-size: px2rem(256);
color: var(--color-foreground-primary);
font-size: deprecated.$fs-14;
font-size: px2rem(14);
}
// Pinned projects
.sidebar-empty-placeholder {
padding: var(--sp-m);
color: var(--empty-message-foreground-color);
color: var(--color-foreground-secondary);
display: flex;
align-items: center;
}
// The DS small icon draws 12px inside a 16px box: 2px less margin on each
// side keeps the drawing and the text where they were.
.pin-icon {
@extend %button-icon-small;
stroke: var(--icon-foreground);
margin: 0 var(--sp-m);
flex-shrink: 0;
stroke-width: 1.33px;
color: var(--color-foreground-secondary);
margin: 0 px2rem(10);
}
.empty-text {
font-size: deprecated.$fs-12;
font-size: px2rem(12);
}
// Search
@ -155,55 +166,58 @@
border: $b-1 solid transparent;
margin: 0 var(--sp-l);
border-radius: $br-8;
background-color: var(--search-bar-input-background-color);
background-color: var(--color-background-tertiary);
}
.input-text {
@include t.use-typography("title-small");
@include use-typography("title-small");
height: $sz-40;
width: 100%;
block-size: $sz-40;
inline-size: 100%;
padding: $sz-6 var(--sp-m);
margin: 0;
border: transparent;
border-radius: $br-8;
background: transparent;
color: var(--search-bar-foreground-color);
color: var(--color-foreground-primary);
&:focus,
&:focus-within,
&:focus-visible {
outline: none;
border: $b-1 solid var(--search-bar-input-border-color-focus);
border: $b-1 solid var(--color-accent-primary);
}
::placeholder {
color: var(--search-bar-placeholder-foreground-color);
color: var(--color-foreground-secondary);
}
}
.search-btn {
@include deprecated.button-style;
@include deprecated.flex-center;
border: none;
background: none;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
--sidebar-search-foreground-color: var(--color-foreground-secondary);
color: var(--sidebar-search-foreground-color);
position: absolute;
right: 0;
height: var(--sp-xxl);
width: var(--sp-xxxl);
inset-inline-end: 0;
block-size: var(--sp-xxl);
inline-size: var(--sp-xxxl);
padding: 0 var(--sp-s);
}
.search-icon,
.clear-search-btn {
@extend %button-icon;
--sidebar-search-foreground-color: var(--search-bar-icon-foreground-color);
stroke: var(--sidebar-search-foreground-color);
.search-icon {
flex-shrink: 0;
stroke-width: 1px;
}
.clear-search-btn:hover {
--sidebar-search-foreground-color: var(--search-bar-icon-foreground-color-hover);
--sidebar-search-foreground-color: var(--color-accent-primary);
}
// Profile
@ -212,33 +226,32 @@
display: grid;
grid-template-columns: 1fr auto;
padding: var(--sp-m) var(--sp-l);
border-top: $b-1 solid var(--panel-border-color);
background-color: var(--profile-section-background-color);
border-block-start: $b-1 solid var(--color-background-quaternary);
background-color: var(--color-background-tertiary);
cursor: pointer;
}
.profile {
@include deprecated.button-style;
border: none;
background: none;
display: grid;
grid-template-columns: auto 1fr;
gap: var(--sp-s);
cursor: pointer;
text-align: left;
text-align: start;
}
.profile-fullname {
@include t.use-typography("title-small");
@include deprecated.text-ellipsis;
@include use-typography("title-small");
@include text-ellipsis;
align-self: center;
max-width: var(--sp-l) 0;
color: var(--profile-foreground-color);
color: var(--color-foreground-primary);
}
.profile-img {
height: $sz-40;
width: $sz-40;
block-size: $sz-40;
inline-size: $sz-40;
border-radius: $br-circle;
}
@ -247,14 +260,14 @@
inset-inline-start: var(--sp-s);
inset-block-end: px2rem(72); // 72 is the height of the profile button
min-width: calc(100% - var(--sp-s));
min-inline-size: calc(100% - var(--sp-s));
// TODO ADD animation fadeInUp
}
.profile-dropdown-item {
@extend %menu-item-base;
@include t.use-typography("body-medium");
@include use-typography("body-medium");
block-size: $sz-40;
margin-block-end: var(--sp-xs);
@ -262,29 +275,25 @@
padding-inline-start: var(--sp-l);
&:hover {
color: var(--menu-foreground-color-hover);
color: var(--color-foreground-primary);
.open-arrow {
--icon-stroke-color: var(--menu-foreground-color-hover);
--icon-stroke-color: var(--color-foreground-primary);
}
}
}
.profile-dropdown-item .open-arrow {
@include deprecated.flex-center;
}
.profile-dropdown-item .open-arrow svg {
@extend %button-icon;
stroke: var(--icon-foreground);
display: flex;
justify-content: center;
align-items: center;
}
.sub-menu {
@extend %menu-dropdown;
inset-inline-start: calc(deprecated.$s-292 + var(--sp-s));
min-width: deprecated.$s-192;
inset-inline-start: calc(#{px2rem(292)} + var(--sp-s));
min-inline-size: $sz-192;
}
// Each submenu is positioned via its bottom edge; the visual top lands
@ -293,11 +302,11 @@
// its top edge sits one row above Community — aligning with the
// "Help & Learning" trigger row in the parent menu.
.sub-menu.help-learning {
inset-block-end: deprecated.$s-120;
inset-block-end: $sz-120;
}
.sub-menu.community {
inset-block-end: deprecated.$s-120;
inset-block-end: $sz-120;
}
.sub-menu.about {
@ -306,7 +315,7 @@
.submenu-item {
@extend %menu-item-base;
@include t.use-typography("body-medium");
@include use-typography("body-medium");
block-size: $sz-40;
margin-block-end: var(--sp-xs);
@ -314,7 +323,7 @@
padding-inline: var(--sp-l);
&:hover {
color: var(--menu-foreground-color-hover);
color: var(--color-foreground-primary);
}
&.checking {
@ -334,7 +343,7 @@
}
.submenu-separator {
border-top: $b-1 solid var(--color-background-quaternary);
border-block-start: $b-1 solid var(--color-background-quaternary);
margin: 0;
}
@ -345,8 +354,8 @@
}
.menu-version {
@include t.use-typography("code-font");
@include deprecated.text-ellipsis;
@include use-typography("code-font");
@include text-ellipsis;
color: var(--color-foreground-secondary);
margin-inline-start: var(--sp-s);
@ -354,8 +363,8 @@
}
.profile-separator {
height: $sz-6;
border-top: $b-1 solid var(--color-background-quaternary);
block-size: $sz-6;
border-block-start: $b-1 solid var(--color-background-quaternary);
}
.item-with-icon {
@ -365,38 +374,6 @@
}
.exit-icon {
@extend %button-icon;
stroke: var(--icon-foreground);
}
.upgrade-plan-section {
@include deprecated.button-style;
display: flex;
justify-content: space-between;
border: $b-1 solid var(--color-background-quaternary);
border-radius: var(--sp-s);
padding: var(--sp-m);
margin: var(--sp-m);
stroke-width: 1px;
color: var(--color-foreground-secondary);
cursor: pointer;
}
.penpot-free {
@include t.use-typography("body-medium");
display: flex;
flex-direction: column;
text-align: left;
}
.no-limits {
@include t.use-typography("body-small");
}
.power-up {
@include t.use-typography("body-small");
color: var(--color-accent-tertiary);
}

View File

@ -356,6 +356,10 @@ msgstr "View full changelog"
msgid "dashboard.check-updates.view-release-notes"
msgstr "View release notes"
#: src/app/main/ui/dashboard/sidebar.cljs:228
msgid "dashboard.clear-search"
msgstr "Clear search"
#: src/app/main/ui/dashboard/deleted.cljs:322
msgid "dashboard.clear-trash-button"
msgstr "Clear trash"

View File

@ -362,6 +362,10 @@ msgstr "Ver changelog completo"
msgid "dashboard.check-updates.view-release-notes"
msgstr "Ver notas de la versión"
#: src/app/main/ui/dashboard/sidebar.cljs:228
msgid "dashboard.clear-search"
msgstr "Borrar búsqueda"
#: src/app/main/ui/dashboard/deleted.cljs:322
msgid "dashboard.clear-trash-button"
msgstr "Vaciar papelera"