From 132755977403d244e08913fd4af469317cd8bb0b Mon Sep 17 00:00:00 2001 From: Dru Hill Date: Thu, 15 Jun 2023 11:57:55 +0100 Subject: [PATCH] feat: add 'ts/color/modifiers format option --- .changeset/chatty-pumpkins-cross.md | 5 ++ README.md | 23 +++-- src/TransformOptions.ts | 7 ++ .../transformColorModifiers.ts | 9 +- src/registerTransforms.ts | 2 +- test/integration/sd-transforms.test.ts | 3 +- .../tokens/sd-transforms.tokens.json | 6 +- .../transformColorModifiers.spec.ts | 89 +++++++++++++++++++ 8 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 .changeset/chatty-pumpkins-cross.md diff --git a/.changeset/chatty-pumpkins-cross.md b/.changeset/chatty-pumpkins-cross.md new file mode 100644 index 0000000..8b4984a --- /dev/null +++ b/.changeset/chatty-pumpkins-cross.md @@ -0,0 +1,5 @@ +--- +'@tokens-studio/sd-transforms': patch +--- + +Add 'ts/color/modifiers' option: `format`, to globally set the output format for color modifiers. diff --git a/README.md b/README.md index 6077cb8..02fc5b5 100644 --- a/README.md +++ b/README.md @@ -140,20 +140,25 @@ registerTransforms({ shadow: false, }, excludeParentKeys: true, + 'ts/color/modifiers': { + format: 'hex', + }, }); ``` Options: -| name | type | required | default | description | -| ------------------ | ------------------------ | -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -| excludeParentKeys | boolean | ❌ | `false` | Whether or not to exclude parent keys from your token files | -| expand | boolean \| ExpandOptions | ❌ | See props below | `false` to not register the parser at all. By default, expands composition tokens. Optionally, border, shadow and typography as well. | -| expand.composition | boolean \| ExpandFilter | ❌ | `true` | Whether or not to expand compositions. Also allows a filter callback function to conditionally expand per token/filePath | -| expand.typography | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand typography. Also allows a filter callback function to conditionally expand per token/filePath | -| expand.shadow | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand shadows. Also allows a filter callback function to conditionally expand per token/filePath | -| expand.border | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand borders. Also allows a filter callback function to conditionally expand per token/filePath | -| | +| name | type | required | default | description | +| ----------------------------- | ------------------------ | -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| excludeParentKeys | boolean | ❌ | `false` | Whether or not to exclude parent keys from your token files | +| expand | boolean \| ExpandOptions | ❌ | See props below | `false` to not register the parser at all. By default, expands composition tokens. Optionally, border, shadow and typography as well. | +| expand.composition | boolean \| ExpandFilter | ❌ | `true` | Whether or not to expand compositions. Also allows a filter callback function to conditionally expand per token/filePath | +| expand.typography | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand typography. Also allows a filter callback function to conditionally expand per token/filePath | +| expand.shadow | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand shadows. Also allows a filter callback function to conditionally expand per token/filePath | +| expand.border | boolean \| ExpandFilter | ❌ | `false` | Whether or not to expand borders. Also allows a filter callback function to conditionally expand per token/filePath | +| ['ts/color/modifiers'] | ColorModifierOptions | ❌ | See props below | Color modifier options | +| ['ts/color/modifiers'].format | ColorModifierFormat | ❌ | `undefined` | Color modifier output format override ('hex' \| 'hsl' \| 'lch' \| 'p3' \| 'srgb'), uses local format or modifier space as default | +| | > Note: you can also import and use the `expandComposites` function to run the expansion on your token object manually. > Handy if you have your own parsers set up (e.g. for JS files), and you want the expansions to work there too. diff --git a/src/TransformOptions.ts b/src/TransformOptions.ts index ad65b2f..216b904 100644 --- a/src/TransformOptions.ts +++ b/src/TransformOptions.ts @@ -24,7 +24,14 @@ export interface ExpandOptions { composition?: boolean | ExpandFilter; // default true } +export type ColorModifierFormat = 'hex' | 'hsl' | 'lch' | 'p3' | 'srgb'; + +export interface ColorModifierOptions { + format: ColorModifierFormat; +} + export interface TransformOptions { expand?: ExpandOptions | false; excludeParentKeys?: boolean; + ['ts/color/modifiers']?: ColorModifierOptions; } diff --git a/src/color-modifiers/transformColorModifiers.ts b/src/color-modifiers/transformColorModifiers.ts index 7a4eadf..ebb596b 100644 --- a/src/color-modifiers/transformColorModifiers.ts +++ b/src/color-modifiers/transformColorModifiers.ts @@ -1,10 +1,17 @@ import { DesignToken } from 'style-dictionary'; import { modifyColor } from './modifyColor.js'; import { ColorModifier } from '@tokens-studio/types'; +import { ColorModifierOptions } from '../TransformOptions.js'; /** * Helper: Transforms color tokens with tokens studio color modifiers */ -export function transformColorModifiers(token: DesignToken): string | undefined { +export function transformColorModifiers( + token: DesignToken, + options?: ColorModifierOptions, +): string | undefined { const modifier = token.$extensions['studio.tokens']?.modify as ColorModifier; + if (options?.format) { + modifier.format = options.format; + } return modifyColor(token.value, modifier); } diff --git a/src/registerTransforms.ts b/src/registerTransforms.ts index 6cad28c..4e53364 100644 --- a/src/registerTransforms.ts +++ b/src/registerTransforms.ts @@ -187,7 +187,7 @@ export async function registerTransforms(sd: Core, transformOpts?: TransformOpti transitive: true, matcher: token => token.type === 'color' && token.$extensions && token.$extensions['studio.tokens']?.modify, - transformer: token => transformColorModifiers(token), + transformer: token => transformColorModifiers(token, transformOpts?.['ts/color/modifiers']), }); _sd.registerTransformGroup({ diff --git a/test/integration/sd-transforms.test.ts b/test/integration/sd-transforms.test.ts index 3f69b48..fad6d29 100644 --- a/test/integration/sd-transforms.test.ts +++ b/test/integration/sd-transforms.test.ts @@ -32,7 +32,7 @@ describe('sd-transforms smoke tests', () => { if (dict) { cleanup(dict); } - dict = init(cfg); + dict = init(cfg, { 'ts/color/modifiers': { format: 'hex' } }); }); afterEach(() => { @@ -43,7 +43,6 @@ describe('sd-transforms smoke tests', () => { it('supports tokens-studio tokens', async () => { const file = await promises.readFile(outputFilePath, 'utf-8'); - console.log(file); expect(file).to.include(`:root { --sdDimensionScale: 2; --sdDimensionXs: 4px; diff --git a/test/integration/tokens/sd-transforms.tokens.json b/test/integration/tokens/sd-transforms.tokens.json index b8aa549..70c9d4b 100644 --- a/test/integration/tokens/sd-transforms.tokens.json +++ b/test/integration/tokens/sd-transforms.tokens.json @@ -70,8 +70,7 @@ "modify": { "type": "lighten", "value": "0.1", - "space": "srgb", - "format": "hex" + "space": "srgb" } } } @@ -88,8 +87,7 @@ "modify": { "type": "darken", "value": "0.1", - "space": "srgb", - "format": "hex" + "space": "srgb" } } } diff --git a/test/spec/color-modifiers/transformColorModifiers.spec.ts b/test/spec/color-modifiers/transformColorModifiers.spec.ts index 34f7f70..7b52831 100644 --- a/test/spec/color-modifiers/transformColorModifiers.spec.ts +++ b/test/spec/color-modifiers/transformColorModifiers.spec.ts @@ -184,6 +184,95 @@ describe('transform color modifiers', () => { expect(transformColorModifiers(token('hex'))).to.equal('#9a3535'); }); + it('allows passing an output format as an option', () => { + const token = (space: ColorSpaceTypes | '') => ({ + value: '#C14242', + type: 'color', + $extensions: { + 'studio.tokens': { + modify: { + type: 'darken', + value: '0.2', + space, + }, + }, + }, + }); + + // uses hex override for output format + expect(transformColorModifiers(token(ColorSpaceTypes.HSL), { format: 'hex' })).to.equal( + '#9c3333', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.LCH), { format: 'hex' })).to.equal( + '#983735', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.P3), { format: 'hex' })).to.equal( + '#9b3535', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.SRGB), { format: 'hex' })).to.equal( + '#9a3535', + ); + + // uses hsl override for output format + expect(transformColorModifiers(token(ColorSpaceTypes.HSL), { format: 'hsl' })).to.equal( + 'hsl(0 50.6% 40.6%)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.LCH), { format: 'hsl' })).to.equal( + 'hsl(0.85 47.9% 40.3%)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.P3), { format: 'hsl' })).to.equal( + 'hsl(0.05 49% 40.7%)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.SRGB), { format: 'hsl' })).to.equal( + 'hsl(0 49% 40.6%)', + ); + + // uses lch override for output format + expect(transformColorModifiers(token(ColorSpaceTypes.HSL), { format: 'lch' })).to.equal( + 'lch(37.8 51 29.8)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.LCH), { format: 'lch' })).to.equal( + 'lch(37.7 47.5 29.7)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.P3), { format: 'lch' })).to.equal( + 'lch(37.9 49.4 29.4)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.SRGB), { format: 'lch' })).to.equal( + 'lch(37.8 49.4 29.4)', + ); + + // uses p3 override for output format + expect(transformColorModifiers(token(ColorSpaceTypes.HSL), { format: 'p3' })).to.equal( + 'color(display-p3 0.57 0.23 0.22)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.LCH), { format: 'p3' })).to.equal( + 'color(display-p3 0.55 0.24 0.22)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.P3), { format: 'p3' })).to.equal( + 'color(display-p3 0.56 0.24 0.22)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.SRGB), { format: 'p3' })).to.equal( + 'color(display-p3 0.56 0.23 0.22)', + ); + + // uses srgb override for output format + expect(transformColorModifiers(token(ColorSpaceTypes.HSL), { format: 'srgb' })).to.equal( + 'rgb(61.2% 20.1% 20.1%)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.LCH), { format: 'srgb' })).to.equal( + 'rgb(59.5% 21.5% 21%)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.P3), { format: 'srgb' })).to.equal( + 'rgb(60.6% 20.8% 20.7%)', + ); + expect(transformColorModifiers(token(ColorSpaceTypes.SRGB), { format: 'srgb' })).to.equal( + 'rgb(60.5% 20.7% 20.7%)', + ); + + // without space, return original + expect(transformColorModifiers(token(''))).to.equal('#C14242'); + }); + it('can convert from a non-srgb space to srgb space to then format it as a hex color (which is fundamentally rgb)', () => { const token = { value: '#C14242',