mirror of
https://github.com/penpot/penpot.git
synced 2026-11-01 07:20:32 +00:00
♻️ Use the DS select for the gradient type in the colorpicker (#12653)
* ♻️ Use the DS select for the gradient type in the colorpicker The Linear / Radial select of the gradient options used the legacy select. It now uses select* with string ids, converted back to the gradient type keyword in the handler. Visible changes: the DS select look (label 1px higher, DS list with taller rows and the DS check mark, opening 2px lower). The list still opens inside the colorpicker, so it needs no portal. The "Create a RADIAL gradient" e2e test picks the type by role instead of a DOM position that only matched the legacy markup. New ds visual test "Gradient type select": closed, hover, open and changed. AI-assisted-by: claude-opus-5-5 * ♻️ Move colorpicker gradient styles off the deprecated styles gradients.scss now uses DS spacing, sizes, borders, typography and logical properties, and resolves the --menu-shadow-color alias. The deprecated import stays only for %input-element: the stop offset input moves to the DS with the numeric inputs group. The offset input uses body-small (line-height 1.3 instead of 1.4); the input keeps its height, so nothing moves. 0px difference against the previous commit, including a new shot of the whole gradient panel. gradients.cljs uses an icon-text class that has no rule in this file. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
f8d97ba2ce
commit
66f1f689f8
@ -144,15 +144,14 @@ test("Create a RADIAL gradient", async ({ page }) => {
|
||||
const gradOption = workspacePage.page.getByText("Gradient");
|
||||
await gradOption.click();
|
||||
|
||||
const gradTypeOptions = workspacePage.colorpicker
|
||||
.locator("div")
|
||||
.filter({ hasText: "Linear" })
|
||||
.nth(3);
|
||||
await gradTypeOptions.click();
|
||||
const gradTypeSelect = workspacePage.colorpicker
|
||||
.getByRole("combobox")
|
||||
.filter({ hasText: "Linear" });
|
||||
await gradTypeSelect.click();
|
||||
|
||||
const gradRadialOption = workspacePage.page
|
||||
.locator("li")
|
||||
.filter({ hasText: "Radial" });
|
||||
const gradRadialOption = workspacePage.page.getByRole("option", {
|
||||
name: "Radial",
|
||||
});
|
||||
await gradRadialOption.click();
|
||||
|
||||
const addStopBtn = workspacePage.page.getByRole("button", {
|
||||
|
||||
@ -1249,3 +1249,58 @@ test.describe("Comments panel filter", () => {
|
||||
await expect(menu).toHaveScreenshot(shot);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Gradient type select", () => {
|
||||
test("Shows the gradient type select closed, open and changed", async ({
|
||||
page,
|
||||
}) => {
|
||||
const shot = { maxDiffPixelRatio: 0, threshold: 0 };
|
||||
const workspace = new WorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "workspace/get-file-not-empty.json");
|
||||
await workspace.goToWorkspace({
|
||||
fileId: "6191cd35-bb1f-81f7-8004-7cc63d087374",
|
||||
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
|
||||
});
|
||||
await workspace.clickLeafLayer("Rectangle");
|
||||
await page.getByRole("button", { name: "#B1B2B5" }).click();
|
||||
await page.getByText("Solid").click();
|
||||
await page.getByText("Gradient").click();
|
||||
|
||||
const typeSelect = workspace.colorpicker
|
||||
.getByRole("combobox")
|
||||
.filter({ hasText: "Linear" });
|
||||
await expect(typeSelect).toBeVisible();
|
||||
const pickerBox = await workspace.colorpicker.boundingBox();
|
||||
const selectBox = await typeSelect.boundingBox();
|
||||
// The gradient bar above the select and room below it for the open list.
|
||||
const clip = {
|
||||
x: Math.floor(pickerBox.x),
|
||||
y: Math.floor(selectBox.y) - 60,
|
||||
width: Math.ceil(pickerBox.width),
|
||||
height: 200,
|
||||
};
|
||||
await page.mouse.move(0, 0);
|
||||
await expect(page).toHaveScreenshot({ clip, ...shot });
|
||||
|
||||
await typeSelect.hover();
|
||||
await expect(page).toHaveScreenshot({ clip, ...shot });
|
||||
|
||||
await typeSelect.click();
|
||||
const radial = page.getByRole("option", { name: "Radial" });
|
||||
await expect(radial).toBeVisible();
|
||||
await page.mouse.move(0, 0);
|
||||
await expect(page).toHaveScreenshot({ clip, ...shot });
|
||||
|
||||
await radial.click();
|
||||
await expect(
|
||||
workspace.colorpicker.getByRole("combobox").filter({ hasText: "Radial" }),
|
||||
).toBeVisible();
|
||||
await page.mouse.move(0, 0);
|
||||
await expect(page).toHaveScreenshot({ clip, ...shot });
|
||||
|
||||
// The whole gradient panel: preview bar, stops and the stop list.
|
||||
const panel = workspace.colorpicker.locator("[class*='gradient-panel']");
|
||||
await expect(panel).toHaveScreenshot(shot);
|
||||
});
|
||||
});
|
||||
|
||||
@ -15,8 +15,8 @@
|
||||
[app.main.features :as features]
|
||||
[app.main.ui.components.numeric-input :as deprecated-input]
|
||||
[app.main.ui.components.reorder-handler :refer [reorder-handler*]]
|
||||
[app.main.ui.components.select :refer [select]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.controls.select :refer [select*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.ui.hooks :as h]
|
||||
@ -167,6 +167,10 @@
|
||||
:on-focus handle-focus-stop-color
|
||||
:on-blur handle-blur-stop-color}]]))
|
||||
|
||||
(def ^:private gradient-type-options
|
||||
[{:id "linear-gradient" :label "Linear"}
|
||||
{:id "radial-gradient" :label "Radial"}])
|
||||
|
||||
(mf/defc gradients*
|
||||
[{:keys [type
|
||||
stops
|
||||
@ -200,9 +204,9 @@
|
||||
handle-change-type
|
||||
(mf/use-callback
|
||||
(mf/deps on-change-type)
|
||||
(fn [event]
|
||||
(fn [type-id]
|
||||
(when on-change-type
|
||||
(on-change-type event))))
|
||||
(on-change-type (keyword type-id)))))
|
||||
|
||||
handle-add-stop
|
||||
(mf/use-callback
|
||||
@ -336,12 +340,10 @@
|
||||
:style {"--position" (dm/str (* offset 100) "%")}}]])]]
|
||||
|
||||
[:div {:class (stl/css :gradient-options)}
|
||||
[:& select
|
||||
{:default-value type
|
||||
:options [{:value :linear-gradient :label "Linear"}
|
||||
{:value :radial-gradient :label "Radial"}]
|
||||
:on-change handle-change-type
|
||||
:class (stl/css :gradient-options-select)}]
|
||||
[:> select* {:default-selected (d/name (d/nilv type :linear-gradient))
|
||||
:options gradient-type-options
|
||||
:on-change handle-change-type
|
||||
:wrapper-class (stl/css :gradient-options-select)}]
|
||||
|
||||
[:div {:class (stl/css :gradient-options-buttons)}
|
||||
[:> icon-button* {:variant "ghost"
|
||||
|
||||
@ -4,18 +4,24 @@
|
||||
//
|
||||
// Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
// Only for %input-element: the stop offset input moves to the DS with the
|
||||
// numeric inputs group.
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/_utils.scss" as *;
|
||||
@use "ds/typography.scss" as *;
|
||||
|
||||
.gradient-panel {
|
||||
margin-top: deprecated.$s-12;
|
||||
margin-block-start: var(--sp-m);
|
||||
display: grid;
|
||||
gap: deprecated.$s-4;
|
||||
grid-template-rows: deprecated.$s-56 deprecated.$s-32 1fr;
|
||||
gap: var(--sp-xs);
|
||||
grid-template-rows: px2rem(56) $sz-32 1fr;
|
||||
}
|
||||
|
||||
.gradient-preview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
inline-size: 100%;
|
||||
block-size: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
@ -23,19 +29,19 @@
|
||||
}
|
||||
|
||||
.gradient-background {
|
||||
width: 100%;
|
||||
height: deprecated.$s-20;
|
||||
border-radius: deprecated.$br-4;
|
||||
inline-size: 100%;
|
||||
block-size: px2rem(20);
|
||||
border-radius: $br-4;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gradient-preview-stop-wrapper {
|
||||
position: absolute;
|
||||
width: calc(100% - deprecated.$s-24 - deprecated.$s-4);
|
||||
height: 100%;
|
||||
left: deprecated.$s-2;
|
||||
top: calc(-1 * deprecated.$s-4);
|
||||
inline-size: calc(100% - #{px2rem(28)});
|
||||
block-size: 100%;
|
||||
inset-inline-start: px2rem(2);
|
||||
inset-block-start: calc(-1 * var(--sp-xs));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -43,47 +49,47 @@
|
||||
background-color: var(--color-foreground-primary);
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=");
|
||||
background-position: left center;
|
||||
background-size: deprecated.$s-8;
|
||||
border-radius: deprecated.$br-6;
|
||||
border: deprecated.$s-2 solid var(--color-foreground-primary);
|
||||
box-shadow: 0 0 deprecated.$s-4 0 var(--menu-shadow-color);
|
||||
height: calc(deprecated.$s-24 - deprecated.$s-2);
|
||||
left: var(--position);
|
||||
background-size: px2rem(8);
|
||||
border-radius: $br-6;
|
||||
border: $b-2 solid var(--color-foreground-primary);
|
||||
box-shadow: 0 0 px2rem(4) 0 var(--color-shadow-dark);
|
||||
block-size: px2rem(22);
|
||||
inset-inline-start: var(--position);
|
||||
overflow: hidden;
|
||||
pointer-events: initial;
|
||||
position: absolute;
|
||||
width: calc(deprecated.$s-24 - deprecated.$s-2);
|
||||
inline-size: px2rem(22);
|
||||
|
||||
&.is-selected,
|
||||
&:hover {
|
||||
outline: deprecated.$s-2 solid var(--color-accent-primary);
|
||||
outline: $b-2 solid var(--color-accent-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.gradient-preview-stop-decoration {
|
||||
background: var(--color-foreground-primary);
|
||||
border-radius: 100%;
|
||||
bottom: deprecated.$s-32;
|
||||
box-shadow: 0 0 deprecated.$s-4 0 var(--menu-shadow-color);
|
||||
height: deprecated.$s-4;
|
||||
left: calc(var(--position) + deprecated.$s-8);
|
||||
inset-block-end: var(--sp-xxxl);
|
||||
box-shadow: 0 0 px2rem(4) 0 var(--color-shadow-dark);
|
||||
block-size: px2rem(4);
|
||||
inset-inline-start: calc(var(--position) + var(--sp-s));
|
||||
position: absolute;
|
||||
width: deprecated.$s-4;
|
||||
inline-size: px2rem(4);
|
||||
}
|
||||
|
||||
.gradient-preview-stop-color {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
inset-inline-start: 0;
|
||||
inline-size: 50%;
|
||||
block-size: 100%;
|
||||
background: var(--color-solid);
|
||||
}
|
||||
|
||||
.gradient-preview-stop-alpha {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
inset-inline-start: 50%;
|
||||
inline-size: 50%;
|
||||
block-size: 100%;
|
||||
background: var(--color-alpha);
|
||||
}
|
||||
|
||||
@ -98,7 +104,7 @@
|
||||
}
|
||||
|
||||
.gradient-options-select {
|
||||
width: deprecated.$s-140;
|
||||
inline-size: px2rem(140);
|
||||
}
|
||||
|
||||
.rotate-icon {
|
||||
@ -108,18 +114,18 @@
|
||||
.gradient-stops-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: deprecated.$s-4;
|
||||
max-height: deprecated.$s-180;
|
||||
gap: var(--sp-xs);
|
||||
max-block-size: px2rem(180);
|
||||
overflow: hidden auto;
|
||||
padding: 0 0 var(--sp-s) var(--sp-m);
|
||||
}
|
||||
|
||||
.gradient-stops-entry {
|
||||
display: flex;
|
||||
gap: deprecated.$s-4;
|
||||
padding: deprecated.$s-2;
|
||||
border-radius: deprecated.$br-12;
|
||||
border: deprecated.$s-1 solid transparent;
|
||||
gap: var(--sp-xs);
|
||||
padding: var(--sp-xxs);
|
||||
border-radius: $br-12;
|
||||
border: $b-1 solid transparent;
|
||||
position: relative;
|
||||
|
||||
&.is-selected {
|
||||
@ -141,26 +147,26 @@
|
||||
|
||||
.offset-input-wrapper {
|
||||
@extend %input-element;
|
||||
@include deprecated.body-small-typography;
|
||||
@include use-typography("body-small");
|
||||
|
||||
width: deprecated.$s-92;
|
||||
inline-size: px2rem(92);
|
||||
}
|
||||
|
||||
.gradient-separator {
|
||||
border-color: var(--color-background-quaternary);
|
||||
border-width: deprecated.$s-3;
|
||||
margin-left: calc(var(--sp-m) / 2);
|
||||
border-width: px2rem(3);
|
||||
margin-inline-start: calc(var(--sp-m) / 2);
|
||||
position: relative;
|
||||
width: calc(100% - var(--sp-m));
|
||||
inline-size: calc(100% - var(--sp-m));
|
||||
}
|
||||
|
||||
.gradient-preview-stop-preview {
|
||||
background: var(--color-foreground-primary);
|
||||
border-radius: 50%;
|
||||
height: deprecated.$s-4;
|
||||
left: calc(var(--preview-position, 0%) - deprecated.$s-2);
|
||||
block-size: px2rem(4);
|
||||
inset-inline-start: calc(var(--preview-position, 0%) - #{px2rem(2)});
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: calc(50% - deprecated.$s-2);
|
||||
width: deprecated.$s-4;
|
||||
inset-block-start: calc(50% - #{px2rem(2)});
|
||||
inline-size: px2rem(4);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user