feat: Extend tailwind-to-webstudio conversion to support background properties (#2387)

## Description

1. Add support of simple tuple types to the parseCssValue
2. Add background properties conversion

## Steps for reproduction

see tests

## 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:
5de6)
- [ ] 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.example`
and the `builder/env-check.js` if mandatory
This commit is contained in:
Ivan Starkov authored and GitHub committed 2023-09-28 19:54:56 +04:00
1 parent e56995d7af
commit 3c75b4e870
4 files changed
+129 -8

No files matched your search

@@ -105,6 +105,27 @@ describe("Parse CSS value", () => {
});
});
describe("Tuples", () => {
test("backgroundPosition", () => {
expect(parseCssValue("backgroundPosition", "left top"))
.toMatchInlineSnapshot(`
{
"type": "tuple",
"value": [
{
"type": "keyword",
"value": "left",
},
{
"type": "keyword",
"value": "top",
},
],
}
`);
});
});
describe("Colors", () => {
test("Color rgba values", () => {
expect(parseCssValue("backgroundColor", "rgba(0,0,0,0)")).toEqual({
+27 -1
View File
@@ -2,7 +2,12 @@ import { colord } from "colord";
import * as csstree from "css-tree";
import hyphenate from "hyphenate-style-name";
import warnOnce from "warn-once";
import type { StyleProperty, StyleValue, Unit } from "@webstudio-is/css-engine";
import {
TupleValue,
type StyleProperty,
type StyleValue,
type Unit,
} from "@webstudio-is/css-engine";
import { keywordValues } from "./__generated__/keyword-values";
import { units } from "./__generated__/units";
@@ -150,6 +155,27 @@ export const parseCssValue = (
}
}
// Probably a tuple like background-position
if (ast != null && ast.type === "Value" && ast.children.size === 2) {
const tupleFirst = parseCssValue(
property,
csstree.generate(ast.children.first!)
);
const tupleLast = parseCssValue(
property,
csstree.generate(ast.children.last!)
);
const tupleResult = TupleValue.safeParse({
type: "tuple",
value: [tupleFirst, tupleLast],
});
if (tupleResult.success) {
return tupleResult.data;
}
}
return {
type: "unparsed",
value: input,
@@ -19,8 +19,11 @@ Quick Validation of Generated CSS in WebStudio:
{
"property": "backgroundImage",
"value": {
"type": "unparsed",
"value": "linear-gradient(to right,rgba(99,102,241,1) 0%,rgba(99,102,241,0) 100%)"
"type": "layers",
"value": [{
type: "unparsed",
value: "linear-gradient(to right,rgba(99,102,241,1) 0%,rgba(99,102,241,0) 100%)"
}]
}
}
],
@@ -75,7 +78,7 @@ describe("parseTailwindToWebstudio", () => {
});
test("substitute variables - gradient", async () => {
const tailwindClasses = `bg-gradient-to-r from-indigo-500`;
const tailwindClasses = `bg-left-top bg-gradient-to-r from-indigo-500 from-10% via-sky-500 via-30% to-emerald-500 to-90%`;
expect(await parseTailwindToWebstudio(tailwindClasses))
.toMatchInlineSnapshot(`
@@ -83,8 +86,34 @@ describe("parseTailwindToWebstudio", () => {
{
"property": "backgroundImage",
"value": {
"type": "unparsed",
"value": "linear-gradient(to right,rgba(99,102,241,1) 0%,rgba(99,102,241,0) 100%)",
"type": "layers",
"value": [
{
"type": "unparsed",
"value": "linear-gradient(to right,rgba(99,102,241,1) 10%,rgba(14,165,233,1) 30%,rgba(16,185,129,1) 90%)",
},
],
},
},
{
"property": "backgroundPosition",
"value": {
"type": "layers",
"value": [
{
"type": "tuple",
"value": [
{
"type": "keyword",
"value": "left",
},
{
"type": "keyword",
"value": "top",
},
],
},
],
},
},
]
+47 -2
View File
@@ -7,7 +7,7 @@ import { expandTailwindShorthand } from "./shorthand";
import { substituteVariables } from "./substitute";
import warnOnce from "warn-once";
import { parseCssValue } from "../parse-css-value";
import { type StyleProperty } from "@webstudio-is/css-engine";
import { LayersValue, type StyleProperty } from "@webstudio-is/css-engine";
let unoLazy: UnoGenerator<Theme> | undefined = undefined;
@@ -55,6 +55,48 @@ const parseCssToWebstudio = (css: string) => {
return styles;
};
/**
* In WebStudio, background-related properties are managed using a specialized "layer" type.
**/
const postprocessBackgrounds = (
styles: EmbedTemplateStyleDecl[],
warn = warnOnce
) => {
const backgroundProps = [
"backgroundAttachment",
"backgroundClip",
"backgroundBlendMode",
"backgroundImage",
"backgroundOrigin",
"backgroundPosition",
"backgroundRepeat",
"backgroundSize",
];
return styles.map((style) => {
if (backgroundProps.includes(style.property)) {
const layersResult = LayersValue.safeParse({
type: "layers",
value: [style.value],
});
if (layersResult.success) {
return {
property: style.property,
value: layersResult.data,
};
}
warn(
true,
`Failed to convert background property ${
style.property
} with value ${JSON.stringify(style.value)} to layers`
);
}
return style;
});
};
/**
* Parses Tailwind classes to webstudio template format.
*/
@@ -63,6 +105,9 @@ export const parseTailwindToWebstudio = async (
warn = warnOnce
) => {
const css = await parseTailwindToCss(classes, warn);
const styles = parseCssToWebstudio(css);
let styles = parseCssToWebstudio(css);
// postprocessing
styles = postprocessBackgrounds(styles, warn);
return styles;
};