diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/rotate.tsx b/apps/builder/app/builder/features/style-panel/sections/transforms/rotate.tsx index d300a3960485..1fb504005179 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/rotate.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/rotate.tsx @@ -3,7 +3,7 @@ import { isUnitValue, updateRotateOrSkewPropertyValue, type TransformFloatingPanelContentProps, -} from "./utils"; +} from "./transform-utils"; import { XAxisRotateIcon, YAxisRotateIcon, diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/scale.tsx b/apps/builder/app/builder/features/style-panel/sections/transforms/scale.tsx index 266647d2c3df..014cdf451def 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/scale.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/scale.tsx @@ -10,7 +10,7 @@ import { isUnitValue, updateTransformTuplePropertyValue, type TransformFloatingPanelContentProps, -} from "./utils"; +} from "./transform-utils"; import { XAxisIcon, YAxisIcon, ZAxisIcon } from "@webstudio-is/icons"; import { parseCssValue } from "@webstudio-is/css-data"; diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/skew.tsx b/apps/builder/app/builder/features/style-panel/sections/transforms/skew.tsx index 45624c03df5a..3cdbc126eb65 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/skew.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/skew.tsx @@ -3,7 +3,7 @@ import { isUnitValue, updateRotateOrSkewPropertyValue, type TransformFloatingPanelContentProps, -} from "./utils"; +} from "./transform-utils"; import { XAxisIcon, YAxisIcon } from "@webstudio-is/icons"; import { CssValueInputContainer } from "../../shared/css-value-input"; import type { StyleUpdateOptions } from "../../shared/use-style-data"; diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/transform-utils.test.ts b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-utils.test.ts new file mode 100644 index 000000000000..5f1222b638e4 --- /dev/null +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-utils.test.ts @@ -0,0 +1,255 @@ +import { afterEach, describe, test, expect } from "@jest/globals"; +import { + addDefaultsForTransormSection, + handleDeleteTransformProperty, + handleHideTransformProperty, + isTransformPanelPropertyExists, + updateRotateOrSkewPropertyValue, + updateTransformTuplePropertyValue, +} from "./transform-utils"; +import type { StyleInfo } from "../../shared/style-info"; +import { + FunctionValue, + toValue, + TupleValue, + type StyleProperty, + type StyleValue, +} from "@webstudio-is/css-engine"; +import { + extractRotatePropertiesFromTransform, + parseCssValue, +} from "@webstudio-is/css-data"; + +let currentStyle: StyleInfo = {}; +const setProperty = (property: StyleProperty) => (value: StyleValue) => { + currentStyle[property] = { value }; +}; +const deleteProperty = (property: StyleProperty) => { + delete currentStyle[property]; +}; + +afterEach(() => { + currentStyle = {}; +}); + +describe("Transform utils CRUD operations", () => { + test("adds a default translate property", () => { + addDefaultsForTransormSection({ + panel: "translate", + currentStyle, + setProperty, + }); + const translate = currentStyle["translate"]?.value; + expect(translate).toEqual({ + type: "tuple", + value: [ + { + type: "unit", + unit: "px", + value: 0, + }, + { + type: "unit", + unit: "px", + value: 0, + }, + { + type: "unit", + unit: "px", + value: 0, + }, + ], + }); + }); + + test("adds a default scale property", () => { + addDefaultsForTransormSection({ + panel: "scale", + currentStyle, + setProperty, + }); + + const scale = currentStyle["scale"]?.value; + expect(scale).toEqual({ + type: "tuple", + value: [ + { + type: "unit", + unit: "number", + value: 1, + }, + { + type: "unit", + unit: "number", + value: 1, + }, + { + type: "unit", + unit: "number", + value: 1, + }, + ], + }); + }); + + test("adds a default rotate and scale property", () => { + addDefaultsForTransormSection({ + panel: "rotate", + currentStyle, + setProperty, + }); + + expect(toValue(currentStyle["transform"]?.value)).toBe( + "rotateX(0deg) rotateY(0deg) rotateZ(0deg)" + ); + + addDefaultsForTransormSection({ + panel: "skew", + currentStyle, + setProperty, + }); + expect(toValue(currentStyle["transform"]?.value)).toBe( + "skewX(0deg) skewY(0deg) rotateX(0deg) rotateY(0deg) rotateZ(0deg)" + ); + }); + + test("checks if any of the transform property exist", () => { + expect( + isTransformPanelPropertyExists({ + currentStyle, + panel: "translate", + }) + ).toBe(false); + + addDefaultsForTransormSection({ + panel: "rotate", + currentStyle, + setProperty, + }); + + expect( + isTransformPanelPropertyExists({ currentStyle, panel: "rotate" }) + ).toBe(true); + }); + + test("deletes rotate property values from the transform", () => { + setProperty("transform")( + parseCssValue( + "transform", + "rotateX(50deg) rotateY(50deg) rotateZ(50deg) scale(1, 1) translate(10px, 10px)" + ) + ); + + handleDeleteTransformProperty({ + panel: "rotate", + currentStyle, + setProperty, + deleteProperty, + }); + + expect(toValue(currentStyle["transform"]?.value)).toBe( + "scale(1, 1) translate(10px, 10px)" + ); + }); + + test("delete skew property values from the transform", () => { + setProperty("transform")( + parseCssValue( + "transform", + "rotateX(50deg) rotateY(50deg) rotateZ(50deg) skew(10deg) skewX(10deg) skewY(10deg)" + ) + ); + handleDeleteTransformProperty({ + panel: "skew", + currentStyle, + setProperty, + deleteProperty, + }); + expect(toValue(currentStyle["transform"]?.value)).toBe( + "rotateX(50deg) rotateY(50deg) rotateZ(50deg) skew(10deg)" + ); + }); + + test("hide translate and rotate properties", () => { + addDefaultsForTransormSection({ + panel: "translate", + currentStyle, + setProperty, + }); + addDefaultsForTransormSection({ + panel: "rotate", + currentStyle, + setProperty, + }); + + handleHideTransformProperty({ + panel: "translate", + currentStyle, + setProperty, + }); + expect(currentStyle["translate"]?.value?.hidden).toBe(true); + + handleHideTransformProperty({ + panel: "rotate", + currentStyle, + setProperty, + }); + const rotate = extractRotatePropertiesFromTransform( + currentStyle["transform"]?.value as TupleValue + ); + + expect(rotate.rotateX?.hidden).toBe(true); + expect(rotate.rotateY?.hidden).toBe(true); + expect(rotate.rotateZ?.hidden).toBe(true); + }); + + test("update translate property value", () => { + const translate = parseCssValue("translate", "0px 0px 0px"); + const newValue = updateTransformTuplePropertyValue( + 1, + { type: "unit", value: 50, unit: "px" }, + translate as TupleValue + ); + + expect(toValue(newValue)).toBe("0px 50px 0px"); + }); + + test("update rotate values in transform property", () => { + const transform = parseCssValue( + "transform", + "rotateX(10deg) rotateY(10deg) rotateZ(10deg) skewX(10deg) skewY(10deg)" + ); + setProperty("transform")(transform); + + const updatedRotateYValue: FunctionValue = { + type: "function", + name: "rotateY", + args: { + type: "layers", + value: [{ type: "unit", value: 50, unit: "deg" }], + }, + }; + const { rotateX, rotateY, rotateZ } = + extractRotatePropertiesFromTransform(transform); + const newValue = updateRotateOrSkewPropertyValue({ + index: 1, + panel: "rotate", + currentStyle, + value: updatedRotateYValue, + propertyValue: { + type: "tuple", + value: [ + rotateX as FunctionValue, + rotateY as FunctionValue, + rotateZ as FunctionValue, + ], + }, + }); + + const result = extractRotatePropertiesFromTransform(newValue); + expect(result.rotateY).toEqual(updatedRotateYValue); + expect(toValue(newValue)).toBe( + "rotateX(10deg) rotateY(50deg) rotateZ(10deg) skewX(10deg) skewY(10deg)" + ); + }); +}); diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/utils.ts b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-utils.ts similarity index 97% rename from apps/builder/app/builder/features/style-panel/sections/transforms/utils.ts rename to apps/builder/app/builder/features/style-panel/sections/transforms/transform-utils.ts index a936d150cfaa..f6f2899b0ccf 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/utils.ts +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/transform-utils.ts @@ -27,6 +27,10 @@ const defaultScale = "1 1 1"; const defaultRotate = "rotateX(0deg) rotateY(0deg) rotateZ(0deg)"; const defaultSkew = "skewX(0deg) skewY(0deg)"; +export const isUnitValue = (value: StyleValue): value is UnitValue => { + return value?.type === "unit" ? true : false; +}; + export const useHumaneTransformPropertyValues = (props: { currentStyle: StyleInfo; panel: TransformPanel; @@ -158,10 +162,6 @@ export const addDefaultsForTransormSection = (props: { } }; -export const isUnitValue = (value: StyleValue): value is UnitValue => { - return value?.type === "unit" ? true : false; -}; - export const isTransformPanelPropertyExists = (params: { currentStyle: StyleInfo; panel: TransformPanel; @@ -368,12 +368,12 @@ export const updateRotateOrSkewPropertyValue = (props: { propertyValue ); - const existingTransform = props.currentStyle["transform"]?.value; - if (existingTransform?.type === "tuple") { - const filteredValues = removeRotateOrSkewValues(panel, existingTransform); + const existingTransforms = props.currentStyle["transform"]?.value; + if (existingTransforms?.type === "tuple") { + const filteredValues = removeRotateOrSkewValues(panel, existingTransforms); return { - ...existingTransform, - value: [...filteredValues, ...newPropertyValue.value], + ...existingTransforms, + value: [...newPropertyValue.value, ...filteredValues], }; } diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/transforms.tsx b/apps/builder/app/builder/features/style-panel/sections/transforms/transforms.tsx index ea1883130aee..0528a0639a48 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/transforms.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/transforms.tsx @@ -30,7 +30,7 @@ import { isTransformPanelPropertyExists, handleDeleteTransformProperty, handleHideTransformProperty, -} from "./utils"; +} from "./transform-utils"; import { FloatingPanel } from "~/builder/shared/floating-panel"; import { TransformPanelContent } from "./transform-panel"; import { isFeatureEnabled } from "@webstudio-is/feature-flags"; diff --git a/apps/builder/app/builder/features/style-panel/sections/transforms/translate.tsx b/apps/builder/app/builder/features/style-panel/sections/transforms/translate.tsx index ed3e8602ab18..53715b14b16a 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transforms/translate.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transforms/translate.tsx @@ -9,7 +9,7 @@ import { isUnitValue, updateTransformTuplePropertyValue, type TransformFloatingPanelContentProps, -} from "./utils"; +} from "./transform-utils"; import type { StyleUpdateOptions } from "../../shared/use-style-data"; import { XAxisIcon, YAxisIcon } from "@webstudio-is/icons"; import { parseCssValue } from "@webstudio-is/css-data";