framer-motion
Version:
An open source and production-ready motion library for React on the web
271 lines (237 loc) • 7.91 kB
text/typescript
import "../../../../../jest.setup"
import { buildHTMLStyles } from "../build-html-styles"
import { DOMVisualElementOptions, TransformOrigin } from "../../types"
import { BoxDelta, Point2D, AxisBox2D } from "../../../../types/geometry"
import { ResolvedValues } from "../../../types"
describe("buildHTMLStyles", () => {
test("Builds basic styles", () => {
const latest = { width: 100 }
const style = {}
build(latest, { style })
expect(style).toEqual({ width: "100px" })
})
test("Builds vars", () => {
const latest = { "--width": 100 }
const vars = {}
build(latest, { vars })
expect(vars).toEqual({ "--width": 100 })
})
test("Builds transform with default value types", () => {
const latest = { x: 1, y: 2, rotateX: 90, transformPerspective: 200 }
const style = {}
build(latest, { style })
expect(style).toEqual({
transform:
"perspective(200px) translateX(1px) translateY(2px) rotateX(90deg) translateZ(0)",
})
})
test("Builds transform with defined value types", () => {
const latest = { x: "1vw", y: "2%", rotateX: "90turn" }
const style = {}
build(latest, { style })
expect(style).toEqual({
transform:
"translateX(1vw) translateY(2%) rotateX(90turn) translateZ(0)",
})
})
test("Builds transform without translateZ if enableHardwareAcceleration is false", () => {
const latest = { x: 1, y: 2, rotateX: 90 }
const style = {}
build(latest, { style, config: { enableHardwareAcceleration: false } })
expect(style).toEqual({
transform: "translateX(1px) translateY(2px) rotateX(90deg)",
})
})
test("Builds transform none if all transforms are default", () => {
const latest = { x: 0, y: 0, scale: 1 }
const style = {}
build(latest, { style })
expect(style).toEqual({
transform: "none",
})
})
test("Doesn't build transform none if all transforms are default if allowTransformNone is false", () => {
const latest = { x: 0, y: 0, scale: 1 }
const style = {}
build(latest, { style, config: { allowTransformNone: false } })
expect(style).toEqual({
transform: "translateX(0px) translateY(0px) scale(1) translateZ(0)",
})
})
test("Builds transformOrigin with correct default value types", () => {
const latest = { originX: 0.2, originY: "60%", originZ: 10 }
const style = {}
build(latest, { style })
expect(style).toEqual({
transformOrigin: "20% 60% 10px",
})
})
test("Applies transformTemplate if provided", () => {
const latest = { x: 1 }
const style = {}
build(latest, {
style,
config: {
transformTemplate: ({ x }: any, gen: any) =>
`translateY(${parseFloat(x as string) * 2}) ${gen}`,
},
})
expect(style).toEqual({
transform: "translateY(2) translateX(1px) translateZ(0)",
})
})
test("Applies transformTemplate if provided", () => {
const latest = { x: 1 }
const style = {}
build(latest, {
style,
config: {
transformTemplate: ({ x }: any, gen: any) =>
`translateY(${parseFloat(x as string) * 2}) ${gen}`,
},
})
expect(style).toEqual({
transform: "translateY(2) translateX(1px) translateZ(0)",
})
})
test("Uses layout projection transform if enabled", () => {
const latest = {}
const style = {}
const delta = {
x: { translate: 1, scale: 0.5, origin: 0.8, originPoint: 0 },
y: { translate: 2, scale: 2, origin: 0.2, originPoint: 0 },
}
build(latest, {
style,
isLayoutProjectionEnabled: true,
treeScale: { x: 1, y: 1 },
delta,
deltaFinal: delta,
targetBox: {
x: { min: 0, max: 100 },
y: { min: 0, max: 100 },
},
})
expect(style).toEqual({
transform: "translate3d(1px, 2px, 0) scale(0.5, 2)",
transformOrigin: "80% 20% 0",
})
})
test("Adds rotate transforms if present, but not translation, scale and skew", () => {
/**
* Translation and rotate are already baked into deltaFinal
*/
const latest = { x: 100, rotate: 90, skew: 3 }
const style = {}
const delta = {
x: { translate: 1, scale: 0.5, origin: 0.8, originPoint: 0 },
y: { translate: 2, scale: 2, origin: 0.2, originPoint: 0 },
}
build(latest, {
style,
isLayoutProjectionEnabled: true,
treeScale: { x: 1, y: 1 },
delta,
deltaFinal: delta,
targetBox: {
x: { min: 0, max: 100 },
y: { min: 0, max: 100 },
},
})
expect(style).toEqual({
transform: "translate3d(1px, 2px, 0) rotate(90deg) scale(0.5, 2)",
transformOrigin: "80% 20% 0",
})
})
test("Applies scale correction to borderRadius and boxShadow", () => {
/**
* Translation and rotate are already baked into deltaFinal
*/
const latest = {
borderRadius: 1,
boxShadow: "10px 20px 30px rgba(0, 0, 0, 0.5)",
}
const style = {}
const delta = {
x: { translate: 1, scale: 0.5, origin: 0.8, originPoint: 0 },
y: { translate: 2, scale: 2, origin: 0.2, originPoint: 0 },
}
build(latest, {
style,
isLayoutProjectionEnabled: true,
treeScale: { x: 0.5, y: 2 },
delta,
deltaFinal: delta,
targetBox: {
x: { min: 0, max: 10 },
y: { min: 0, max: 20 },
},
})
expect(style).toEqual({
transform: "translate3d(2px, 1px, 0) scale(0.5, 2)",
transformOrigin: "80% 20% 0",
borderBottomLeftRadius: "10% 5%",
borderBottomRightRadius: "10% 5%",
borderTopLeftRadius: "10% 5%",
borderTopRightRadius: "10% 5%",
boxShadow: "40px 5px 14.11765px rgba(0, 0, 0, 0.5)",
})
})
})
interface BuildProps {
style: ResolvedValues
vars: ResolvedValues
transform: ResolvedValues
transformOrigin: TransformOrigin
transformKeys: string[]
config: DOMVisualElementOptions & { transformTemplate?: any }
isLayoutProjectionEnabled: boolean
delta?: BoxDelta
deltaFinal?: BoxDelta
treeScale?: Point2D
targetBox?: AxisBox2D
}
function build(
latest: ResolvedValues,
{
style = {},
vars = {},
transform = {},
transformOrigin = {},
transformKeys = [],
config = {
enableHardwareAcceleration: true,
allowTransformNone: true,
},
isLayoutProjectionEnabled,
delta,
deltaFinal,
treeScale,
targetBox,
}: Partial<BuildProps> = {}
) {
buildHTMLStyles(
{
style,
vars,
transform,
transformOrigin,
transformKeys,
},
latest,
{
target: targetBox,
isEnabled: isLayoutProjectionEnabled,
isTargetLocked: false,
targetFinal: targetBox,
} as any,
{
delta,
deltaFinal,
treeScale,
isHydrated: true,
} as any,
config,
config.transformTemplate
)
}