Skip to content

Commit

Permalink
Feat(variables-scss): Add export for shadows, gradients and typography
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelklibani committed Sep 19, 2024
1 parent bbd6137 commit 8ff987f
Show file tree
Hide file tree
Showing 17 changed files with 491 additions and 71 deletions.
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ makefile
CODEOWNERS

# variable-scss exporter example mock test files
exporters/variables-scss/src/**/*.scss
exporters/variables-scss/tests/fixtures/*.scss

# variable-scss exporter generated cjs
exporters/variables-scss/generated/**/*.cjs
36 changes: 21 additions & 15 deletions exporters/variables-scss/generated/exporter.cjs

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions exporters/variables-scss/src/config/fileConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,25 @@ export const nonThemedFilesData: FileData[] = [
hasParentPrefix: false,
sortByNumValue: true,
},
{
fileName: '_shadows.scss',
tokenTypes: [TokenType.shadow],
groupNames: [''],
hasParentPrefix: false,
},
{
fileName: '_gradients.scss',
tokenTypes: [TokenType.gradient],
groupNames: [''],
hasParentPrefix: true,
},
{
fileName: '_typography.scss',
tokenTypes: [TokenType.typography],
groupNames: [''],
withCssObject: true,
hasParentPrefix: false,
},
];

export const themedFilesData: FileData[] = [
Expand Down
14 changes: 8 additions & 6 deletions exporters/variables-scss/src/formatters/cssFormatter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const IDENTATION = ' ';
const INDENTATION = ' ';

export const removeExtraBlankLines = (css: string): string => {
return css.replace(/\n{3,}/g, '\n\n');
Expand All @@ -14,16 +14,18 @@ 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`;
// Check if both '(' and ')' are on the same line
if (line.includes('(') && line.includes(')')) {
formattedCSS += `${INDENTATION.repeat(indentationLevel)}${line}\n`;
} else if (line.includes('(')) {
formattedCSS += `${INDENTATION.repeat(indentationLevel)}${line}\n`;
indentationLevel += 1;
} else if (line.includes(')')) {
indentationLevel -= 1;
formattedCSS += `${IDENTATION.repeat(indentationLevel)}${line}\n`;
formattedCSS += `${INDENTATION.repeat(indentationLevel)}${line}\n`;
} else {
formattedCSS += `${IDENTATION.repeat(indentationLevel)}${line}\n`;
formattedCSS += `${INDENTATION.repeat(indentationLevel)}${line}\n`;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +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 '../../../tests/fixtures/mockedExampleTokens';
import {
exampleMockedGroups,
exampleMockedTokens,
exampleTypographyMockedTokens,
} from '../../../tests/fixtures/mockedExampleTokens';
import { FileData } from '../../config/fileConfig';

const mockedExpectedResult = fs.readFileSync(
Expand Down Expand Up @@ -56,11 +60,30 @@ describe('contentGenerator', () => {
},
];

const dataTypographyProviderItems = {
type: TokenType.typography,
group: 'Heading',
tokenIdentifier: 'typographyHeadingRef1',
};

it.each(dataProviderItems)('should filter $description', ({ type, group, tokenIdentifier }) => {
const tokens = Array.from(exampleMockedTokens.values());
const expectedTokens = [exampleMockedTokens.get(tokenIdentifier) as Token];

expect(filterTokensByTypeAndGroup(tokens, type, group)).toStrictEqual(expectedTokens);
});

it(`should filter ${dataTypographyProviderItems.type} token type and ${dataTypographyProviderItems.group} group and exclude tokens with "-Underline"`, () => {
const tokens = Array.from(exampleTypographyMockedTokens.values());
const expectedTokens = [exampleTypographyMockedTokens.get(dataTypographyProviderItems.tokenIdentifier) as Token];

const filteredTokens = filterTokensByTypeAndGroup(
tokens,
dataTypographyProviderItems.type,
dataTypographyProviderItems.group,
);

expect(filteredTokens).toStrictEqual(expectedTokens);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ const mockedExpectedResult = fs.readFileSync(
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`;
const indexFile = `@forward 'borders';
@forward 'other';
@forward 'radii';
@forward 'spacing';
@forward 'shadows';
@forward 'gradients';
@forward 'typography';
`;
const indexColorFile = `@forward 'colors';\n`;

describe('fileGenerator', () => {
describe('generateOutputFilesByThemes', () => {
Expand Down Expand Up @@ -40,8 +49,14 @@ describe('fileGenerator', () => {
{ path: './globals/', fileName: '_other.scss', content: mockedExpectedResult },
{ path: './globals/', fileName: '_radii.scss', content: emptyFile },
{ path: './globals/', fileName: '_spacing.scss', content: emptyFile },
{ path: './globals/', fileName: '_shadows.scss', content: emptyFile },
{ path: './globals/', fileName: '_gradients.scss', content: emptyFile },
{ path: './globals/', fileName: '_typography.scss', content: emptyFile },
{ path: './globals/', fileName: 'index.scss', content: indexFile },
{ path: './themes/theme-light/', fileName: '_colors.scss', content: emptyFile },
{ path: './themes/theme-light/', fileName: 'index.scss', content: indexColorFile },
{ path: './themes/theme-light-inverted/', fileName: '_colors.scss', content: emptyFile },
{ path: './themes/theme-light-inverted/', fileName: 'index.scss', content: indexColorFile },
]);
});
});
Expand All @@ -59,6 +74,9 @@ describe('fileGenerator', () => {
{ fileName: '_other.scss', content: mockedExpectedResult },
{ fileName: '_radii.scss', content: emptyFile },
{ fileName: '_spacing.scss', content: emptyFile },
{ fileName: '_shadows.scss', content: emptyFile },
{ fileName: '_gradients.scss', content: emptyFile },
{ fileName: '_typography.scss', content: emptyFile },
]);
});
});
Expand Down
28 changes: 18 additions & 10 deletions exporters/variables-scss/src/generators/contentGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ export const addDisclaimer = (content: string): string => {
};

export const filterTokensByTypeAndGroup = (tokens: Token[], type: TokenType, group: string) => {
return tokens.filter((token) => token.tokenType === type && token.origin?.name?.includes(group));
return tokens.filter((token) => {
const hasMatchingType = token.tokenType === type;
const isInGroup = token.origin?.name?.includes(group);
const hasValidTypography = !(token.tokenType === TokenType.typography && token.name.includes('-Underline'));

return hasMatchingType && isInGroup && hasValidTypography;
});
};

export const generateFileContent = (
Expand All @@ -30,15 +36,17 @@ export const generateFileContent = (
const filteredTokens = filterTokensByTypeAndGroup(tokens, tokenType, group);

// Generate css tokens
cssTokens += generateCssFromTokens(
filteredTokens,
mappedTokens,
tokenGroups,
group,
hasParentPrefix,
sortByNumValue,
);
cssTokens += '\n\n';
if (tokenType !== TokenType.typography) {
cssTokens += generateCssFromTokens(
filteredTokens,
mappedTokens,
tokenGroups,
group,
hasParentPrefix,
sortByNumValue,
);
cssTokens += '\n\n';
}

// Generate css object and merge it with the existing one
const groupCssObject = generateCssObjectFromTokens(filteredTokens, mappedTokens, tokenGroups, hasParentPrefix);
Expand Down
60 changes: 55 additions & 5 deletions exporters/variables-scss/src/generators/cssGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import { ColorToken, DimensionToken, StringToken, Token, TokenGroup, TokenType } from '@supernovaio/sdk-exporters';
import {
ColorToken,
DimensionToken,
GradientToken,
ShadowToken,
StringToken,
Token,
TokenGroup,
TokenType,
} from '@supernovaio/sdk-exporters';
import { ColorFormat, CSSHelper } from '@supernovaio/export-helpers';
import { addEmptyLineBetweenTokenGroups, formatTokenName, sortTokens, tokenVariableName } from '../helpers/tokenHelper';
import {
addAngleVarToGradient,
addEmptyLineBetweenTokenGroups,
formatTokenName,
sortTokens,
tokenVariableName,
} from '../helpers/tokenHelper';
import { handleSpecialCase } from '../helpers/specialCaseHelper';
import { normalizeColor } from '../helpers/colorHelper';

Expand All @@ -10,7 +25,13 @@ export const tokenToCSSByType = (
tokenGroups: Array<TokenGroup>,
withParent: boolean,
): string | null => {
if (token.tokenType === TokenType.dimension) {
const hasTokenType = (type: TokenType) => {
const { tokenType } = token;

return tokenType === type;
};

if (hasTokenType(TokenType.dimension)) {
const dimensionToken = token as DimensionToken;
const name = tokenVariableName(dimensionToken, tokenGroups, withParent);
let value = dimensionToken.value?.measure;
Expand All @@ -20,7 +41,7 @@ export const tokenToCSSByType = (
return formatTokenName(name, value, unit);
}

if (token.tokenType === TokenType.string) {
if (hasTokenType(TokenType.string)) {
const stringToken = token as StringToken;
const name = tokenVariableName(stringToken, tokenGroups, withParent);
let value = stringToken.value.text;
Expand All @@ -29,7 +50,7 @@ export const tokenToCSSByType = (
return formatTokenName(name, value);
}

if (token.tokenType === TokenType.color) {
if (hasTokenType(TokenType.color)) {
const colorToken = token as ColorToken;
const name = tokenVariableName(colorToken, tokenGroups, withParent);
let value = CSSHelper.colorTokenValueToCSS(colorToken.value, mappedTokens, {
Expand All @@ -44,6 +65,35 @@ export const tokenToCSSByType = (
return formatTokenName(name, value);
}

if (hasTokenType(TokenType.shadow)) {
const shadowToken = token as ShadowToken;
const name = tokenVariableName(token, tokenGroups, withParent);
const { value } = shadowToken;
const color = CSSHelper.shadowTokenValueToCSS(value, mappedTokens, {
allowReferences: true,
decimals: 3,
colorFormat: ColorFormat.hashHex8,
tokenToVariableRef: () => '',
});

return formatTokenName(name, color).replace(/0px/g, '0');
}

if (hasTokenType(TokenType.gradient)) {
const gradientToken = token as GradientToken;
const name = tokenVariableName(token, tokenGroups, withParent);
const { value } = gradientToken;
let gradient = CSSHelper.gradientTokenValueToCSS(value, mappedTokens, {
allowReferences: true,
colorFormat: ColorFormat.hashHex8,
decimals: 3,
tokenToVariableRef: () => '',
});
gradient = addAngleVarToGradient(gradient);

return formatTokenName(name, gradient);
}

return null;
};

Expand Down
Loading

0 comments on commit 8ff987f

Please sign in to comment.