diff --git a/src/plugin/figmaTransforms/fontWeight.ts b/src/plugin/figmaTransforms/fontWeight.ts new file mode 100644 index 000000000..7d0608bf4 --- /dev/null +++ b/src/plugin/figmaTransforms/fontWeight.ts @@ -0,0 +1,24 @@ +export function convertFontWeightToFigma(value: string) { + switch (value) { + case '100': + return ['Thin', 'Hairline']; + case '200': + return ['ExtraLight', 'Extra Light', 'UltraLight', 'Ultra Light']; + case '300': + return ['Light']; + case '400': + return ['Regular', 'Normal']; + case '500': + return ['Medium']; + case '600': + return ['SemiBold', 'Semi Bold', 'DemiBold', 'Demi Bold']; + case '700': + return ['Bold']; + case '800': + return ['ExtraBold', 'Extra Bold', 'UltraBold', 'Ultra Bold']; + case '900': + return ['Black', 'Heavy']; + default: + return []; + } +} diff --git a/src/plugin/helpers.test.ts b/src/plugin/helpers.test.ts index 92f075825..2445dd68b 100644 --- a/src/plugin/helpers.test.ts +++ b/src/plugin/helpers.test.ts @@ -67,6 +67,16 @@ describe('transformValue', () => { type: 'opacity', output: 0.6, }, + { + input: '100', + type: 'fontWeights', + output: ['Thin', 'Hairline'], + }, + { + input: 'bold', + type: 'fontWeights', + output: [], + }, ]; it('transforms non-conform values into their required formats', () => { tokens.forEach((token) => { diff --git a/src/plugin/helpers.ts b/src/plugin/helpers.ts index da4e610a6..da2cab6ae 100644 --- a/src/plugin/helpers.ts +++ b/src/plugin/helpers.ts @@ -5,6 +5,7 @@ import { convertLineHeightToFigma } from './figmaTransforms/lineHeight'; import { convertBoxShadowTypeToFigma } from './figmaTransforms/boxShadow'; import { convertTextCaseToFigma } from './figmaTransforms/textCase'; import { convertTextDecorationToFigma } from './figmaTransforms/textDecoration'; +import { convertFontWeightToFigma } from './figmaTransforms/fontWeight'; import { UserIdProperty } from '@/figmaStorage'; import { generateId } from '@/utils/generateId'; import { Properties } from '@/constants/Properties'; @@ -27,6 +28,7 @@ export async function getUserId() { return userId; } +export function transformValue(value: string, type: 'fontWeights'): ReturnType; export function transformValue(value: string, type: 'letterSpacing'): LetterSpacing | null; export function transformValue(value: string, type: 'lineHeights'): LineHeight | null; export function transformValue(value: string, type: 'boxShadowType'): ReturnType; @@ -60,6 +62,8 @@ export function transformValue(value: string, type: string) { case 'paragraphSpacing': case 'fontSizes': return convertTypographyNumberToFigma(value); + case 'fontWeights': + return convertFontWeightToFigma(value); case 'letterSpacing': return convertLetterSpacingToFigma(value); case 'lineHeights': diff --git a/src/plugin/setTextValuesOnTarget.test.ts b/src/plugin/setTextValuesOnTarget.test.ts index c3efaa5ee..5eae3b7ad 100644 --- a/src/plugin/setTextValuesOnTarget.test.ts +++ b/src/plugin/setTextValuesOnTarget.test.ts @@ -2,6 +2,7 @@ import setTextValuesOnTarget from './setTextValuesOnTarget'; describe('setTextValuesOnTarget', () => { let textNodeMock; + const loadFontAsyncSpy = jest.spyOn(figma, 'loadFontAsync'); beforeEach(() => { textNodeMock = { @@ -35,6 +36,25 @@ describe('setTextValuesOnTarget', () => { expect(textNodeMock).toEqual({ ...textNodeMock, fontName: { ...textNodeMock.fontName, style: 'Bold' } }); }); + it('converts a numerical fontWeight and sets to the node', async () => { + loadFontAsyncSpy.mockImplementationOnce(() => ( + Promise.reject() + )); + loadFontAsyncSpy.mockImplementation(() => ( + Promise.resolve() + )); + await setTextValuesOnTarget(textNodeMock, { value: { fontWeight: '500' } }); + expect(textNodeMock).toEqual({ ...textNodeMock, fontName: { ...textNodeMock.fontName, style: 'Medium' } }); + }); + + it('can\'t set number fontWeight to the node if there is no matching fontWeight', async () => { + loadFontAsyncSpy.mockImplementation(() => ( + Promise.reject() + )); + await setTextValuesOnTarget(textNodeMock, { value: { fontWeight: '500' } }); + expect(textNodeMock).toEqual({ ...textNodeMock, fontName: { ...textNodeMock.fontName } }); + }); + it('sets textCase, textDecoration and description if those are given', async () => { await setTextValuesOnTarget(textNodeMock, { description: 'Use with care', @@ -47,4 +67,23 @@ describe('setTextValuesOnTarget', () => { textCase: 'TITLE', }); }); + + it('it throws error, when there is no value in token', async () => { + await setTextValuesOnTarget(textNodeMock, { + description: 'Use with care', + }); + expect(textNodeMock).toEqual({ + ...textNodeMock, + }); + }); + + it('it does nothing when the type of value is string', async () => { + await setTextValuesOnTarget(textNodeMock, { + description: 'Use with care', + value: 'string' + }); + expect(textNodeMock).toEqual({ + ...textNodeMock, + }); + }); }); diff --git a/src/plugin/setTextValuesOnTarget.ts b/src/plugin/setTextValuesOnTarget.ts index 337d633c8..8c14fdc1f 100644 --- a/src/plugin/setTextValuesOnTarget.ts +++ b/src/plugin/setTextValuesOnTarget.ts @@ -18,14 +18,34 @@ export default async function setTextValuesOnTarget(target: TextNode | TextStyle } = value; const family = fontFamily?.toString() || (target.fontName !== figma.mixed ? target.fontName.family : ''); const style = fontWeight?.toString() || (target.fontName !== figma.mixed ? target.fontName.style : ''); - await figma.loadFontAsync({ family, style }); - if (fontFamily || fontWeight) { - target.fontName = { - family, - style, - }; - } + try { + if (fontFamily || fontWeight) { + await figma.loadFontAsync({ family, style }); + target.fontName = { + family, + style, + }; + } + } catch { + const candidateStyles = transformValue(style, 'fontWeights'); + await Promise.all( + candidateStyles.map(async (candidateStyle) => ( + figma.loadFontAsync({ family, style: candidateStyle }) + .then(() => { + if (candidateStyle) { + target.fontName = { + family, + style: candidateStyle, + }; + } + }) + .catch((e) => { + console.log('Error setting fontWeight on target', e); + }) + )), + ); + } if (fontSize) { target.fontSize = transformValue(fontSize, 'fontSizes'); } diff --git a/src/plugin/setValuesOnNode.ts b/src/plugin/setValuesOnNode.ts index 51fdcd63d..f984ffbfe 100644 --- a/src/plugin/setValuesOnNode.ts +++ b/src/plugin/setValuesOnNode.ts @@ -29,7 +29,6 @@ export default async function setValuesOnNode( const stylePathPrefix = prefixStylesWithThemeName && activeThemeObject ? activeThemeObject.name : null; - try { // BORDER RADIUS if (