Skip to content

Commit 9ae93ac

Browse files
authored
fix: Fix/color swatch in dropdown (#5675)
## Description 1. What is this PR about (link the issue and add a short description) ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 0000) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
1 parent e7860b6 commit 9ae93ac

3 files changed

Lines changed: 128 additions & 17 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { describe, test, expect } from "vitest";
2+
import { __testing__ } from "./css-value-input";
3+
const { getItemColor } = __testing__;
4+
5+
describe("getItemColor", () => {
6+
test("returns undefined for non-color keyword", () => {
7+
expect(getItemColor({ type: "keyword", value: "auto" })).toBeUndefined();
8+
expect(getItemColor({ type: "keyword", value: "flex" })).toBeUndefined();
9+
});
10+
11+
test("returns the keyword value for a named color keyword", () => {
12+
expect(getItemColor({ type: "keyword", value: "red" })).toBe("red");
13+
expect(getItemColor({ type: "keyword", value: "transparent" })).toBe(
14+
"transparent"
15+
);
16+
});
17+
18+
test("returns undefined for non-color var fallback types", () => {
19+
expect(
20+
getItemColor({
21+
type: "var",
22+
value: "spacing",
23+
fallback: { type: "unit", value: 8, unit: "px" },
24+
})
25+
).toBeUndefined();
26+
expect(
27+
getItemColor({
28+
type: "var",
29+
value: "display",
30+
fallback: { type: "keyword", value: "flex" },
31+
})
32+
).toBeUndefined();
33+
});
34+
35+
test("returns color string for rgb var fallback", () => {
36+
expect(
37+
getItemColor({
38+
type: "var",
39+
value: "brand",
40+
fallback: { type: "rgb", r: 255, g: 0, b: 0, alpha: 1 },
41+
})
42+
).toBe("rgb(255 0 0 / 1)");
43+
});
44+
45+
test("returns color string for color var fallback", () => {
46+
const result = getItemColor({
47+
type: "var",
48+
value: "brand",
49+
fallback: {
50+
type: "color",
51+
colorSpace: "srgb",
52+
components: [1, 0, 0],
53+
alpha: 1,
54+
},
55+
});
56+
expect(result).toMatch(/rgb/);
57+
});
58+
59+
test("returns value for unparsed var fallback that is a valid color", () => {
60+
expect(
61+
getItemColor({
62+
type: "var",
63+
value: "brand",
64+
fallback: { type: "unparsed", value: "red" },
65+
})
66+
).toBe("red");
67+
});
68+
69+
test("returns undefined for unparsed var fallback that is not a color", () => {
70+
expect(
71+
getItemColor({
72+
type: "var",
73+
value: "spacing",
74+
fallback: { type: "unparsed", value: "1rem" },
75+
})
76+
).toBeUndefined();
77+
});
78+
79+
test("returns value for keyword var fallback that is a named color", () => {
80+
expect(
81+
getItemColor({
82+
type: "var",
83+
value: "brand",
84+
fallback: { type: "keyword", value: "red" },
85+
})
86+
).toBe("red");
87+
});
88+
89+
test("returns undefined for var without fallback", () => {
90+
expect(getItemColor({ type: "var", value: "brand" })).toBeUndefined();
91+
});
92+
93+
test("returns undefined for unit item", () => {
94+
expect(
95+
getItemColor({ type: "unit", value: 16, unit: "px" })
96+
).toBeUndefined();
97+
});
98+
});

apps/builder/app/builder/features/style-panel/shared/css-value-input/css-value-input.tsx

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
theme,
1717
Flex,
1818
styled,
19-
Text,
2019
ColorThumb,
2120
} from "@webstudio-is/design-system";
2221
import type {
@@ -45,6 +44,7 @@ import {
4544
camelCaseProperty,
4645
declarationDescriptions,
4746
isValidDeclaration,
47+
parseColor,
4848
} from "@webstudio-is/css-data";
4949
import { $selectedInstanceSizes } from "~/shared/nano-states";
5050
import { convertUnits } from "./convert-units";
@@ -363,6 +363,20 @@ const itemToString = (item: CssValueInputValue | null) => {
363363

364364
const Description = styled(Box, { width: theme.spacing[27] });
365365

366+
// Returns the CSS color string to show as a color swatch for a dropdown item,
367+
// or undefined if the item has no meaningful color preview.
368+
const getItemColor = (item: CssValueInputValue): string | undefined => {
369+
let colorString: string | undefined;
370+
if (item.type === "var" && item.fallback !== undefined) {
371+
colorString = toValue(item.fallback);
372+
} else if (item.type === "keyword") {
373+
colorString = item.value;
374+
}
375+
if (colorString !== undefined && parseColor(colorString) !== undefined) {
376+
return colorString;
377+
}
378+
};
379+
366380
/**
367381
* Common:
368382
* - Free text editing
@@ -943,22 +957,19 @@ export const CssValueInput = ({
943957
{...getItemProps({ item, index })}
944958
key={index}
945959
>
946-
{item.type === "var" ? (
947-
<Flex justify="between" align="center" grow gap={2}>
948-
<Box>--{item.value}</Box>
949-
{item.fallback?.type === "unit" && (
950-
<Text variant="small" color="subtle">
951-
{toValue(item.fallback)}
952-
</Text>
953-
)}
954-
{(item.fallback?.type === "rgb" ||
955-
item.fallback?.type === "color") && (
956-
<ColorThumb color={toValue(item.fallback)} />
957-
)}
958-
</Flex>
959-
) : (
960-
itemToString(item)
961-
)}
960+
{(() => {
961+
const label = itemToString(item);
962+
const colorValue = getItemColor(item);
963+
if (colorValue === undefined) {
964+
return label;
965+
}
966+
return (
967+
<Flex justify="between" align="center" grow gap={2}>
968+
<Box>{label}</Box>
969+
<ColorThumb color={colorValue} />
970+
</Flex>
971+
);
972+
})()}
962973
</ComboboxListboxItem>
963974
))}
964975
</ComboboxScrollArea>
@@ -974,3 +985,5 @@ export const CssValueInput = ({
974985
</ComboboxRoot>
975986
);
976987
};
988+
989+
export const __testing__ = { getItemColor };

apps/builder/app/builder/features/style-panel/shared/css-value-input/parse-intermediate-or-invalid-value.ts.test.ts renamed to apps/builder/app/builder/features/style-panel/shared/css-value-input/parse-intermediate-or-invalid-value.test.ts

File renamed without changes.

0 commit comments

Comments
 (0)