From 6e97e232d87c624eade4142e897ddafe6d2d49dd Mon Sep 17 00:00:00 2001 From: Lukas Oppermann Date: Wed, 23 Apr 2025 13:38:25 +0200 Subject: [PATCH 1/4] add schema for transition tokens --- src/schemas/designToken.ts | 2 + src/schemas/transitionToken.ts | 20 ++++ src/schemas/transitionTokenSchema.test.ts | 138 ++++++++++++++++++++++ src/schemas/validTokenType.ts | 1 + 4 files changed, 161 insertions(+) create mode 100644 src/schemas/transitionToken.ts create mode 100644 src/schemas/transitionTokenSchema.test.ts diff --git a/src/schemas/designToken.ts b/src/schemas/designToken.ts index a01f59ae0..8d6c8a8cb 100644 --- a/src/schemas/designToken.ts +++ b/src/schemas/designToken.ts @@ -13,6 +13,7 @@ import {shadowToken} from './shadowToken.js' import {durationToken} from './durationToken.js' import {cubicBezierToken} from './cubicBezierToken.js' import {gradientToken} from './gradientToken.js' +import {transitionToken} from './transitionToken.js' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: TODO: fix this @@ -34,6 +35,7 @@ export const designToken = z.record( numberToken, durationToken, stringToken, + transitionToken, ]), designToken, ]) diff --git a/src/schemas/transitionToken.ts b/src/schemas/transitionToken.ts new file mode 100644 index 000000000..e9e092e67 --- /dev/null +++ b/src/schemas/transitionToken.ts @@ -0,0 +1,20 @@ +import {z} from 'zod' +import {baseToken} from './baseToken.js' +import {referenceValue} from './referenceValue.js' +import {durationToken} from './durationToken.js' +import {cubicBezierToken} from './cubicBezierToken.js' +import {tokenType} from './tokenType.js' + +export const transitionToken = baseToken + .extend({ + $value: z.union([ + z.object({ + duration: z.union([durationToken.shape.$value, referenceValue]), + timing: z.union([cubicBezierToken.shape.$value, referenceValue]), + delay: z.union([durationToken.shape.$value, referenceValue]).optional(), + }), + referenceValue, + ]), + $type: tokenType('transition'), + }) + .strict() diff --git a/src/schemas/transitionTokenSchema.test.ts b/src/schemas/transitionTokenSchema.test.ts new file mode 100644 index 000000000..f4fb5dd6a --- /dev/null +++ b/src/schemas/transitionTokenSchema.test.ts @@ -0,0 +1,138 @@ +import {transitionToken} from './transitionToken.js' + +describe('Schema: transitionToken', () => { + const validToken = { + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + delay: '0ms', + }, + $type: 'transition', + $description: 'Standard transition', + } + + it('parses valid transition tokens', () => { + expect(transitionToken.safeParse(validToken).success).toStrictEqual(true) + }) + + it('parses valid transition tokens without delay', () => { + const tokenWithoutDelay = { + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + }, + $type: 'transition', + $description: 'Standard transition without delay', + } + expect(transitionToken.safeParse(tokenWithoutDelay).success).toStrictEqual(true) + }) + + it('parses valid transition tokens with reference values', () => { + const tokenWithReferences = { + $value: { + duration: '{duration.medium}', + timing: '{timing.easeInOut}', + delay: '{duration.small}', + }, + $type: 'transition', + $description: 'Transition with reference values', + } + expect(transitionToken.safeParse(tokenWithReferences).success).toStrictEqual(true) + }) + + it('parses valid transition tokens with direct reference', () => { + const tokenWithDirectReference = { + $value: '{transition.standard}', + $type: 'transition', + $description: 'Transition with direct reference', + } + expect(transitionToken.safeParse(tokenWithDirectReference).success).toStrictEqual(true) + }) + + it('fails on invalid properties', () => { + // additional element + expect( + transitionToken.safeParse({ + ...validToken, + extra: 'value', + }).success, + ).toStrictEqual(false) + // missing value + expect( + transitionToken.safeParse({ + $type: 'transition', + }).success, + ).toStrictEqual(false) + // missing type + expect( + transitionToken.safeParse({ + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + }, + }).success, + ).toStrictEqual(false) + }) + + it('fails on invalid duration value', () => { + expect( + transitionToken.safeParse({ + ...validToken, + $value: { + ...validToken.$value, + duration: 'invalid', + }, + }).success, + ).toStrictEqual(false) + }) + + it('fails on invalid timing value', () => { + expect( + transitionToken.safeParse({ + ...validToken, + $value: { + ...validToken.$value, + timing: [0.4, 0, 0.2], // Missing one value + }, + }).success, + ).toStrictEqual(false) + }) + + it('fails on invalid delay value', () => { + expect( + transitionToken.safeParse({ + ...validToken, + $value: { + ...validToken.$value, + delay: 'invalid', + }, + }).success, + ).toStrictEqual(false) + }) + + it('fails on wrong type', () => { + // invalid string + expect( + transitionToken.safeParse({ + ...validToken, + $type: 'motion', + }).success, + ).toStrictEqual(false) + // undefined + expect( + transitionToken.safeParse({ + ...validToken, + $type: undefined, + }).success, + ).toStrictEqual(false) + // no type + expect( + transitionToken.safeParse({ + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + }, + }).success, + ).toStrictEqual(false) + }) +}) diff --git a/src/schemas/validTokenType.ts b/src/schemas/validTokenType.ts index 155399a43..49636485a 100644 --- a/src/schemas/validTokenType.ts +++ b/src/schemas/validTokenType.ts @@ -14,6 +14,7 @@ const validTypes = [ 'gradient', 'number', 'string', + 'transition', 'custom-viewportRange', ] as const From ce80256af6129610fb9e1e9985822f4859dc4d0a Mon Sep 17 00:00:00 2001 From: Lukas Oppermann Date: Wed, 23 Apr 2025 15:01:07 +0200 Subject: [PATCH 2/4] add transition transformer --- src/filters/index.ts | 1 + src/filters/isTransition.test.ts | 22 ++++ src/filters/isTransition.ts | 10 ++ src/platforms/css.ts | 1 + src/primerStyleDictionary.ts | 3 + src/test-utilities/getMockToken.ts | 4 +- src/transformers/index.ts | 1 + src/transformers/transitionToCss.test.ts | 135 +++++++++++++++++++++++ src/transformers/transitionToCss.ts | 31 ++++++ 9 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 src/filters/isTransition.test.ts create mode 100644 src/filters/isTransition.ts create mode 100644 src/transformers/transitionToCss.test.ts create mode 100644 src/transformers/transitionToCss.ts diff --git a/src/filters/index.ts b/src/filters/index.ts index d94656209..e70c393a7 100644 --- a/src/filters/index.ts +++ b/src/filters/index.ts @@ -12,3 +12,4 @@ export {isNumber} from './isNumber.js' export {isShadow} from './isShadow.js' export {isSource} from './isSource.js' export {isTypography} from './isTypography.js' +export {isTransition} from './isTransition.js' diff --git a/src/filters/isTransition.test.ts b/src/filters/isTransition.test.ts new file mode 100644 index 000000000..8d1429e67 --- /dev/null +++ b/src/filters/isTransition.test.ts @@ -0,0 +1,22 @@ +import {getMockToken} from '../test-utilities/index.js' +import {isTransition} from './isTransition.js' + +describe('Filter: isTransition', () => { + it('returns true if $type property is `transition`', () => { + expect(isTransition(getMockToken({$type: 'transition'}))).toStrictEqual(true) + }) + + it('returns false if $type property is not `transition`', () => { + expect(isTransition(getMockToken({$type: 'pumpkin'}))).toStrictEqual(false) + }) + + it('returns false if $type property is missing', () => { + expect(isTransition(getMockToken({alpha: 0.4}))).toStrictEqual(false) + }) + + it('returns false if $type property is falsy', () => { + expect(isTransition(getMockToken({$type: false}))).toStrictEqual(false) + expect(isTransition(getMockToken({$type: undefined}))).toStrictEqual(false) + expect(isTransition(getMockToken({$type: null}))).toStrictEqual(false) + }) +}) diff --git a/src/filters/isTransition.ts b/src/filters/isTransition.ts new file mode 100644 index 000000000..a7351b47b --- /dev/null +++ b/src/filters/isTransition.ts @@ -0,0 +1,10 @@ +import type {TransformedToken} from 'style-dictionary/types' + +/** + * @description Checks if token is of $type `transition` + * @param token [TransformedToken](https://github.com/amzn/style-dictionary/blob/main/types/TransformedToken.d.ts) + * @returns boolean + */ +export const isTransition = (token: TransformedToken): boolean => { + return token.$type === 'transition' +} diff --git a/src/platforms/css.ts b/src/platforms/css.ts index 14f61654c..4fd5e39e5 100644 --- a/src/platforms/css.ts +++ b/src/platforms/css.ts @@ -35,6 +35,7 @@ export const css: PlatformInitializer = (outputFile, prefix, buildPath, options) 'shadow/css', 'border/css', 'typography/css', + 'transition/css', 'fontFamily/css', 'fontWeight/number', 'gradient/css', diff --git a/src/primerStyleDictionary.ts b/src/primerStyleDictionary.ts index 35af51de3..e6dbaabda 100644 --- a/src/primerStyleDictionary.ts +++ b/src/primerStyleDictionary.ts @@ -25,6 +25,7 @@ import { dimensionToRemPxArray, floatToPixel, floatToPixelUnitless, + transitionToCss, } from './transformers/index.js' import { javascriptCommonJs, @@ -152,6 +153,8 @@ PrimerStyleDictionary.registerTransform(borderToCss) PrimerStyleDictionary.registerTransform(typographyToCss) +PrimerStyleDictionary.registerTransform(transitionToCss) + PrimerStyleDictionary.registerTransform(fontWeightToNumber) PrimerStyleDictionary.registerTransform(fontFamilyToCss) diff --git a/src/test-utilities/getMockToken.ts b/src/test-utilities/getMockToken.ts index 885e29fd2..18a14df85 100644 --- a/src/test-utilities/getMockToken.ts +++ b/src/test-utilities/getMockToken.ts @@ -36,9 +36,9 @@ export const getMockToken = ( [key: keyof TransformedToken]: unknown }, options?: getMockTokenOptions, -) => { +): TransformedToken => { return { ...removeProps(mockTokenDefaults, options?.remove), ...valueOverrides, - } + } as TransformedToken } diff --git a/src/transformers/index.ts b/src/transformers/index.ts index 0490bfc28..f84f2cfc6 100644 --- a/src/transformers/index.ts +++ b/src/transformers/index.ts @@ -23,3 +23,4 @@ export {namePathToKebabCase} from './namePathToKebabCase.js' export {namePathToSlashNotation} from './namePathToSlashNotation.js' export {shadowToCss} from './shadowToCss.js' export {typographyToCss} from './typographyToCss.js' +export {transitionToCss} from './transitionToCss.js' diff --git a/src/transformers/transitionToCss.test.ts b/src/transformers/transitionToCss.test.ts new file mode 100644 index 000000000..52da0d404 --- /dev/null +++ b/src/transformers/transitionToCss.test.ts @@ -0,0 +1,135 @@ +import {getMockToken} from '../test-utilities/getMockToken.js' +import {transitionToCss} from './transitionToCss.js' + +describe('transitionToCss', () => { + it('should transform basic transition token to CSS', () => { + const token = getMockToken({ + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + }, + $type: 'transition', + }) + + expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1)') + }) + + it('should transform transition token with delay to CSS', () => { + const token = getMockToken({ + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + delay: '100ms', + }, + $type: 'transition', + }) + + expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1) 100ms') + }) + + it('should transform transition token with resolved references', () => { + const token = { + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + delay: '0ms', + }, + $type: 'transition', + } + const resolvedRefs = { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + delay: '0ms', + } + + expect(transitionToCss.transform(token, resolvedRefs)).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms') + }) + + it('should transform transition token with mixed values and references', () => { + const token = getMockToken({ + $value: { + duration: '{motion.duration.base.value}', + timing: [0.4, 0, 0.2, 1], + delay: '0ms', + }, + $type: 'transition', + }) + + expect(transitionToCss.transform(token, resolvedRefs)).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms') + }) + + it('should handle common timing function aliases', () => { + const token = { + $value: { + duration: '300ms', + timing: [0.4, 0, 1, 1], + }, + $type: 'transition', + } + + expect(transitionToCss.transform(token, {}, {})).toBe('300ms ease-out') + }) + + it('should handle direct reference token', () => { + const token = { + $value: '{motion.transition.standard}', + $type: 'transition', + } + const resolvedRefs = { + 'motion.transition.standard': { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + delay: '0ms', + }, + } + + expect(transitionToCss.transform(token, resolvedRefs)).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms') + }) + + it('should handle missing optional delay', () => { + const token = { + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + }, + $type: 'transition', + } + + expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1)') + }) + + it('should throw error for invalid token type', () => { + const token = { + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2, 1], + }, + $type: 'invalid', + } + + expect(() => transitionToCss.transform(token, {}, {})).toThrow('Invalid token type: invalid') + }) + + it('should throw error for missing required properties', () => { + const token = { + $value: { + duration: '300ms', + }, + $type: 'transition', + } + // @ts-expect-error: testing invalid token + expect(() => transitionToCss.transform(token, {}, {})).toThrow('Missing required property: timing') + }) + + it('should throw error for invalid timing value', () => { + const token = { + $value: { + duration: '300ms', + timing: [0.4, 0, 0.2], // Missing one value + }, + $type: 'transition', + } + + expect(() => transitionToCss.transform(token, {}, {})).toThrow('Invalid timing value') + }) +}) diff --git a/src/transformers/transitionToCss.ts b/src/transformers/transitionToCss.ts new file mode 100644 index 000000000..5ea752876 --- /dev/null +++ b/src/transformers/transitionToCss.ts @@ -0,0 +1,31 @@ +import {isTransition} from '../filters/index.js' +import {checkRequiredTokenProperties} from './utilities/checkRequiredTokenProperties.js' +import {getTokenValue} from './utilities/getTokenValue.js' +import type {Transform, TransformedToken} from 'style-dictionary/types' + +/** + * @description converts transition tokens to CSS transition string + * @type value transformer — [StyleDictionary.ValueTransform](https://github.com/amzn/style-dictionary/blob/main/types/Transform.d.ts) + * @matcher matches all tokens of $type `transition` + * @transformer returns css transition `string` + */ +export const transitionToCss: Transform = { + name: 'transition/css', + type: 'value', + transitive: true, + filter: isTransition, + transform: (token: TransformedToken) => { + // extract value + const value = getTokenValue(token) + + // if value is a string, it's probably a reference, return as is + if (typeof value === 'string') { + return value + } + + // check required properties + checkRequiredTokenProperties(value, ['duration', 'timingFunction']) + + return `${value.duration} ${value.timingFunction} ${value.delay ? value.delay : ''}`.trim() + }, +} From 021eeb2601307ed2f7c8573ac9bb6e9db6312b67 Mon Sep 17 00:00:00 2001 From: Lukas Oppermann Date: Wed, 23 Apr 2025 15:47:56 +0200 Subject: [PATCH 3/4] fix schema --- src/schemas/transitionToken.ts | 2 +- src/schemas/transitionTokenSchema.test.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/schemas/transitionToken.ts b/src/schemas/transitionToken.ts index e9e092e67..81b355cc5 100644 --- a/src/schemas/transitionToken.ts +++ b/src/schemas/transitionToken.ts @@ -10,7 +10,7 @@ export const transitionToken = baseToken $value: z.union([ z.object({ duration: z.union([durationToken.shape.$value, referenceValue]), - timing: z.union([cubicBezierToken.shape.$value, referenceValue]), + timingFunction: z.union([cubicBezierToken.shape.$value, referenceValue]), delay: z.union([durationToken.shape.$value, referenceValue]).optional(), }), referenceValue, diff --git a/src/schemas/transitionTokenSchema.test.ts b/src/schemas/transitionTokenSchema.test.ts index f4fb5dd6a..92d67d1d5 100644 --- a/src/schemas/transitionTokenSchema.test.ts +++ b/src/schemas/transitionTokenSchema.test.ts @@ -4,7 +4,7 @@ describe('Schema: transitionToken', () => { const validToken = { $value: { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], delay: '0ms', }, $type: 'transition', @@ -19,7 +19,7 @@ describe('Schema: transitionToken', () => { const tokenWithoutDelay = { $value: { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], }, $type: 'transition', $description: 'Standard transition without delay', @@ -31,7 +31,7 @@ describe('Schema: transitionToken', () => { const tokenWithReferences = { $value: { duration: '{duration.medium}', - timing: '{timing.easeInOut}', + timingFunction: '{timing.easeInOut}', delay: '{duration.small}', }, $type: 'transition', @@ -68,7 +68,7 @@ describe('Schema: transitionToken', () => { transitionToken.safeParse({ $value: { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], }, }).success, ).toStrictEqual(false) @@ -92,7 +92,7 @@ describe('Schema: transitionToken', () => { ...validToken, $value: { ...validToken.$value, - timing: [0.4, 0, 0.2], // Missing one value + timingFunction: [0.4, 0, 0.2], // Missing one value }, }).success, ).toStrictEqual(false) @@ -130,7 +130,7 @@ describe('Schema: transitionToken', () => { transitionToken.safeParse({ $value: { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], }, }).success, ).toStrictEqual(false) From bf4f95a2cf415dd31d47a61ce050ea346860a01f Mon Sep 17 00:00:00 2001 From: Lukas Oppermann Date: Thu, 24 Apr 2025 10:05:00 +0200 Subject: [PATCH 4/4] fix transformer --- src/transformers/cubicBezierToCss.ts | 20 +++-- src/transformers/transitionToCss.test.ts | 94 ++++++------------------ src/transformers/transitionToCss.ts | 3 +- 3 files changed, 36 insertions(+), 81 deletions(-) diff --git a/src/transformers/cubicBezierToCss.ts b/src/transformers/cubicBezierToCss.ts index c80343168..c7a0544a1 100644 --- a/src/transformers/cubicBezierToCss.ts +++ b/src/transformers/cubicBezierToCss.ts @@ -14,13 +14,17 @@ export const cubicBezierToCss: Transform = { filter: isCubicBezier, transform: (token: TransformedToken, _config: PlatformConfig) => { const value = token.$value ?? token.value - // throw value of more or less than 4 items in array - if (value.length !== 4 || value.some((item: unknown) => typeof item !== 'number')) { - throw new Error( - `Invalid cubicBezier token ${token.path.join('.')}, must be an array with 4 numbers, but got this instead: ${JSON.stringify(value)}`, - ) - } - // return value - return `cubic-bezier(${value.join(',')})` + return cubicBezierArrayToCss(value, token.path) }, } + +export const cubicBezierArrayToCss = (value: number[], path: string[]) => { + // throw value of more or less than 4 items in array + if (value.length !== 4 || value.some((item: unknown) => typeof item !== 'number')) { + throw new Error( + `Invalid cubicBezier token ${path.join('.')}, must be an array with 4 numbers, but got this instead: ${JSON.stringify(value)}`, + ) + } + // return value + return `cubic-bezier(${value.join(',')})` +} diff --git a/src/transformers/transitionToCss.test.ts b/src/transformers/transitionToCss.test.ts index 52da0d404..2e197887d 100644 --- a/src/transformers/transitionToCss.test.ts +++ b/src/transformers/transitionToCss.test.ts @@ -6,130 +6,80 @@ describe('transitionToCss', () => { const token = getMockToken({ $value: { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], }, $type: 'transition', }) - expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1)') + expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4,0,0.2,1)') }) it('should transform transition token with delay to CSS', () => { const token = getMockToken({ $value: { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], delay: '100ms', }, $type: 'transition', }) - expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1) 100ms') + expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4,0,0.2,1) 100ms') }) it('should transform transition token with resolved references', () => { const token = { $value: { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], delay: '0ms', }, $type: 'transition', } const resolvedRefs = { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], delay: '0ms', } - expect(transitionToCss.transform(token, resolvedRefs)).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms') - }) - - it('should transform transition token with mixed values and references', () => { - const token = getMockToken({ - $value: { - duration: '{motion.duration.base.value}', - timing: [0.4, 0, 0.2, 1], - delay: '0ms', - }, - $type: 'transition', - }) - - expect(transitionToCss.transform(token, resolvedRefs)).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms') - }) - - it('should handle common timing function aliases', () => { - const token = { - $value: { - duration: '300ms', - timing: [0.4, 0, 1, 1], - }, - $type: 'transition', - } - - expect(transitionToCss.transform(token, {}, {})).toBe('300ms ease-out') - }) - - it('should handle direct reference token', () => { - const token = { - $value: '{motion.transition.standard}', - $type: 'transition', - } - const resolvedRefs = { - 'motion.transition.standard': { - duration: '300ms', - timing: [0.4, 0, 0.2, 1], - delay: '0ms', - }, - } - - expect(transitionToCss.transform(token, resolvedRefs)).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms') + expect(transitionToCss.transform(token, resolvedRefs)).toBe('300ms cubic-bezier(0.4,0,0.2,1) 0ms') }) it('should handle missing optional delay', () => { - const token = { + const token = getMockToken({ $value: { duration: '300ms', - timing: [0.4, 0, 0.2, 1], + timingFunction: [0.4, 0, 0.2, 1], }, $type: 'transition', - } - - expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4, 0, 0.2, 1)') - }) - - it('should throw error for invalid token type', () => { - const token = { - $value: { - duration: '300ms', - timing: [0.4, 0, 0.2, 1], - }, - $type: 'invalid', - } + }) - expect(() => transitionToCss.transform(token, {}, {})).toThrow('Invalid token type: invalid') + expect(transitionToCss.transform(token, {}, {})).toBe('300ms cubic-bezier(0.4,0,0.2,1)') }) it('should throw error for missing required properties', () => { - const token = { + const token = getMockToken({ $value: { duration: '300ms', }, $type: 'transition', - } - // @ts-expect-error: testing invalid token - expect(() => transitionToCss.transform(token, {}, {})).toThrow('Missing required property: timing') + }) + expect(() => transitionToCss.transform(token, {}, {})).toThrow( + 'Missing property: timingFunction on token with value {"duration":"300ms"}', + ) }) it('should throw error for invalid timing value', () => { - const token = { + const token = getMockToken({ $value: { duration: '300ms', - timing: [0.4, 0, 0.2], // Missing one value + timingFunction: [0.4, 0, 0.2], // Missing one value }, $type: 'transition', - } + }) - expect(() => transitionToCss.transform(token, {}, {})).toThrow('Invalid timing value') + expect(() => transitionToCss.transform(token, {}, {})).toThrow( + 'Invalid cubicBezier token path, must be an array with 4 numbers, but got this instead: [0.4,0,0.2]', + ) }) }) diff --git a/src/transformers/transitionToCss.ts b/src/transformers/transitionToCss.ts index 5ea752876..1fd807160 100644 --- a/src/transformers/transitionToCss.ts +++ b/src/transformers/transitionToCss.ts @@ -1,4 +1,5 @@ import {isTransition} from '../filters/index.js' +import {cubicBezierArrayToCss} from './cubicBezierToCss.js' import {checkRequiredTokenProperties} from './utilities/checkRequiredTokenProperties.js' import {getTokenValue} from './utilities/getTokenValue.js' import type {Transform, TransformedToken} from 'style-dictionary/types' @@ -26,6 +27,6 @@ export const transitionToCss: Transform = { // check required properties checkRequiredTokenProperties(value, ['duration', 'timingFunction']) - return `${value.duration} ${value.timingFunction} ${value.delay ? value.delay : ''}`.trim() + return `${value.duration} ${cubicBezierArrayToCss(value.timingFunction, token.path)} ${value.delay ? value.delay : ''}`.trim() }, }