♻️ Copy shorthands using user selected color space (#7752)

* ♻️ Copy shorthands using user selected color space

* ♻️ Add tests to ensure color space changes affect all properties
This commit is contained in:
Xaviju 2025-11-18 10:54:10 +01:00 committed by GitHub
parent 04185b3544
commit 64b892f82d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 88 additions and 32 deletions

View File

@ -120,6 +120,16 @@ const openInspectTab = async (workspacePage) => {
await inspectButton.click();
};
const selectColorSpace = async (workspacePage, colorSpace) => {
const sidebar = workspacePage.page.getByTestId("right-sidebar");
const colorSpaceSelector = sidebar.getByLabel("Select color space");
await colorSpaceSelector.click();
const colorSpaceOption = sidebar.getByRole("option", {
name: colorSpace,
});
await colorSpaceOption.click();
};
test.describe("Inspect tab - Styles", () => {
test("Open Inspect tab", async ({ page }) => {
const workspacePage = new WorkspacePage(page);
@ -383,6 +393,46 @@ test.describe("Inspect tab - Styles", () => {
expect(propertyRowCount).toBeGreaterThanOrEqual(1);
});
test("Change color space and ensure fill and shorthand changes", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await setupFile(workspacePage);
await selectLayer(workspacePage, shapeToLayerName.fill.solid);
await openInspectTab(workspacePage);
const panel = await getPanelByTitle(workspacePage, "Fill");
await expect(panel).toBeVisible();
const propertyRow = panel.getByTestId("property-row");
const backgroundRow = propertyRow.filter({
hasText: "Background",
});
await expect(backgroundRow).toBeVisible();
// Ensure initial value and copied value are in HEX format
expect(backgroundRow).toContainText("#0438d5 100%");
await copyPropertyFromPropertyRow(panel, "Background");
const backgroundHEX = await page.evaluate(() =>
navigator.clipboard.readText(),
);
expect(backgroundHEX).toContain("background: #0438d5FF;");
// Change color space to RGBA
await selectColorSpace(workspacePage, "rgba");
// Ensure new value and copied value are in RGBA format
expect(backgroundRow).toContainText("4, 56, 213, 1");
await copyPropertyFromPropertyRow(panel, "Background");
const backgroundRGBA = await page.evaluate(() =>
navigator.clipboard.readText(),
);
expect(backgroundRGBA).toContain("background: rgba(4, 56, 213, 1);");
});
test("Shape - Fill - Gradient", async ({ page }) => {
const workspacePage = new WorkspacePage(page);
await setupFile(workspacePage);

View File

@ -122,7 +122,8 @@
(fn []
(if (seq shapes)
(st/emit! (ptk/event ::ev/event {::ev/name "inspect-mode-click-element"}))
(handle-change-tab (if (contains? cf/flags :inspect-styles) :styles :info)))))
(handle-change-tab (if (contains? cf/flags :inspect-styles) :styles :info)))
(reset! color-space* "hex")))
[:aside {:class (stl/css-case :settings-bar-right true
:viewer-code (= from :viewer))}
@ -166,12 +167,14 @@
[:div {:class (stl/css :inspect-tab-switcher-controls)}
[:div {:class (stl/css :inspect-tab-switcher-controls-color-space)}
[:> select* {:class (stl/css :inspect-tab-switcher-controls-color-space-select)
:aria-label (tr "inspect.tabs.switcher.color-space.label")
:options color-spaces
:default-selected "hex"
:variant "ghost"
:on-change handle-change-color-space}]]
[:div {:class (stl/css :inspect-tab-switcher-controls-tab)}
[:> select* {:options tabs
:aria-label (tr "inspect.tabs.switcher.inspect-tab.label")
:default-selected (name @section)
:on-change handle-change-tab}]]]]
nil)

View File

@ -7,7 +7,6 @@
(ns app.main.ui.inspect.styles.panels.fill
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.common.types.fills :as types.fills]
[app.config :as cfg]
[app.main.ui.inspect.attributes.common :as cmm]
@ -36,20 +35,14 @@
(= 0 idx)))
(defn- generate-fill-shorthand
[shape]
[shape color-space]
(reduce
(fn [acc fill]
(let [color-type (types.fills/fill->color fill)
color-value (:color color-type)
color-gradient (:gradient color-type)
gradient-data {:type color-type
:stops (:stops color-gradient)}
color-image (:image color-type)
prefix (if color-value "background-color: " "background-image: ")
(let [color (types.fills/fill->color fill)
prefix (if (:color color) "background-color: " "background-image: ")
value (cond
(:color color-type) (dm/str color-value)
color-gradient (uc/gradient->css gradient-data)
color-image (str "url(\"" (cfg/resolve-file-media color-image) "\")")
(or (:color color) (:gradient color)) (uc/color->format->background color (keyword color-space))
(:image color) (str "url('" (cfg/resolve-file-media (:image color)) "')")
:else "")
full-value (str prefix value ";")]
(if (empty? acc)
@ -60,12 +53,12 @@
(mf/defc fill-panel*
[{:keys [shapes resolved-tokens color-space on-fill-shorthand]}]
(let [shorthand* (mf/use-state #(generate-fill-shorthand (first shapes)))
(let [shorthand* (mf/use-state #(generate-fill-shorthand (first shapes) color-space))
shorthand (deref shorthand*)]
(mf/use-effect
(mf/deps shorthand on-fill-shorthand shapes)
(fn []
(reset! shorthand* (generate-fill-shorthand (first shapes)))
(reset! shorthand* (generate-fill-shorthand (first shapes) color-space))
(on-fill-shorthand {:panel :fill
:property shorthand})))
[:div {:class (stl/css :fill-panel)}

View File

@ -53,7 +53,7 @@
(is-first-element? idx)))
(defn- generate-stroke-shorthand
[shapes]
[shapes color-space]
(when (= (count shapes) 1)
(let [shape (first shapes)]
(reduce
@ -62,12 +62,13 @@
stroke-width (:stroke-width stroke)
stroke-style (:stroke-style stroke)
color-value (:color stroke-type)
formatted-color-value (uc/color->format->background stroke-type (keyword color-space))
color-gradient (:gradient stroke-type)
gradient-data {:type (get-in stroke-type [:gradient :type])
:stops (get-in stroke-type [:gradient :stops])}
color-image (:image stroke-type)
value (cond
color-value (dm/str "border: " stroke-width "px " (d/name stroke-style) " " color-value ";")
color-value (dm/str "border: " stroke-width "px " (d/name stroke-style) " " formatted-color-value ";")
color-gradient (dm/str "border-image: " (uc/gradient->css gradient-data) " 100 / " stroke-width "px;")
color-image (dm/str "border-image: url(" (cfg/resolve-file-media color-image) ") 100 / " stroke-width "px;")
:else "")]
@ -79,12 +80,12 @@
(mf/defc stroke-panel*
[{:keys [shapes objects resolved-tokens color-space on-stroke-shorthand]}]
(let [shorthand* (mf/use-state #(generate-stroke-shorthand shapes))
(let [shorthand* (mf/use-state #(generate-stroke-shorthand shapes color-space))
shorthand (deref shorthand*)]
(mf/use-effect
(mf/deps shorthand on-stroke-shorthand shapes)
(fn []
(reset! shorthand* (generate-stroke-shorthand shapes))
(reset! shorthand* (generate-stroke-shorthand shapes color-space))
(on-stroke-shorthand {:panel :stroke
:property shorthand})))
[:div {:class (stl/css :stroke-panel)}

View File

@ -6,6 +6,7 @@
(ns app.main.ui.inspect.styles.rows.color-properties-row
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.color :as cc]
@ -51,15 +52,16 @@
formatted-color-value (mf/use-memo
(mf/deps color format color-opacity)
#(cond
(some? (:color color)) (case format
"hex" (dm/str color-value " " color-opacity)
"rgba" (let [[r g b a] (cc/hex->rgba color-value color-opacity)
result (cc/format-rgba [r g b a])]
result)
"hsla" (let [[h s l a] (cc/hex->hsla color-value color-opacity)
result (cc/format-hsla [h s l a])]
result)
color-value)
(some? (:color color))
(case format
"hex" (dm/str color-value " " color-opacity)
"rgba" (let [[r g b a] (cc/hex->rgba color-value color-opacity)
result (cc/format-rgba [r g b a])]
result)
"hsla" (let [[h s l a] (cc/hex->hsla color-value color-opacity)
result (cc/format-hsla [h s l a])]
result)
color-value)
(some? (:gradient color)) (uc/gradient-type->string (:type color-gradient))
(some? (:image color)) (tr "media.image")
:else "none"))
@ -71,13 +73,11 @@
(str/replace #"^-" ""))
copiable-value (mf/use-memo
(mf/deps color formatted-color-value color-opacity color-image-url token)
(mf/deps color token format color-opacity)
#(if (some? token)
(:name token)
(cond
(:color color) (if (= format "hex")
(dm/str css-term ": " color-value "; opacity: " color-opacity ";")
(dm/str css-term ": " formatted-color-value ";"))
(:color color) (dm/str css-term ": " (uc/color->format->background color (keyword format)) ";")
(:gradient color) (dm/str css-term ": " (uc/color->background color) ";")
(:image color) (dm/str css-term ": url(" color-image-url ") no-repeat center center / cover;")
:else "none")))

View File

@ -64,6 +64,7 @@
[:span {:class (stl/css :panel-title)} title]
(when shorthand
[:> icon-button* {:variant "ghost"
:tooltip-placement "top-left"
:aria-label (tr "inspect.tabs.styles.panel.copy-style-shorthand")
:on-click copy-shorthand
:icon i/clipboard}])]

View File

@ -1999,6 +1999,14 @@ msgstr "Resolved value:"
msgid "inspect.tabs.switcher.label"
msgstr "Layer info"
#: src/app/main/ui/inspect/right_sidebar.cljs:165
msgid "inspect.tabs.switcher.color-space.label"
msgstr "Select color space"
#: src/app/main/ui/inspect/right_sidebar.cljs:165
msgid "inspect.tabs.switcher.inspect-tab.label"
msgstr "Select inspect tab"
#: src/app/main/ui/dashboard/comments.cljs:96
msgid "label.mark-all-as-read"
msgstr "Mark all as read"