mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
♻️ Use DS buttons and icons in the assets tab (#12559)
* ♻️ Use DS buttons and icons in the assets tab - "Add library" and "Manage library" become DS primary and secondary buttons, full width with the label centered; they keep their test id. - The filter button stays a raw segment of the search bar; its legacy icon becomes the DS filter icon. Its opened state comes before the focus and hover rules, as the legacy placeholder did, so the focus border still shows. - The create/rename group modal gets a ghost DS icon button named "Close", placed on the tooltip wrapper, and DS cancel (secondary) and create/rename (primary) buttons instead of raw inputs. A visual test covers the add and manage library buttons, the filter at rest, on hover and opened, and the create group modal. AI-assisted-by: claude-opus-5-5 * ♻️ Move assets tab styles off the deprecated styles assets.scss no longer imports refactor/: its mixins, sizes, spacing and input / button icon color aliases resolve to DS values and tokens, with logical properties. The unused .sections-container, .section-item and .section-btn rules are deleted (the filter menu is the shared context menu). assets/groups.scss does the same and keeps refactor/ only for the modal placeholders (%modal-*) and %input-with-label. Its legacy uppercase title becomes the DS headline-small typography, which differs only in its size (12px instead of 11px): the group modal grows 2px. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
33d304c0af
commit
8800ff4bc9
@ -1106,3 +1106,58 @@ test.describe("Unpublish library modal", () => {
|
||||
await expect(modal).toHaveScreenshot(shot);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Assets tab controls", () => {
|
||||
const shot = { maxDiffPixelRatio: 0, threshold: 0 };
|
||||
|
||||
test("Add library button", async ({ page }) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.goToWorkspace();
|
||||
await workspace.clickAssets();
|
||||
|
||||
const add = workspace.sidebar.getByTestId("libraries");
|
||||
await expect(add).toBeVisible();
|
||||
await page.mouse.move(640, 600);
|
||||
await expect(add).toHaveScreenshot(shot);
|
||||
await add.hover();
|
||||
await expect(add).toHaveScreenshot(shot);
|
||||
});
|
||||
|
||||
test("Libraries, filter and group modal", async ({ page }) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const { fileId, pageId } = await setupFileWithAssets(workspace);
|
||||
await workspace.goToWorkspace({ fileId, pageId });
|
||||
await workspace.clickAssets();
|
||||
|
||||
const header = workspace.sidebar
|
||||
.getByTestId("libraries")
|
||||
.locator("xpath=..");
|
||||
await expect(workspace.sidebar.getByTestId("libraries")).toBeVisible();
|
||||
await page.mouse.move(640, 600);
|
||||
await expect(header).toHaveScreenshot(shot);
|
||||
|
||||
await workspace.sidebar.getByTestId("libraries").hover();
|
||||
await expect(header).toHaveScreenshot(shot);
|
||||
|
||||
const filter = workspace.sidebar.getByTitle("Filter");
|
||||
await filter.hover();
|
||||
await expect(header).toHaveScreenshot(shot);
|
||||
|
||||
await filter.click();
|
||||
await page.mouse.move(640, 600);
|
||||
await expect(header).toHaveScreenshot(shot);
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
await workspace.sidebar.getByRole("button", { name: "Colors" }).click();
|
||||
await workspace.sidebar
|
||||
.locator("[class*='colors'] [class*='asset-list-item']")
|
||||
.first()
|
||||
.click({ button: "right" });
|
||||
await page.getByText("Group", { exact: true }).click();
|
||||
const modal = page.locator("[class*='groups__modal-container']");
|
||||
await expect(modal).toBeVisible();
|
||||
await page.mouse.move(10, 700);
|
||||
await expect(modal).toHaveScreenshot(shot);
|
||||
});
|
||||
});
|
||||
|
||||
@ -18,8 +18,9 @@
|
||||
[app.main.ui.components.context-menu-a11y :refer [context-menu*]]
|
||||
[app.main.ui.components.search-bar :refer [search-bar*]]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||
[app.main.ui.workspace.sidebar.assets.file-library :refer [file-library*]]
|
||||
[app.main.ui.workspace.sidebar.scroll :as sc]
|
||||
@ -201,14 +202,18 @@
|
||||
[:div {:class (stl/css :assets-header)}
|
||||
(when-not ^boolean read-only?
|
||||
(if (and (= num-libs 1) (empty? components) (not shared?))
|
||||
[:button {:class (stl/css :add-library-button)
|
||||
:on-click show-libraries-dialog
|
||||
:data-testid "libraries"}
|
||||
[:> button* {:variant "primary"
|
||||
:type "button"
|
||||
:class (stl/css :libraries-button)
|
||||
:on-click show-libraries-dialog
|
||||
:data-testid "libraries"}
|
||||
(tr "workspace.assets.add-library")]
|
||||
|
||||
[:button {:class (stl/css :libraries-button)
|
||||
:on-click show-libraries-dialog
|
||||
:data-testid "libraries"}
|
||||
[:> button* {:variant "secondary"
|
||||
:type "button"
|
||||
:class (stl/css :libraries-button)
|
||||
:on-click show-libraries-dialog
|
||||
:data-testid "libraries"}
|
||||
(tr "workspace.assets.manage-library")]))
|
||||
|
||||
|
||||
@ -221,7 +226,8 @@
|
||||
:title (tr "workspace.assets.filter")
|
||||
:class (stl/css-case :section-button true
|
||||
:opened menu-open?)}
|
||||
deprecated-icon/filter-icon]]
|
||||
[:> icon* {:icon-id i/filter
|
||||
:class (stl/css :section-icon)}]]]
|
||||
|
||||
[:> context-menu*
|
||||
{:on-close on-menu-close
|
||||
|
||||
@ -4,141 +4,98 @@
|
||||
//
|
||||
// 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 *;
|
||||
|
||||
.assets-bar {
|
||||
display: grid;
|
||||
grid-auto-rows: max-content;
|
||||
|
||||
// TODO: ugly hack :( Fix this! we shouldn't be hardcoding this height
|
||||
height: calc(100vh - deprecated.$s-92);
|
||||
block-size: calc(100vh - #{px2rem(92)});
|
||||
scrollbar-gutter: stable;
|
||||
overflow-y: auto;
|
||||
padding-top: deprecated.$s-8;
|
||||
padding-block-start: var(--sp-s);
|
||||
}
|
||||
|
||||
// A DS button keeps its label at the start when it grows.
|
||||
.libraries-button {
|
||||
@extend %button-secondary;
|
||||
@include deprecated.uppercase-title-typography;
|
||||
|
||||
gap: deprecated.$s-2;
|
||||
height: deprecated.$s-32;
|
||||
width: 100%;
|
||||
margin-bottom: deprecated.$s-4;
|
||||
border-radius: deprecated.$s-8;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-secondary-background-color-hover);
|
||||
color: var(--button-secondary-foreground-color-hover);
|
||||
border: deprecated.$s-1 solid var(--button-secondary-border-color-hover);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: var(--button-secondary-background-color-focus);
|
||||
color: var(--button-secondary-foreground-color-focus);
|
||||
border: deprecated.$s-1 solid var(--button-secondary-border-color-focus);
|
||||
}
|
||||
}
|
||||
|
||||
.add-library-button {
|
||||
@extend %button-primary;
|
||||
@include deprecated.uppercase-title-typography;
|
||||
|
||||
gap: deprecated.$s-2;
|
||||
height: deprecated.$s-32;
|
||||
width: 100%;
|
||||
margin-bottom: deprecated.$s-4;
|
||||
border-radius: deprecated.$s-8;
|
||||
inline-size: 100%;
|
||||
margin-block-end: var(--sp-xs);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.section-button {
|
||||
@include deprecated.flex-center;
|
||||
@include deprecated.button-style;
|
||||
|
||||
height: deprecated.$s-32;
|
||||
width: deprecated.$s-32;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
block-size: $sz-32;
|
||||
inline-size: $sz-32;
|
||||
margin: 0;
|
||||
border: deprecated.$s-1 solid var(--input-border-color-rest);
|
||||
border-radius: deprecated.$br-8 deprecated.$br-2 deprecated.$br-2 deprecated.$br-8;
|
||||
background-color: var(--input-background-color-rest);
|
||||
border: $b-1 solid var(--color-background-tertiary);
|
||||
border-radius: $br-8 px2rem(2) px2rem(2) $br-8;
|
||||
background-color: var(--color-background-tertiary);
|
||||
|
||||
svg {
|
||||
height: deprecated.$s-16;
|
||||
width: deprecated.$s-16;
|
||||
stroke: var(--icon-foreground);
|
||||
.section-icon {
|
||||
flex-shrink: 0;
|
||||
stroke-width: 1px;
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
// Before :focus and :hover, as the legacy placeholder was: the focus
|
||||
// border wins over the opened state.
|
||||
&.opened {
|
||||
outline: none;
|
||||
border-color: var(--color-background-quaternary);
|
||||
background-color: var(--color-background-quaternary);
|
||||
color: var(--color-accent-primary);
|
||||
|
||||
.section-icon {
|
||||
color: var(--color-accent-primary);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: deprecated.$s-1 solid var(--input-border-color-focus);
|
||||
border: $b-1 solid var(--color-accent-primary);
|
||||
outline: 0;
|
||||
background-color: var(--input-background-color-focus);
|
||||
color: var(--input-foreground-color-focus);
|
||||
background-color: var(--color-background-tertiary);
|
||||
color: var(--color-foreground-primary);
|
||||
|
||||
svg {
|
||||
background-color: var(--input-background-color-focus);
|
||||
.section-icon {
|
||||
background-color: var(--color-background-tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: deprecated.$s-1 solid var(--input-border-color-hover);
|
||||
background-color: var(--input-background-color-hover);
|
||||
border: $b-1 solid var(--color-background-quaternary);
|
||||
background-color: var(--color-background-quaternary);
|
||||
|
||||
svg {
|
||||
background-color: var(--input-background-color-hover);
|
||||
stroke: var(--button-foreground-hover);
|
||||
.section-icon {
|
||||
background-color: var(--color-background-quaternary);
|
||||
color: var(--color-accent-primary);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: deprecated.$s-1 solid var(--input-border-color-focus);
|
||||
border: $b-1 solid var(--color-accent-primary);
|
||||
outline: 0;
|
||||
background-color: var(--input-background-color-focus);
|
||||
color: var(--input-foreground-color-focus);
|
||||
background-color: var(--color-background-tertiary);
|
||||
color: var(--color-foreground-primary);
|
||||
|
||||
svg {
|
||||
background-color: var(--input-background-color-focus);
|
||||
.section-icon {
|
||||
background-color: var(--color-background-tertiary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.opened {
|
||||
@extend %button-icon-selected;
|
||||
}
|
||||
}
|
||||
|
||||
.sections-container {
|
||||
@include deprecated.menu-shadow;
|
||||
@include deprecated.flex-column;
|
||||
|
||||
position: absolute;
|
||||
top: deprecated.$s-84;
|
||||
left: deprecated.$s-12;
|
||||
width: deprecated.$s-192;
|
||||
padding: deprecated.$s-4;
|
||||
border-radius: deprecated.$br-8;
|
||||
background-color: var(--menu-background-color);
|
||||
z-index: deprecated.$z-index-2;
|
||||
}
|
||||
|
||||
.section-item {
|
||||
@include deprecated.body-small-typography;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: deprecated.$s-6;
|
||||
border-radius: deprecated.$br-8;
|
||||
}
|
||||
|
||||
.section-btn {
|
||||
@include deprecated.button-style;
|
||||
}
|
||||
|
||||
.assets-header {
|
||||
padding: 0 0 deprecated.$s-24 deprecated.$s-12;
|
||||
padding: 0 0 var(--sp-xxl) var(--sp-m);
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
display: flex;
|
||||
gap: deprecated.$s-4;
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
@ -14,9 +14,9 @@
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.main.ui.components.title-bar :refer [title-bar*]]
|
||||
[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.icons :as deprecated-icon]
|
||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
@ -159,8 +159,11 @@
|
||||
(if create?
|
||||
(tr "workspace.assets.create-group")
|
||||
(tr "workspace.assets.rename-group"))]
|
||||
[:button {:class (stl/css :modal-close-btn)
|
||||
:on-click modal/hide!} deprecated-icon/close]]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:tooltip-class (stl/css :modal-close-btn)
|
||||
:aria-label (tr "labels.close")
|
||||
:icon i/close
|
||||
:on-click modal/hide!}]]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:& fm/form {:form form :on-submit on-accept}
|
||||
@ -172,16 +175,13 @@
|
||||
|
||||
[:div {:class (stl/css :modal-footer)}
|
||||
[:div {:class (stl/css :action-buttons)}
|
||||
[:input
|
||||
{:class (stl/css :cancel-button)
|
||||
:type "button"
|
||||
:value (tr "labels.cancel")
|
||||
:on-click modal/hide!}]
|
||||
[:> button* {:variant "secondary"
|
||||
:type "button"
|
||||
:on-click modal/hide!}
|
||||
(tr "labels.cancel")]
|
||||
|
||||
[:input
|
||||
{:type "button"
|
||||
:class (stl/css-case :accept-btn true
|
||||
:global/disabled (not (:valid @form)))
|
||||
:disabled (not (:valid @form))
|
||||
:value (if create? (tr "labels.create") (tr "labels.rename"))
|
||||
:on-click on-accept}]]]]]))
|
||||
[:> button* {:variant "primary"
|
||||
:type "button"
|
||||
:disabled (not (:valid @form))
|
||||
:on-click on-accept}
|
||||
(if create? (tr "labels.create") (tr "labels.rename"))]]]]]))
|
||||
|
||||
@ -4,10 +4,14 @@
|
||||
//
|
||||
// Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
// refactor/ stays for the modal placeholders (%modal-*), which move with
|
||||
// the modals refactor, and the input placeholder (%input-with-label).
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
@use "ds/mixins.scss" as *;
|
||||
@use "ds/typography.scss" as *;
|
||||
|
||||
.group-title-wrapper {
|
||||
width: 100%;
|
||||
inline-size: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@ -30,21 +34,21 @@
|
||||
}
|
||||
|
||||
.group-title {
|
||||
padding-left: deprecated.$s-4;
|
||||
padding-inline-start: var(--sp-xs);
|
||||
}
|
||||
|
||||
.pre-path {
|
||||
margin-left: deprecated.$s-2;
|
||||
margin-inline-start: var(--sp-xxs);
|
||||
text-transform: initial;
|
||||
color: var(--title-foreground-color);
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.path {
|
||||
@include deprecated.text-ellipsis;
|
||||
@include text-ellipsis;
|
||||
|
||||
margin-left: deprecated.$s-2;
|
||||
margin-inline-start: var(--sp-xxs);
|
||||
text-transform: initial;
|
||||
color: var(--title-foreground-color-hover);
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.modal-overlay {
|
||||
@ -56,44 +60,35 @@
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
margin-bottom: deprecated.$s-24;
|
||||
margin-block-end: var(--sp-xxl);
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include deprecated.uppercase-title-typography;
|
||||
@include use-typography("headline-small");
|
||||
|
||||
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 deprecated.body-small-typography;
|
||||
@include use-typography("body-small");
|
||||
|
||||
margin-bottom: deprecated.$s-24;
|
||||
margin-block-end: var(--sp-xxl);
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
@extend %input-with-label;
|
||||
@include deprecated.body-small-typography;
|
||||
@include use-typography("body-small");
|
||||
|
||||
margin-bottom: deprecated.$s-8;
|
||||
margin-block-end: var(--sp-s);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
@extend %modal-action-btns;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
@extend %modal-cancel-btn;
|
||||
}
|
||||
|
||||
.accept-btn {
|
||||
@extend %modal-accept-btn;
|
||||
|
||||
&.danger {
|
||||
@extend %modal-danger-btn;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user