Skip to content

Commit e3eaf5b

Browse files
authored
refactor: replace keywords with change-case (#5011)
We don't need custom utilities for keywords with super small and well supported change-case. Got rid of it of keywords utilities and its tests ;) Also fixed shadows and backdrop-filter sections I broke while migrating to hyphenated properties. Touched animations a little.
1 parent 9e77277 commit e3eaf5b

File tree

14 files changed

+56
-153
lines changed

14 files changed

+56
-153
lines changed

apps/builder/app/builder/features/settings-panel/props-section/animation/animation-panel-content.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useState } from "react";
12
import {
23
Box,
34
Flex,
@@ -8,7 +9,6 @@ import {
89
toast,
910
} from "@webstudio-is/design-system";
1011
import { keywordValues } from "@webstudio-is/css-data";
11-
import { toPascalCase } from "~/builder/features/style-panel/shared/keyword-utils";
1212
import { useIds } from "~/shared/form-utils";
1313

1414
import type {
@@ -31,9 +31,8 @@ import {
3131
toValue,
3232
type StyleValue,
3333
} from "@webstudio-is/css-engine";
34-
import { useState } from "react";
3534
import { Keyframes } from "./animation-keyframes";
36-
import { titleCase } from "title-case";
35+
import { humanizeString } from "~/shared/string-utils";
3736

3837
type Props = {
3938
type: "scroll" | "view";
@@ -274,7 +273,7 @@ export const AnimationPanelContent = ({ onChange, value, type }: Props) => {
274273
<Select
275274
id={fieldIds.fill}
276275
options={fillModeNames}
277-
getLabel={(fillModeName: string) => titleCase(fillModeName)}
276+
getLabel={humanizeString}
278277
value={value.timing.fill ?? fillModeNames[0]}
279278
onItemHighlight={(fillModeName) => {
280279
if (fillModeName === undefined) {
@@ -356,9 +355,7 @@ export const AnimationPanelContent = ({ onChange, value, type }: Props) => {
356355
<Select
357356
id={fieldIds.rangeStartName}
358357
options={timelineRangeNames}
359-
getLabel={(timelineRangeName: string) =>
360-
toPascalCase(timelineRangeName)
361-
}
358+
getLabel={humanizeString}
362359
value={value.timing.rangeStart?.[0] ?? timelineRangeNames[0]!}
363360
getDescription={(timelineRangeName: string) => (
364361
<Box
@@ -455,9 +452,7 @@ export const AnimationPanelContent = ({ onChange, value, type }: Props) => {
455452
<Select
456453
id={fieldIds.rangeEndName}
457454
options={timelineRangeNames}
458-
getLabel={(timelineRangeName: string) =>
459-
toPascalCase(timelineRangeName)
460-
}
455+
getLabel={humanizeString}
461456
value={value.timing.rangeEnd?.[0] ?? timelineRangeNames[0]!}
462457
getDescription={(timelineRangeName: string) => (
463458
<Box

apps/builder/app/builder/features/settings-panel/props-section/animation/animation-section.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useState } from "react";
12
import {
23
Grid,
34
theme,
@@ -20,7 +21,6 @@ import type {
2021
AnimationActionScroll,
2122
InsetUnitValue,
2223
} from "@webstudio-is/sdk";
23-
import { toPascalCase } from "~/builder/features/style-panel/shared/keyword-utils";
2424
import {
2525
animationActionSchema,
2626
insetUnitValueSchema,
@@ -34,7 +34,7 @@ import {
3434
CssValueInput,
3535
type IntermediateStyleValue,
3636
} from "~/builder/features/style-panel/shared/css-value-input";
37-
import { useState } from "react";
37+
import { humanizeString } from "~/shared/string-utils";
3838

3939
const animationTypeDescription: Record<AnimationAction["type"], string> = {
4040
scroll:
@@ -292,9 +292,7 @@ export const AnimateSection = ({
292292
<Select
293293
id={fieldIds.type}
294294
options={animationTypes}
295-
getLabel={(animationType: AnimationAction["type"]) =>
296-
toPascalCase(animationType)
297-
}
295+
getLabel={humanizeString}
298296
value={value.type}
299297
getDescription={(animationType: AnimationAction["type"]) => (
300298
<Box
@@ -352,9 +350,7 @@ export const AnimateSection = ({
352350
<Select
353351
id={fieldIds.source}
354352
options={animationSources}
355-
getLabel={(
356-
animationSource: NonNullable<AnimationActionScroll["source"]>
357-
) => toPascalCase(animationSource)}
353+
getLabel={humanizeString}
358354
value={value.source ?? "nearest"}
359355
getDescription={(
360356
animationSource: NonNullable<AnimationActionScroll["source"]>

apps/builder/app/builder/features/style-panel/controls/select/select-control.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { noCase } from "change-case";
21
import {
32
camelCaseProperty,
43
declarationDescriptions,
@@ -11,7 +10,6 @@ import {
1110
type CssProperty,
1211
} from "@webstudio-is/css-engine";
1312
import { Box, Select, theme } from "@webstudio-is/design-system";
14-
import { toKebabCase } from "../../shared/keyword-utils";
1513
import { useComputedStyleDecl } from "../../shared/model";
1614
import {
1715
resetEphemeralStyles,
@@ -70,7 +68,6 @@ export const SelectControl = ({
7068
// Show empty field instead of radix placeholder like css value input does.
7169
placeholder=""
7270
options={options}
73-
getLabel={toKebabCase}
7471
value={valueString}
7572
onChange={(name) => setValue({ type: "keyword", value: name })}
7673
onItemHighlight={(name) => {
@@ -100,7 +97,7 @@ export const SelectControl = ({
10097
declarationDescriptions[`${camelCaseProperty(property)}:${option}`];
10198
return (
10299
<Box css={{ width: theme.spacing[26] }}>
103-
{description ?? `The ${noCase(property)} is ${option}`}
100+
{description ?? `The ${property} is ${option}`}
104101
</Box>
105102
);
106103
}}

apps/builder/app/builder/features/style-panel/sections/backgrounds/background-thumbnail.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from "@webstudio-is/css-engine";
1010
import { $assets } from "~/shared/nano-states";
1111
import brokenImage from "~/shared/images/broken-image-placeholder.svg";
12-
import { toPascalCase } from "../../shared/keyword-utils";
12+
import { humanizeString } from "~/shared/string-utils";
1313
import { useComputedStyles } from "../../shared/model";
1414
import { getComputedRepeatedItem } from "../../shared/repeated-style";
1515

@@ -105,7 +105,7 @@ export const getBackgroundLabel = (
105105
backgroundImageStyle.value.includes(name)
106106
);
107107

108-
return gradientName ? toPascalCase(gradientName) : "Gradient";
108+
return gradientName ? humanizeString(gradientName) : "Gradient";
109109
}
110110

111111
return "None";

apps/builder/app/builder/features/style-panel/sections/box-shadows/box-shadows.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export const Section = () => {
7878
onAdd={() => {
7979
addRepeatedStyleItem(
8080
[styleDecl],
81-
parseCssFragment(initialBoxShadow, ["boxShadow"])
81+
parseCssFragment(initialBoxShadow, ["box-shadow"])
8282
);
8383
}}
8484
>

apps/builder/app/builder/features/style-panel/sections/text-shadows/text-shadows.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export const Section = () => {
6565
onAdd={() => {
6666
addRepeatedStyleItem(
6767
[styleDecl],
68-
parseCssFragment(initialTextShadow, ["textShadow"])
68+
parseCssFragment(initialTextShadow, ["text-shadow"])
6969
);
7070
}}
7171
>

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
completionKeymap,
88
type CompletionSource,
99
} from "@codemirror/autocomplete";
10-
import { parseCss } from "@webstudio-is/css-data";
10+
import { parseCss, shorthandProperties } from "@webstudio-is/css-data";
1111
import { css as style } from "@webstudio-is/design-system";
1212
import type { CssProperty, StyleValue } from "@webstudio-is/css-engine";
1313
import {
@@ -19,9 +19,11 @@ import {
1919
} from "~/builder/shared/code-editor-base";
2020
import { $availableVariables } from "./model";
2121

22+
type ShorthandProperty = (typeof shorthandProperties)[number];
23+
2224
export const parseCssFragment = (
2325
css: string,
24-
fallbacks: string[]
26+
fallbacks: (CssProperty | ShorthandProperty)[]
2527
): Map<CssProperty, StyleValue> => {
2628
let parsed = parseCss(`.styles{${css}}`);
2729
if (parsed.length === 0) {

apps/builder/app/builder/features/style-panel/shared/css-value-input/keyword-utils.test.ts

Lines changed: 0 additions & 34 deletions
This file was deleted.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { kebabCase } from "change-case";
12
import type {
23
StyleValue,
34
InvalidValue,
@@ -12,7 +13,6 @@ import {
1213
} from "@webstudio-is/css-data";
1314
import type { IntermediateStyleValue } from "./css-value-input";
1415
import { evaluateMath } from "./evaluate-math";
15-
import { toKebabCase } from "../keyword-utils";
1616

1717
const unitsList = Object.values(units).flat();
1818

@@ -110,7 +110,7 @@ export const parseIntermediateOrInvalidValue = (
110110
}
111111

112112
// Probably in kebab-case value will be valid
113-
styleInput = parseCssValue(property, toKebabCase(value));
113+
styleInput = parseCssValue(property, kebabCase(value));
114114

115115
if (styleInput.type !== "invalid") {
116116
return styleInput;

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

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
import { describe, test, expect } from "vitest";
22
import { parseIntermediateOrInvalidValue } from "./parse-intermediate-or-invalid-value";
3-
import { toKebabCase, toPascalCase } from "../keyword-utils";
43

54
const properties = ["width", "line-height"] as const;
65

7-
const propertiesAndKeywords = [
8-
["width", "auto" as string],
9-
["line-height", "normal" as string],
10-
] as const;
11-
126
test("forgive trailing semicolon", () => {
137
expect(
148
parseIntermediateOrInvalidValue("width", {
@@ -99,37 +93,37 @@ describe("Parse intermediate or invalid value without math evaluation", () => {
9993
});
10094

10195
test("accept keywords", () => {
102-
for (const [propery, keyword] of propertiesAndKeywords) {
103-
const result = parseIntermediateOrInvalidValue(propery, {
96+
expect(
97+
parseIntermediateOrInvalidValue("width", {
10498
type: "intermediate",
105-
value: keyword,
99+
value: "auto",
106100
unit: "em",
107-
});
108-
109-
expect(result).toEqual({
110-
type: "keyword",
111-
value: keyword,
112-
});
113-
}
101+
})
102+
).toEqual({ type: "keyword", value: "auto" });
103+
expect(
104+
parseIntermediateOrInvalidValue("line-height", {
105+
type: "intermediate",
106+
value: "normal",
107+
unit: "em",
108+
})
109+
).toEqual({ type: "keyword", value: "normal" });
114110
});
115111

116112
test("accept keywords written as pascal case", () => {
117-
const pascalCaseKeywords = propertiesAndKeywords.map(
118-
([property, keyword]) => [property, toPascalCase(keyword)] as const
119-
);
120-
121-
for (const [propery, keyword] of pascalCaseKeywords) {
122-
const result = parseIntermediateOrInvalidValue(propery, {
113+
expect(
114+
parseIntermediateOrInvalidValue("width", {
123115
type: "intermediate",
124-
value: keyword,
116+
value: "Auto",
125117
unit: "em",
126-
});
127-
128-
expect(result).toEqual({
129-
type: "keyword",
130-
value: toKebabCase(keyword),
131-
});
132-
}
118+
})
119+
).toEqual({ type: "keyword", value: "auto" });
120+
expect(
121+
parseIntermediateOrInvalidValue("line-height", {
122+
type: "intermediate",
123+
value: "Normal",
124+
unit: "em",
125+
})
126+
).toEqual({ type: "keyword", value: "normal" });
133127
});
134128

135129
test("keyword with pascal case name", () => {

0 commit comments

Comments
 (0)