fix: Variable bindings on Class don't show on live site (#4253)

## Description

closes #3683

## Steps for reproduction

Tests and fixtures


## 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` file
This commit is contained in:
Ivan Starkov authored and GitHub committed 2024-10-09 15:04:48 +03:00
1 parent b921c1d558
commit 02bf5974a9
7 files changed
+181 -19

No files matched your search

@@ -1,10 +1,10 @@
{
"build": {
"id": "40962d82-ea2d-4536-b94f-dece81312190",
"id": "6876de3a-942a-466b-9761-ad7cb2fa2c21",
"projectId": "cddc1d44-af37-4cb6-a430-d300cf6f932d",
"version": 315,
"createdAt": "2024-09-30T13:22:48.689+00:00",
"updatedAt": "2024-09-30T13:22:48.689+00:00",
"version": 321,
"createdAt": "2024-10-08T17:47:04.557+00:00",
"updatedAt": "2024-10-08T17:47:04.557+00:00",
"pages": {
"meta": {
"siteName": "KittyGuardedZone",
@@ -1540,6 +1540,19 @@
"value": 300
}
}
],
[
"g6Z7dAxi6LYB-XtI6SaRR:UoTkWyaFuTYJihS3MFYK5:marginTop:",
{
"breakpointId": "UoTkWyaFuTYJihS3MFYK5",
"styleSourceId": "g6Z7dAxi6LYB-XtI6SaRR",
"property": "marginTop",
"value": {
"type": "unit",
"value": 12,
"unit": "px"
}
}
]
],
"styleSources": [
@@ -1717,6 +1730,13 @@
"type": "local",
"id": "o9VRyqmvIarF6HrGMccU8"
}
],
[
"g6Z7dAxi6LYB-XtI6SaRR",
{
"type": "local",
"id": "g6Z7dAxi6LYB-XtI6SaRR"
}
]
],
"styleSourceSelections": [
@@ -1894,6 +1914,13 @@
"instanceId": "drCx7m8q_gnNQLrhPDA-g",
"values": ["o9VRyqmvIarF6HrGMccU8"]
}
],
[
"T-rqx2b12pRrWcafRRoIG",
{
"instanceId": "T-rqx2b12pRrWcafRRoIG",
"values": ["g6Z7dAxi6LYB-XtI6SaRR"]
}
]
],
"props": [
@@ -2297,6 +2324,16 @@
"type": "string",
"value": "\"broken'with`symbols"
}
],
[
"I4cM2PzBX49KWxsKdGMd1",
{
"id": "I4cM2PzBX49KWxsKdGMd1",
"instanceId": "T-rqx2b12pRrWcafRRoIG",
"name": "className",
"type": "expression",
"value": "`${$ws$dataSource$FG6camSaR89vSBREAV9PT} class_3`"
}
]
],
"dataSources": [
@@ -2463,6 +2500,19 @@
"scopeInstanceId": "mdyCS8QnKx3fL1MLXifmy",
"name": "system"
}
],
[
"FG6camSaR89vSBREAV9PT",
{
"type": "variable",
"id": "FG6camSaR89vSBREAV9PT",
"scopeInstanceId": "mdyCS8QnKx3fL1MLXifmy",
"name": "classVar",
"value": {
"type": "string",
"value": "varClass"
}
}
]
],
"resources": [
@@ -3581,6 +3631,10 @@
{
"type": "id",
"value": "drCx7m8q_gnNQLrhPDA-g"
},
{
"type": "id",
"value": "T-rqx2b12pRrWcafRRoIG"
}
]
}
@@ -3593,6 +3647,15 @@
"component": "Box",
"children": []
}
],
[
"T-rqx2b12pRrWcafRRoIG",
{
"type": "instance",
"id": "T-rqx2b12pRrWcafRRoIG",
"component": "Box",
"children": []
}
]
],
"deployment": {
@@ -1,26 +1,26 @@
export const sitemap = [
{
path: "/",
lastModified: "2024-09-30",
lastModified: "2024-10-08",
},
{
path: "/_route_with_symbols_",
lastModified: "2024-09-30",
lastModified: "2024-10-08",
},
{
path: "/form",
lastModified: "2024-09-30",
lastModified: "2024-10-08",
},
{
path: "/heading-with-id",
lastModified: "2024-09-30",
lastModified: "2024-10-08",
},
{
path: "/resources",
lastModified: "2024-09-30",
lastModified: "2024-10-08",
},
{
path: "/nested/nested-page",
lastModified: "2024-09-30",
lastModified: "2024-10-08",
},
];
@@ -27,12 +27,16 @@ export const pageFontAssets: FontAsset[] = [];
export const pageBackgroundImageAssets: ImageAsset[] = [];
const Page = ({}: { system: any }) => {
let [classVar, set$classVar] = useVariableState<any>("varClass");
return (
<Body className={"w-body"}>
<Box
id={"\"broken'with`symbols"}
className={"w-box cm16yxw custom-class \"broken 'with `symbols"}
className={
"w-box cm16yxw" + " " + "custom-class \"broken 'with `symbols"
}
/>
<Box className={"w-box ctm310" + " " + `${classVar} class_3`} />
</Body>
);
};
@@ -385,4 +385,7 @@
.cm16yxw {
width: 300px;
}
.ctm310 {
margin-top: 12px;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
"typecheck": "tsc",
"cli": "NODE_OPTIONS='--conditions=webstudio --import=tsx' webstudio",
"fixtures:link": "pnpm cli link --link https://p-cddc1d44-af37-4cb6-a430-d300cf6f932d-dot-${BUILDER_HOST:-main.development.webstudio.is}'?authToken=1cdc6026-dd5b-4624-b89b-9bd45e9bcc3d'",
"fixtures:sync": "pnpm cli sync --buildId 40962d82-ea2d-4536-b94f-dece81312190 && pnpm prettier --write ./.webstudio/",
"fixtures:sync": "pnpm cli sync --buildId 6876de3a-942a-466b-9761-ad7cb2fa2c21 && pnpm prettier --write ./.webstudio/",
"fixtures:build": "pnpm cli build --template vercel --template internal --preview && pnpm prettier --write ./app/ ./package.json ./tsconfig.json"
},
"private": true,
@@ -567,6 +567,30 @@ test("generate component with variables and actions", () => {
);
});
test("merge classes if no className", () => {
expect(
generateWebstudioComponent({
classesMap: new Map([["body", ["cls1"]]]),
scope: createScope(),
name: "Page",
rootInstanceId: "body",
parameters: [],
dataSources: new Map(),
indexesWithinAncestors: new Map(),
...renderJsx(<$.Body ws:id="body"></$.Body>),
})
).toEqual(
validateJSX(
clear(`
const Page = () => {
return <Body
className={"cls1"} />
}
`)
)
);
});
test("add classes and merge classes", () => {
expect(
generateWebstudioComponent({
@@ -584,7 +608,70 @@ test("add classes and merge classes", () => {
clear(`
const Page = () => {
return <Body
className={"cls1 cls2 \\"cls3\\""} />
className={"cls1" + " " + "cls2 \\"cls3\\""} />
}
`)
)
);
});
test("add classes", () => {
expect(
generateWebstudioComponent({
classesMap: new Map(),
scope: createScope(),
name: "Page",
rootInstanceId: "body",
parameters: [],
dataSources: new Map(),
indexesWithinAncestors: new Map(),
...renderJsx(<$.Body ws:id="body" className='cls2 "cls3"'></$.Body>),
})
).toEqual(
validateJSX(
clear(`
const Page = () => {
return <Body
className={"cls2 \\"cls3\\""} />
}
`)
)
);
});
test("add bind classes and merge classes", () => {
expect(
generateWebstudioComponent({
classesMap: new Map([["body", ["cls1"]]]),
scope: createScope(),
name: "Page",
rootInstanceId: "body",
parameters: [],
dataSources: toMap([
{
type: "variable",
id: "variableId",
name: "variableName",
value: { type: "string", value: "cls3" },
},
]),
indexesWithinAncestors: new Map(),
...renderJsx(
<$.Body
ws:id="body"
className={
new ExpressionValue(`'cls2' + ' ' + $ws$dataSource$variableId`)
}
></$.Body>
),
})
).toEqual(
validateJSX(
clear(`
const Page = () => {
let [variableName, set$variableName] = useVariableState<any>("cls3")
return <Body
className={"cls1" + " " + 'cls2' + ' ' + variableName} />
}
`)
)
+11 -6
View File
@@ -170,11 +170,17 @@ export const generateJsxElement = ({
let collectionDataValue: undefined | string;
let collectionItemValue: undefined | string;
const classes = Array.from(classesMap?.get(instance.id) ?? []);
const classMapArray = classesMap?.get(instance.id);
const classes =
classMapArray !== undefined
? [JSON.stringify(classMapArray.join(" "))]
: [];
for (const prop of props.values()) {
if (prop.instanceId !== instance.id) {
continue;
}
const propValue = generatePropValue({
scope,
prop,
@@ -210,10 +216,9 @@ export const generateJsxElement = ({
continue;
}
// We need to merge atomic classes with user-defined className prop.
if (prop.name === "className") {
if (prop.type === "string") {
classes.push(prop.value);
}
if (prop.name === "className" && propValue !== undefined) {
classes.push(propValue);
continue;
}
if (propValue !== undefined) {
@@ -222,7 +227,7 @@ export const generateJsxElement = ({
}
if (classes.length !== 0) {
generatedProps += `\nclassName={${JSON.stringify(classes.join(" "))}}`;
generatedProps += `\nclassName={${classes.join(` + " " + `)}}`;
}
let generatedElement = "";