mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
♻️ Use DS icons and an icon button in the undo history panel (#12472)
* ♻️ Use DS icons and an icon button in the undo history panel The undo history panel (History > Actions) drew its entry type icons with the deprecated icon set, and the arrow that shows the details of an entry was a div with a click handler: no button role, no keyboard access and no accessible name. - The entry type icons use the DS icon (drop-icon is i/drop in the DS). - The arrow becomes a DS action icon button labeled "Show details", with aria-expanded for its state. It keeps its data attribute, its classes and its click handler, and it also shows on keyboard focus. The DS icon draws 12px inside a 16px box, so the entry icon gets a negative margin, and the button wrapper a negative end margin: icon, arrow and text stay where they were. The arrow turns through its own class instead of an svg rule. Add the workspace.undo.entry.show-details key (en, es) and a visual test that draws a rectangle, opens the actions list and expands the entry. AI-assisted-by: claude-opus-5-5 * ♻️ Move undo history panel styles off the deprecated styles Move history.scss to DS tokens, sizes, borders and logical properties, and drop the refactor import. The entry color aliases become local variables of the entry, set by its disabled and hover states, and the arrow opacity goes through one as well. AI-assisted-by: claude-opus-5-5 * 💄 Place the undo history icon and arrow without negative margins Drop the -2px margin of the entry icon and the -6px end margin of the details button wrapper. Keep text and arrow in place with token spacing instead: the text after the 16px icon box starts with --sp-xs instead of --sp-s, and the entry end padding is --sp-xxs, since the 24px button leaves 6px around its 12px arrow. The icon now sits 2px to the right, centered in its box. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
d6e850e917
commit
6acb360f4a
@ -560,6 +560,42 @@ test.describe("Right header", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Undo history", () => {
|
||||
test("Shows the actions list with an entry expanded", async ({ page }) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(
|
||||
"update-file?id=*",
|
||||
"workspace/update-file-empty.json",
|
||||
);
|
||||
await workspace.mockRPC(
|
||||
"get-file-snapshots?file-id=*",
|
||||
"workspace/versions-snapshot-1.json",
|
||||
);
|
||||
await workspace.goToWorkspace();
|
||||
|
||||
await page.keyboard.press("r");
|
||||
await workspace.clickWithDragViewportAt(128, 128, 200, 100);
|
||||
|
||||
await page.keyboard.press("Control+Alt+h");
|
||||
await page.getByRole("tab", { name: "Actions" }).click();
|
||||
const panel = page.getByRole("tabpanel", { name: "Actions" });
|
||||
const entry = panel.getByText("New rectangle");
|
||||
// The short id and the relative time change on every run.
|
||||
const mask = [page.locator('[class*="history-entry-meta"]')];
|
||||
|
||||
// The expand arrow only shows while the entry is hovered.
|
||||
await entry.hover();
|
||||
await expect(panel).toHaveScreenshot({ mask });
|
||||
|
||||
// The data attribute exists before and after the DS migration.
|
||||
await panel.locator('[data-has-entry="true"]').click();
|
||||
await expect(panel.getByText("Rectangle", { exact: true })).toBeVisible();
|
||||
await entry.hover();
|
||||
await expect(panel).toHaveScreenshot({ mask });
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Palette", () => {
|
||||
test("Shows the bottom palette expanded and collapsed", async ({ page }) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
|
||||
@ -13,9 +13,9 @@
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]]
|
||||
[app.main.ui.ds.product.empty-state :refer [empty-state*]]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr] :as i18n]
|
||||
[cuerdas.core :as str]
|
||||
@ -155,22 +155,23 @@
|
||||
:move (tr "workspace.undo.entry.move" value)
|
||||
(tr "workspace.undo.entry.unknown" value))))
|
||||
|
||||
(defn entry->icon [{:keys [type]}]
|
||||
(defn- entry->icon-id
|
||||
[{:keys [type]}]
|
||||
(case type
|
||||
:page deprecated-icon/document
|
||||
:shape deprecated-icon/svg
|
||||
:rect deprecated-icon/rectangle
|
||||
:circle deprecated-icon/ellipse
|
||||
:text deprecated-icon/text
|
||||
:path deprecated-icon/path
|
||||
:frame deprecated-icon/board
|
||||
:group deprecated-icon/group
|
||||
:color deprecated-icon/drop-icon
|
||||
:typography deprecated-icon/text-palette
|
||||
:component deprecated-icon/component
|
||||
:media deprecated-icon/img
|
||||
:image deprecated-icon/img
|
||||
deprecated-icon/svg))
|
||||
:page i/document
|
||||
:shape i/svg
|
||||
:rect i/rectangle
|
||||
:circle i/ellipse
|
||||
:text i/text
|
||||
:path i/path
|
||||
:frame i/board
|
||||
:group i/group
|
||||
:color i/drop
|
||||
:typography i/text-palette
|
||||
:component i/component
|
||||
:media i/img
|
||||
:image i/img
|
||||
i/svg))
|
||||
|
||||
(defn is-shape? [type]
|
||||
(contains? #{:shape :rect :circle :text :path :frame :group} type))
|
||||
@ -339,7 +340,7 @@
|
||||
|
||||
[:div {:class (stl/css :history-entry-summary)}
|
||||
[:div {:class (stl/css :history-entry-summary-icon)}
|
||||
(entry->icon entry)]
|
||||
[:> icon* {:icon-id (entry->icon-id entry) :size "s"}]]
|
||||
[:div {:class (stl/css :history-entry-summary-text)}
|
||||
[:div {:class (stl/css :history-entry-title)}
|
||||
(entry->message entry)]
|
||||
@ -363,11 +364,17 @@
|
||||
[:span {:class (stl/css :history-entry-author)}
|
||||
(tr "workspace.undo.entry.by" author)])])]
|
||||
(when (:detail entry)
|
||||
[:div {:class (stl/css-case :history-entry-summary-button true
|
||||
:button-opened @show-detail?)
|
||||
:on-click toggle-show-detail
|
||||
:data-has-entry (dm/str (not (nil? (:detail entry))))}
|
||||
deprecated-icon/arrow])]
|
||||
[:> icon-button* {:variant "action"
|
||||
:class (stl/css-case :history-entry-summary-button true
|
||||
:button-opened @show-detail?)
|
||||
:tooltip-class (stl/css :history-entry-summary-button-wrapper)
|
||||
:aria-label (tr "workspace.undo.entry.show-details")
|
||||
:aria-expanded @show-detail?
|
||||
:icon i/arrow
|
||||
:icon-size "s"
|
||||
:icon-class (stl/css :history-entry-summary-arrow)
|
||||
:on-click toggle-show-detail
|
||||
:data-has-entry (dm/str (not (nil? (:detail entry))))}])]
|
||||
|
||||
(when @show-detail?
|
||||
[:> history-entry-details* {:entry entry}])]))
|
||||
|
||||
@ -4,13 +4,14 @@
|
||||
//
|
||||
// Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
@use "ds/_utils.scss" as *;
|
||||
|
||||
.history-toolbox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--panel-background-color);
|
||||
background-color: var(--color-background-primary);
|
||||
}
|
||||
|
||||
.history-entry-empty {
|
||||
@ -22,21 +23,29 @@
|
||||
}
|
||||
|
||||
.history-entries {
|
||||
height: calc(100vh - deprecated.$s-100);
|
||||
padding: deprecated.$s-12;
|
||||
block-size: calc(100vh - #{px2rem(100)});
|
||||
padding: var(--sp-m);
|
||||
overflow: hidden auto;
|
||||
font-size: deprecated.$fs-12;
|
||||
font-size: px2rem(12);
|
||||
}
|
||||
|
||||
.history-entry {
|
||||
--entry-background-color: var(--color-background-tertiary);
|
||||
--entry-foreground-color: var(--color-foreground-secondary);
|
||||
--entry-border-color: transparent;
|
||||
--entry-arrow-opacity: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
min-height: deprecated.$s-32;
|
||||
margin: deprecated.$s-4;
|
||||
padding: deprecated.$s-4 deprecated.$s-8;
|
||||
border: deprecated.$s-2 solid transparent;
|
||||
border-radius: deprecated.$s-8;
|
||||
min-block-size: $sz-32;
|
||||
margin: var(--sp-xs);
|
||||
|
||||
// The end padding is small because the details button leaves 6px
|
||||
// around its 12px arrow.
|
||||
padding: var(--sp-xs) var(--sp-xxs) var(--sp-xs) var(--sp-s);
|
||||
border: $b-2 solid var(--entry-border-color);
|
||||
border-radius: $br-8;
|
||||
background-color: var(--entry-background-color);
|
||||
cursor: pointer;
|
||||
transition: border 0.2s;
|
||||
@ -46,18 +55,18 @@
|
||||
align-items: center;
|
||||
|
||||
.history-entry-summary-icon {
|
||||
svg {
|
||||
@extend %button-icon-small;
|
||||
|
||||
stroke: var(--entry-foreground-color);
|
||||
}
|
||||
display: flex;
|
||||
color: var(--entry-foreground-color);
|
||||
}
|
||||
|
||||
.history-entry-summary-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: deprecated.$s-2;
|
||||
margin: 0 deprecated.$s-8;
|
||||
gap: var(--sp-xxs);
|
||||
|
||||
// The DS icon draws 12px inside a 16px box: a smaller start margin
|
||||
// keeps the text where it was.
|
||||
margin-inline: var(--sp-xs) var(--sp-s);
|
||||
color: var(--color-foreground-primary);
|
||||
min-inline-size: 0;
|
||||
}
|
||||
@ -73,7 +82,7 @@
|
||||
// template ("hash · time by Name") survive rendering. Issue #7660.
|
||||
.history-entry-meta {
|
||||
color: var(--color-foreground-secondary);
|
||||
font-size: deprecated.$fs-10;
|
||||
font-size: px2rem(10);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@ -99,28 +108,28 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.history-entry-summary-button {
|
||||
opacity: deprecated.$op-0;
|
||||
margin-left: auto;
|
||||
// Tooltip wrapper of the details button, pushed to the end of the row.
|
||||
.history-entry-summary-button-wrapper {
|
||||
margin-inline-start: auto;
|
||||
}
|
||||
|
||||
&.button-opened {
|
||||
svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.history-entry-summary-button {
|
||||
opacity: var(--entry-arrow-opacity);
|
||||
|
||||
&:focus-visible {
|
||||
--entry-arrow-opacity: 1;
|
||||
}
|
||||
|
||||
svg {
|
||||
@extend %button-icon-small;
|
||||
|
||||
stroke: var(--entry-foreground-color);
|
||||
&.button-opened .history-entry-summary-arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.history-entry-detail {
|
||||
display: block;
|
||||
padding-top: deprecated.$s-16;
|
||||
color: var(--modal-text-foreground-color);
|
||||
padding-block-start: var(--sp-l);
|
||||
color: var(--color-foreground-secondary);
|
||||
|
||||
.history-entry-details-list {
|
||||
margin: 0;
|
||||
@ -128,31 +137,16 @@
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
border-color: var(--entry-border-color-disabled);
|
||||
background-color: var(--entry-background-color-disabled);
|
||||
--entry-background-color: var(--color-background-primary);
|
||||
--entry-border-color: var(--color-background-quaternary);
|
||||
}
|
||||
|
||||
&.hover,
|
||||
&:hover {
|
||||
background-color: var(--entry-background-color-hover);
|
||||
color: var(--entry-foreground-color-hover);
|
||||
--entry-background-color: var(--color-background-quaternary);
|
||||
--entry-foreground-color: var(--color-foreground-primary);
|
||||
--entry-arrow-opacity: 1;
|
||||
|
||||
.history-entry-summary {
|
||||
.history-entry-summary-icon {
|
||||
svg {
|
||||
stroke: var(--entry-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.history-entry-summary-button {
|
||||
opacity: deprecated.$op-10;
|
||||
|
||||
&.button-opened {
|
||||
svg {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9407,6 +9407,10 @@ msgstr "There are no history changes so far"
|
||||
msgid "workspace.undo.entry.by"
|
||||
msgstr "by %s"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/history.cljs
|
||||
msgid "workspace.undo.entry.show-details"
|
||||
msgstr "Show details"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/history.cljs:154
|
||||
msgid "workspace.undo.entry.delete"
|
||||
msgstr "Deleted %s"
|
||||
|
||||
@ -9513,6 +9513,10 @@ msgstr "Todavía no hay cambios en el histórico"
|
||||
msgid "workspace.undo.entry.by"
|
||||
msgstr "por %s"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/history.cljs
|
||||
msgid "workspace.undo.entry.show-details"
|
||||
msgstr "Mostrar detalles"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/history.cljs:154
|
||||
msgid "workspace.undo.entry.delete"
|
||||
msgstr "%s eliminado"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user