Skip to content

Commit

Permalink
Feat(exporter-tokens): Use variable prefix from tokens
Browse files Browse the repository at this point in the history
- Use variable prefix from tokens for gradient and shadow color variables
  • Loading branch information
pavelklibani committed Oct 30, 2024
1 parent a127a3a commit f0fec13
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 63 deletions.
56 changes: 28 additions & 28 deletions exporters/tokens/generated/exporter.cjs

Large diffs are not rendered by default.

91 changes: 77 additions & 14 deletions exporters/tokens/src/generators/__tests__/stylesGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { exampleColorsTokens } from '../../../tests/fixtures/exampleColorTokens'
import { exampleDimensionAndStringTokens } from '../../../tests/fixtures/exampleDimensionAndStringTokens';
import { exampleGradientTokens } from '../../../tests/fixtures/exampleGradientTokens';
import { exampleGroups } from '../../../tests/fixtures/exampleGroups';
import { examplePrefixToken } from '../../../tests/fixtures/examplePrefixToken';
import { exampleShadowTokens } from '../../../tests/fixtures/exampleShadowTokens';
import { findTokenPrefix } from '../../helpers/findTokenPrefix';
import { generateStylesFromTokens, tokenToStyleByType } from '../stylesGenerator';

const mappedTokens: Map<string, Token> = new Map([]);
Expand All @@ -25,27 +27,31 @@ describe('stylesGenerator', () => {
description: 'dimension type token with parent prefix',
hasParentPrefix: true,
hasJsOutput: false,
hasTokenPrefix: true,
expectedStyles: '$grid-spacing-desktop: 32px !default;',
},
{
token: exampleDimensionAndStringTokens.get('dimensionRef') as DimensionToken,
description: 'dimension type token without parent prefix',
hasParentPrefix: false,
hasJsOutput: false,
hasTokenPrefix: true,
expectedStyles: '$desktop: 32px !default;',
},
{
token: exampleDimensionAndStringTokens.get('stringRef') as StringToken,
description: 'string type token with parent prefix',
hasParentPrefix: true,
hasJsOutput: false,
hasTokenPrefix: true,
expectedStyles: '$grid-columns: 12 !default;',
},
{
token: exampleDimensionAndStringTokens.get('stringRef') as StringToken,
description: 'string type token without parent prefix',
hasParentPrefix: false,
hasJsOutput: false,
hasTokenPrefix: false,
expectedStyles: '$columns: 12 !default;',
},
{
Expand All @@ -57,57 +63,83 @@ describe('stylesGenerator', () => {
description: 'unsupported token type',
hasParentPrefix: true,
hasJsOutput: false,
hasTokenPrefix: false,
expectedStyles: null,
},
{
token: exampleDimensionAndStringTokens.get('dimensionRef') as DimensionToken,
description: 'dimension type token with parent prefix and js output',
hasParentPrefix: true,
hasJsOutput: true,
hasTokenPrefix: false,
expectedStyles: "export const gridSpacingDesktop = '32px';",
},
{
token: exampleDimensionAndStringTokens.get('dimensionRef') as DimensionToken,
description: 'dimension type token without parent prefix and js output',
hasParentPrefix: false,
hasJsOutput: true,
hasTokenPrefix: false,
expectedStyles: "export const desktop = '32px';",
},
{
token: exampleDimensionAndStringTokens.get('stringRef') as StringToken,
description: 'string type token with parent prefix and js output',
hasParentPrefix: true,
hasJsOutput: true,
hasTokenPrefix: false,
expectedStyles: "export const gridColumns = '12';",
},
{
token: exampleDimensionAndStringTokens.get('stringRef') as StringToken,
description: 'string type token without parent prefix and js output',
hasParentPrefix: false,
hasJsOutput: true,
hasTokenPrefix: false,
expectedStyles: "export const columns = '12';",
},
{
token: exampleShadowTokens.get('shadowRef') as ShadowToken,
description: 'shadow type token without parent prefix',
hasParentPrefix: false,
hasJsOutput: false,
hasTokenPrefix: true,
expectedStyles: '$shadow-100: 0 2px 8px 0 var(--spirit-color-shadows-shadow-100-color-01, #00000026) !default;',
},
{
token: exampleShadowTokens.get('shadowRef') as ShadowToken,
description: 'shadow type token without parent prefix',
hasParentPrefix: false,
hasJsOutput: false,
hasTokenPrefix: false,
expectedStyles: '$shadow-100: 0 2px 8px 0 var(--color-shadows-shadow-100-color-01, #00000026) !default;',
},
{
token: exampleGradientTokens.get('gradientRef') as GradientToken,
description: 'gradient type token without parent prefix',
hasParentPrefix: false,
hasJsOutput: false,
hasTokenPrefix: true,
expectedStyles:
'$basic-overlay: linear-gradient(var(--gradient-angle, 90deg), var(--spirit-color-gradient-basic-overlay-color-01, #fff) 0%, var(--spirit-color-gradient-basic-overlay-color-02, #fff0) 100%) !default;',
},
{
token: exampleGradientTokens.get('gradientRef') as GradientToken,
description: 'gradient type token without parent prefix',
hasParentPrefix: false,
hasJsOutput: false,
hasTokenPrefix: false,
expectedStyles:
'$basic-overlay: linear-gradient(var(--gradient-angle, 90deg), var(--color-gradient-basic-overlay-color-01, #fff) 0%, var(--color-gradient-basic-overlay-color-02, #fff0) 100%) !default;',
},
];

it.each(dataProvider)(
'should correctly generate styles for $description',
({ token, expectedStyles, hasParentPrefix, hasJsOutput }) => {
const styles = tokenToStyleByType(token, mappedTokens, tokenGroups, hasParentPrefix, hasJsOutput);
({ token, expectedStyles, hasParentPrefix, hasTokenPrefix, hasJsOutput }) => {
const prefixTokens = Array.from(examplePrefixToken.values());
const tokenPrefix = hasTokenPrefix ? findTokenPrefix(prefixTokens) : '';
const styles = tokenToStyleByType(token, mappedTokens, tokenGroups, tokenPrefix, hasParentPrefix, hasJsOutput);

expect(styles).toBe(expectedStyles);
},
Expand All @@ -121,6 +153,7 @@ describe('stylesGenerator', () => {
groupName: 'Grid',
hasJsOutput: false,
hasParentPrefix: true,
hasTokenPrefix: false,
description: 'should generate styles from tokens',
expectedStyles: '$grid-columns: 12 !default;\n\n$grid-spacing-desktop: 32px !default;',
},
Expand All @@ -129,6 +162,7 @@ describe('stylesGenerator', () => {
groupName: 'Grid',
hasJsOutput: true,
hasParentPrefix: true,
hasTokenPrefix: false,
description: 'should generate styles from tokens with js output',
expectedStyles: "export const gridColumns = '12';\n\nexport const gridSpacingDesktop = '32px';",
},
Expand All @@ -137,6 +171,7 @@ describe('stylesGenerator', () => {
groupName: '',
hasJsOutput: false,
hasParentPrefix: false,
hasTokenPrefix: false,
description: 'should generate styles from tokens with colors',
expectedStyles: '$active: #ca2026 !default;\n\n$primary: #fff !default;',
},
Expand All @@ -145,6 +180,7 @@ describe('stylesGenerator', () => {
groupName: '',
hasJsOutput: true,
hasParentPrefix: false,
hasTokenPrefix: false,
description: 'should generate styles from tokens with colors with js output',
expectedStyles: "export const active = '#ca2026';\n\nexport const primary = '#fff';",
},
Expand All @@ -153,32 +189,59 @@ describe('stylesGenerator', () => {
groupName: '',
hasJsOutput: false,
hasParentPrefix: false,
hasTokenPrefix: true,
description: 'should generate styles from tokens with shadows',
expectedStyles: '$shadow-100: 0 2px 8px 0 var(--spirit-color-shadows-shadow-100-color-01, #00000026) !default;',
},
{
tokens: exampleShadowTokens,
groupName: '',
hasJsOutput: false,
hasParentPrefix: false,
hasTokenPrefix: false,
description: 'should generate styles from tokens with shadows',
expectedStyles: '$shadow-100: 0 2px 8px 0 var(--color-shadows-shadow-100-color-01, #00000026) !default;',
},
{
tokens: exampleGradientTokens,
groupName: '',
hasJsOutput: false,
hasParentPrefix: false,
hasTokenPrefix: true,
description: 'should generate styles from tokens with gradients',
expectedStyles:
'$basic-overlay: linear-gradient(var(--gradient-angle, 90deg), var(--spirit-color-gradient-basic-overlay-color-01, #fff) 0%, var(--spirit-color-gradient-basic-overlay-color-02, #fff0) 100%) !default;',
},
{
tokens: exampleGradientTokens,
groupName: '',
hasJsOutput: false,
hasParentPrefix: false,
hasTokenPrefix: false,
description: 'should generate styles from tokens with gradients',
expectedStyles:
'$basic-overlay: linear-gradient(var(--gradient-angle, 90deg), var(--color-gradient-basic-overlay-color-01, #fff) 0%, var(--color-gradient-basic-overlay-color-02, #fff0) 100%) !default;',
},
];

it.each(dataProvider)('$description', ({ tokens, groupName, hasJsOutput, hasParentPrefix, expectedStyles }) => {
const styles = generateStylesFromTokens(
Array.from(tokens.values()),
mappedTokens,
tokenGroups,
groupName,
hasParentPrefix,
false,
hasJsOutput,
);
it.each(dataProvider)(
'$description',
({ tokens, groupName, hasJsOutput, hasParentPrefix, hasTokenPrefix, expectedStyles }) => {
const prefixTokens = Array.from(examplePrefixToken.values());
const tokenPrefix = hasTokenPrefix ? findTokenPrefix(prefixTokens) : '';
const styles = generateStylesFromTokens(
Array.from(tokens.values()),
mappedTokens,
tokenGroups,
tokenPrefix,
groupName,
hasParentPrefix,
false,
hasJsOutput,
);

expect(styles).toBe(expectedStyles);
});
expect(styles).toBe(expectedStyles);
},
);
});
});
3 changes: 3 additions & 0 deletions exporters/tokens/src/generators/contentGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { indentAndFormat } from '../formatters/stylesFormatter';
import { convertToJs, convertToScss, deepMergeObjects } from '../helpers/objectHelper';
import { generateStylesFromTokens } from './stylesGenerator';
import { StylesObjectType, generateStylesObjectFromTokens } from './stylesObjectGenerator';
import { findTokenPrefix } from '../helpers/findTokenPrefix';

// Add disclaimer to the top of the content
export const addDisclaimer = (content: string): string => {
Expand Down Expand Up @@ -101,13 +102,15 @@ export const generateFileContent = (

groups.forEach((group) => {
const filteredTokens = filterTokensByTypeAndGroup(tokens, tokenType, group);
const tokenPrefix = findTokenPrefix(tokens);

// Generate css tokens
if (tokenType !== TokenType.typography) {
styledTokens += generateStylesFromTokens(
filteredTokens,
mappedTokens,
tokenGroups,
tokenPrefix,
group,
hasParentPrefix,
sortByNumValue,
Expand Down
8 changes: 5 additions & 3 deletions exporters/tokens/src/generators/stylesGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const tokenToStyleByType = (
token: Token,
mappedTokens: Map<string, Token>,
tokenGroups: Array<TokenGroup>,
tokenPrefix: string,
withParent: boolean,
hasJsOutput: boolean,
): string | null => {
Expand Down Expand Up @@ -82,7 +83,7 @@ export const tokenToStyleByType = (
});
// add group name to the variable if it is not already in the name
const groupName = withParent ? undefined : origin?.name?.split('/')[0].toLowerCase();
shadow = transformColorsToVariables(name, shadow, groupName); // add color variables
shadow = transformColorsToVariables(name, shadow, tokenPrefix, groupName); // add color variables
shadow = findAllHexColorsInStringAndNormalize(shadow); // find hex codes and normalize them

return formatTokenStyleByOutput(name, shadow, hasJsOutput);
Expand All @@ -101,7 +102,7 @@ export const tokenToStyleByType = (
gradient = addAngleVarToGradient(gradient); // add angle variable
// add group name to the variable if it is not already in the name
const groupName = withParent ? undefined : origin?.name?.split('/')[0].toLowerCase();
gradient = transformColorsToVariables(name, gradient, groupName); // add color variables
gradient = transformColorsToVariables(name, gradient, tokenPrefix, groupName); // add color variables
gradient = findAllHexColorsInStringAndNormalize(gradient); // find hex codes and normalize them

return formatTokenStyleByOutput(name, gradient, hasJsOutput);
Expand All @@ -114,6 +115,7 @@ export const generateStylesFromTokens = (
tokens: Token[],
mappedTokens: Map<string, Token>,
tokenGroups: Array<TokenGroup>,
tokenPrefix: string,
group: string,
hasParentPrefix: boolean,
sortByNumValue: boolean,
Expand All @@ -122,7 +124,7 @@ export const generateStylesFromTokens = (
const sortedTokens = sortTokens(tokens, tokenGroups, hasParentPrefix, group, sortByNumValue);

const cssTokens = sortedTokens.map((token) => ({
css: tokenToStyleByType(token, mappedTokens, tokenGroups, hasParentPrefix, hasJsOutput),
css: tokenToStyleByType(token, mappedTokens, tokenGroups, tokenPrefix, hasParentPrefix, hasJsOutput),
parentGroupId: token.parentGroupId,
}));

Expand Down
46 changes: 31 additions & 15 deletions exporters/tokens/src/helpers/__tests__/colorHelper.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { examplePrefixToken } from '../../../tests/fixtures/examplePrefixToken';
import {
canHexBeShortened,
findAllHexColorsInStringAndNormalize,
Expand All @@ -6,6 +7,7 @@ import {
shortenHex,
transformColorsToVariables,
} from '../colorHelper';
import { findTokenPrefix } from '../findTokenPrefix';

const dataProviderItems = [
['ffffffff', '#fff'],
Expand All @@ -20,20 +22,31 @@ const dataProviderItems = [
['00000040', '#00000040'],
];

// [group, name, value, expectedValue]
const dataProviderItemsTransformColorsToVariables = [
[
'shadows',
'shadow-100',
'0px 1px 0px #ffffff, 0px 1px 0px #00000001',
'0 1px 0 var(--spirit-color-shadows-shadow-100-color-01, #ffffff), 0 1px 0 var(--spirit-color-shadows-shadow-100-color-02, #00000001)',
],
[
'',
'shadow-200',
'0px 1px 0px #ffffff, 0px 1px 0px #00000001, 0px 1px 0px #00000002',
'0 1px 0 var(--spirit-color-shadow-200-color-01, #ffffff), 0 1px 0 var(--spirit-color-shadow-200-color-02, #00000001), 0 1px 0 var(--spirit-color-shadow-200-color-03, #00000002)',
],
{
hasTokenPrefix: true,
group: 'shadows',
name: 'shadow-100',
value: '0px 1px 0px #ffffff, 0px 1px 0px #00000001',
expectedValue:
'0 1px 0 var(--spirit-color-shadows-shadow-100-color-01, #ffffff), 0 1px 0 var(--spirit-color-shadows-shadow-100-color-02, #00000001)',
},
{
hasTokenPrefix: true,
group: '',
name: 'shadow-200',
value: '0px 1px 0px #ffffff, 0px 1px 0px #00000001, 0px 1px 0px #00000002',
expectedValue:
'0 1px 0 var(--spirit-color-shadow-200-color-01, #ffffff), 0 1px 0 var(--spirit-color-shadow-200-color-02, #00000001), 0 1px 0 var(--spirit-color-shadow-200-color-03, #00000002)',
},
{
hasTokenPrefix: false,
group: '',
name: 'shadow-200',
value: '0px 1px 0px #ffffff, 0px 1px 0px #00000001, 0px 1px 0px #00000002',
expectedValue:
'0 1px 0 var(--color-shadow-200-color-01, #ffffff), 0 1px 0 var(--color-shadow-200-color-02, #00000001), 0 1px 0 var(--color-shadow-200-color-03, #00000002)',
},
];

describe('colorHelper', () => {
Expand Down Expand Up @@ -83,9 +96,12 @@ describe('colorHelper', () => {

describe.each(dataProviderItemsTransformColorsToVariables)(
'transformColorsToVariables',
(group, name, value, expectedValue) => {
({ hasTokenPrefix, group, name, value, expectedValue }) => {
it('should transform colors to variables', () => {
expect(transformColorsToVariables(name, value, group)).toBe(expectedValue);
const prefixTokens = Array.from(examplePrefixToken.values());
const tokenPrefix = hasTokenPrefix ? findTokenPrefix(prefixTokens) : '';

expect(transformColorsToVariables(name, value, tokenPrefix, group)).toBe(expectedValue);
});
},
);
Expand Down
19 changes: 19 additions & 0 deletions exporters/tokens/src/helpers/__tests__/findTokenPrefix.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Token } from '@supernovaio/sdk-exporters';
import { examplePrefixToken } from '../../../tests/fixtures/examplePrefixToken';
import { findTokenPrefix } from '../findTokenPrefix';

describe('findTokenPrefix', () => {
it('should return token prefix', () => {
const tokens = Array.from(examplePrefixToken.values());
const expectedValue = 'spirit-';

expect(findTokenPrefix(tokens)).toBe(expectedValue);
});

it('should return empty string if token prefix is not found', () => {
const tokens = [] as Token[];
const expectedValue = '';

expect(findTokenPrefix(tokens)).toBe(expectedValue);
});
});
Loading

0 comments on commit f0fec13

Please sign in to comment.