Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(variables-scss): Formatting color tokens and support themes #DS-1461 #1624

Merged
merged 2 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions exporters/variables-scss/generated/exporter.cjs

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions exporters/variables-scss/src/config/fileConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { TokenType } from '@supernovaio/sdk-exporters';

export type FileData = {
fileName: string;
tokenTypes: TokenType[];
groupNames: string[];
withCssObject?: boolean;
hasParentPrefix?: boolean;
sortByNumValue?: boolean;
};

export const nonThemedFilesData: FileData[] = [
{
fileName: '_borders.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Border'],
withCssObject: false,
sortByNumValue: true,
},
{
fileName: '_other.scss',
tokenTypes: [TokenType.dimension, TokenType.string],
groupNames: ['Grid', 'Container', 'Breakpoint'],
},
{
fileName: '_radii.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Radius'],
hasParentPrefix: false,
sortByNumValue: true,
},
{
fileName: '_spacing.scss',
tokenTypes: [TokenType.dimension],
groupNames: ['Spacing'],
hasParentPrefix: false,
sortByNumValue: true,
},
];

export const themedFilesData: FileData[] = [
{
fileName: '_colors.scss',
tokenTypes: [TokenType.color],
groupNames: [''],
},
];

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import fs from 'fs';
import path from 'path';
import { formatCSS } from '../cssFormatter';

const mockedUnformattedCSS = fs.readFileSync(path.join(__dirname, '../__fixtures__/unformattedExample.scss'), 'utf-8');
const mockedUnformattedCSS = fs.readFileSync(
path.join(__dirname, '../../../tests/fixtures/unformattedExample.scss'),
'utf-8',
);

const mockedFormattedCSS = fs.readFileSync(path.join(__dirname, '../__fixtures__/formattedExample.scss'), 'utf-8');
const mockedFormattedCSS = fs.readFileSync(
path.join(__dirname, '../../../tests/fixtures/formattedExample.scss'),
'utf-8',
);

describe('formatCSS', () => {
it('should correctly format CSS string', () => {
Expand Down
3 changes: 2 additions & 1 deletion exporters/variables-scss/src/formatters/cssFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ export const formatCSS = (css: string): string => {

const lines = css.split('\n');

// TODO: Try to replace this functionality with prettier
for (const line of lines) {
if (line.includes('(')) {
formattedCSS += `${IDENTATION.repeat(indentationLevel)}${line}\n`;
indentationLevel += 1;
formattedCSS += `${line}\n`;
} else if (line.includes(')')) {
indentationLevel -= 1;
formattedCSS += `${IDENTATION.repeat(indentationLevel)}${line}\n`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import fs from 'fs';
import path from 'path';
import { Token, TokenGroup, TokenType } from '@supernovaio/sdk-exporters';
import { generateFileContent, addDisclaimer, filterTokensByTypeAndGroup } from '../contentGenerator';
import { exampleMockedGroups, exampleMockedTokens } from '../../formatters/__fixtures__/mockedExampleTokens';
import { exampleMockedGroups, exampleMockedTokens } from '../../../tests/fixtures/mockedExampleTokens';
import { FileData } from '../../config/fileConfig';

const mockedExpectedResult = fs.readFileSync(
path.join(__dirname, '../../formatters/__fixtures__/exampleFileContent.scss'),
path.join(__dirname, '../../../tests/fixtures/exampleFileContent.scss'),
'utf-8',
);
const mappedTokens: Map<string, Token> = new Map([]);
Expand All @@ -15,20 +16,16 @@ describe('contentGenerator', () => {
describe('generateFileContent', () => {
it('should generate file content', () => {
const tokens = Array.from(exampleMockedTokens.values());
const tokenTypes = [TokenType.dimension, TokenType.string];
const groupNames = ['Grid', 'String'];
const withCssObject = true;
const hasParentPrefix = true;
const fileData: FileData = {
fileName: 'testFile',
tokenTypes: [TokenType.dimension, TokenType.string],
groupNames: ['Grid', 'String'],
withCssObject: true,
hasParentPrefix: true,
sortByNumValue: false,
};

const fileContent = generateFileContent(
tokens,
mappedTokens,
tokenGroups,
tokenTypes,
groupNames,
withCssObject,
hasParentPrefix,
);
const fileContent = generateFileContent(tokens, mappedTokens, tokenGroups, fileData);

expect(fileContent).toStrictEqual({ content: mockedExpectedResult });
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DimensionToken, StringToken, Token, TokenGroup, TokenType } from '@supernovaio/sdk-exporters';
import { generateCssFromTokens, tokenToCSSByType } from '../cssGenerator';
import { exampleMockedGroups, exampleMockedTokens } from '../../formatters/__fixtures__/mockedExampleTokens';
import { exampleMockedGroups, exampleMockedTokens } from '../../../tests/fixtures/mockedExampleTokens';

const mappedTokens: Map<string, Token> = new Map([]);
const tokenGroups: Array<TokenGroup> = exampleMockedGroups;
Expand Down Expand Up @@ -34,7 +34,7 @@ const dataProvider = [
token: {
id: '3',
name: 'unsupportedToken',
tokenType: TokenType.color,
tokenType: TokenType.duration,
} as Token,
expectedCss: null,
hasParentPrefix: true,
Expand All @@ -53,9 +53,16 @@ describe('cssGenerator', () => {

describe('generateCssFromTokens', () => {
it('should generate CSS from tokens', () => {
const css = generateCssFromTokens(Array.from(exampleMockedTokens.values()), mappedTokens, tokenGroups, true);
const css = generateCssFromTokens(
Array.from(exampleMockedTokens.values()),
mappedTokens,
tokenGroups,
'Grid',
true,
false,
);

expect(css).toBe('$grid-spacing-desktop: 32px !default;\n$grid-columns: 12 !default;');
expect(css).toBe('$grid-columns: 12 !default;\n\n$grid-spacing-desktop: 32px !default;');
});
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import { Token, TokenGroup } from '@supernovaio/sdk-exporters';
import { generateCssObjectFromTokens, generateObjectContent } from '../cssObjectGenerator';
import { exampleMockedGroups, exampleMockedTokens } from '../../formatters/__fixtures__/mockedExampleTokens';
import { Token, TokenGroup, TokenType } from '@supernovaio/sdk-exporters';
import {
colorGroupsReducer,
createGlobalColorsObject,
createObjectStructureFromTokenNameParts,
generateCssObjectFromTokens,
getTokenAlias,
normalizeFirstNamePart,
parseGroupName,
} from '../cssObjectGenerator';
import {
exampleMockedColorGroups,
exampleMockedColorsTokens,
exampleMockedGroups,
exampleMockedInvariantTokens,
exampleMockedTokens,
} from '../../../tests/fixtures/mockedExampleTokens';

const mappedTokens: Map<string, Token> = new Map([]);
const tokenGroups: Array<TokenGroup> = exampleMockedGroups;
Expand All @@ -15,21 +29,104 @@ describe('cssObjectGenerator', () => {
true,
);

expect(css).toBe(
'$grid-spacings: (\nspacing-desktop: $grid-spacing-desktop,\n) !default;\n\n$grids: (\ncolumns: $grid-columns,\n) !default;\n\n',
expect(css).toStrictEqual({
$grids: { columns: '$grid-columns', spacing: { desktop: '$grid-spacing-desktop' } },
});
});

it('should generate CSS object from tokens with colors', () => {
const css = generateCssObjectFromTokens(
Array.from(exampleMockedColorsTokens.values()),
mappedTokens,
exampleMockedColorGroups,
true,
);

expect(css).toStrictEqual({
'$action-colors': {
button: {
primary: {
active: '$action-button-primary-active',
},
},
},
'$background-colors': {
primary: '$background-primary',
},
$colors: {
action: '$action-colors',
background: '$background-colors',
},
});
});
});

describe('generateObjectContent', () => {
it('should generate object content', () => {
const objectContent = generateObjectContent(
[exampleMockedTokens.get('dimensionRef') as Token],
describe('createObjectStructureFromTokenNameParts', () => {
it('should create object structure from token name parts', () => {
const cssObject = createObjectStructureFromTokenNameParts(
exampleMockedTokens.get('dimensionRef') as Token,
tokenGroups,
true,
{ $grids: { columns: '$grid-columns' } },
);

expect(objectContent).toBe('spacing-desktop: $grid-spacing-desktop,\n');
expect(cssObject).toStrictEqual({
$grids: { columns: '$grid-columns', spacing: { desktop: '$grid-spacing-desktop' } },
});
});
});

describe('handleInvariantTokens', () => {
it('should return token alias for invariant case', () => {
const token = exampleMockedInvariantTokens.get('radiiRef') as Token;
expect(getTokenAlias(token)).toBe('full');
});

it('should return token alias for non-invariant case', () => {
const token = exampleMockedTokens.get('dimensionRef') as Token;
expect(getTokenAlias(token)).toBe('desktop');
});
});

describe('getTokenAlias', () => {
it('should return token alias for non-numeric', () => {
const token = exampleMockedTokens.get('dimensionRef') as Token;
expect(getTokenAlias(token)).toBe('desktop');
});
});

describe('normalizeFirstNamePart', () => {
it('should return correct first part name for token type dimension', () => {
expect(normalizeFirstNamePart('grid', TokenType.dimension)).toBe('$grids');
});

it('should return correct first part name for token type color', () => {
expect(normalizeFirstNamePart('action', TokenType.color)).toBe('$action-colors');
});
});

describe('createGlobalColorsObject', () => {
it('should create global colors object', () => {
const colorKeys = ['$action-colors', '$background-colors'];
const colorsObject = createGlobalColorsObject(colorKeys);

expect(colorsObject).toStrictEqual({ action: '$action-colors', background: '$background-colors' });
});
});

describe('parseGroupName', () => {
it('should parse group name', () => {
expect(parseGroupName('$background-colors')).toBe('background');
});
});

describe('colorGroupsReducer', () => {
it('should reduce color groups', () => {
const accumulatedColorKeys = { action: '$action-colors' };
const currentColorKey = '$background-colors';
const result = colorGroupsReducer(accumulatedColorKeys, currentColorKey);

expect(result).toStrictEqual({ action: '$action-colors', background: '$background-colors' });
});
});
});
Original file line number Diff line number Diff line change
@@ -1,30 +1,65 @@
import fs from 'fs';
import path from 'path';
import { Token, TokenGroup } from '@supernovaio/sdk-exporters';
import { generateFiles } from '../fileGenerator';
import { exampleMockedGroups, exampleMockedTokens } from '../../formatters/__fixtures__/mockedExampleTokens';
import { Supernova, Token, TokenGroup, TokenTheme } from '@supernovaio/sdk-exporters';
import { generateFiles, generateOutputFilesByThemes } from '../fileGenerator';
import { exampleMockedGroups, exampleMockedTokens } from '../../../tests/fixtures/mockedExampleTokens';
import { nonThemedFilesData } from '../../config/fileConfig';

const mockedExpectedResult = fs.readFileSync(
path.join(__dirname, '../../formatters/__fixtures__/exampleFileContent.scss'),
path.join(__dirname, '../../../tests/fixtures/exampleFileContent.scss'),
'utf-8',
);
const mappedTokens: Map<string, Token> = new Map([]);
const tokenGroups: Array<TokenGroup> = exampleMockedGroups;
const emptyFile = `/* This file was generated by Supernova, don't change manually */\n\n`;

describe('fileGenerator', () => {
it('should generate files', () => {
const tokens = Array.from(exampleMockedTokens.values());
const files = generateFiles(tokens, mappedTokens, tokenGroups);
describe('generateOutputFilesByThemes', () => {
it('should generate output files by themes', async () => {
const tokens = Array.from(exampleMockedTokens.values());
const sdk = {
tokens: {
computeTokensByApplyingThemes: jest.fn().mockResolvedValue(tokens),
},
};
const themes = [{ name: 'theme-light' }, { name: 'theme-light-inverted' }];
const outputFiles = await generateOutputFilesByThemes(
tokens,
mappedTokens,
tokenGroups,
themes as TokenTheme[],
sdk as unknown as Supernova,
);

expect(files).toStrictEqual([
{
fileName: '_borders.scss',
content: emptyFile,
},
{ fileName: '_other.scss', content: mockedExpectedResult },
{ fileName: '_radii.scss', content: emptyFile },
{ fileName: '_spacing.scss', content: emptyFile },
]);
expect(outputFiles).toStrictEqual([
{
path: './globals/',
fileName: '_borders.scss',
content: emptyFile,
},
{ path: './globals/', fileName: '_other.scss', content: mockedExpectedResult },
{ path: './globals/', fileName: '_radii.scss', content: emptyFile },
{ path: './globals/', fileName: '_spacing.scss', content: emptyFile },
{ path: './themes/theme-light/', fileName: '_colors.scss', content: emptyFile },
{ path: './themes/theme-light-inverted/', fileName: '_colors.scss', content: emptyFile },
]);
});
});

describe('generateFiles', () => {
it('should generate files', () => {
const tokens = Array.from(exampleMockedTokens.values());
const files = generateFiles(tokens, mappedTokens, tokenGroups, nonThemedFilesData);

expect(files).toStrictEqual([
{
fileName: '_borders.scss',
content: emptyFile,
},
{ fileName: '_other.scss', content: mockedExpectedResult },
{ fileName: '_radii.scss', content: emptyFile },
{ fileName: '_spacing.scss', content: emptyFile },
]);
});
});
});
Loading
Loading