From dfd1db0121baf33affd9a528f3e0c0c31ed32f03 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Tue, 24 Sep 2024 23:43:53 +0530 Subject: [PATCH 1/7] initial commit to fix typography tokens resolution --- .../src/plugin/applyTypographyTokenOnNode.ts | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts index 9aa562477..d9c6bbed9 100644 --- a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts +++ b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts @@ -42,21 +42,61 @@ export async function applyTypographyTokenOnNode( node.type === 'TEXT' && (!matchingStyleId || (matchingStyleId && !(await trySetStyleId(node, 'text', matchingStyleId)))) ) { - if (isSingleTypographyValue(resolvedToken.value)) { + if (isSingleTypographyValue(resolvedToken.value) && + !((values.fontFamilies + || values.fontWeights + || values.lineHeights + || values.fontSizes + || values.letterSpacing + || values.paragraphSpacing + || values.textCase + || values.textDecoration))) { setTextValuesOnTarget(node, data.typography, baseFontSize); } } } if ( - values.fontFamilies + (values.fontFamilies || values.fontWeights || values.lineHeights || values.fontSizes || values.letterSpacing || values.paragraphSpacing || values.textCase - || values.textDecoration + || values.textDecoration) && data.typography ) { + console.log("here", data, values); + const resolvedToken = defaultTokenValueRetriever.get(data.typography); + console.log("resolved Token is", resolvedToken); + const resolvedValueObject = { + fontFamily: isPrimitiveValue(data.fontFamilies) ? String(data.fontFamilies.startsWith('{') ? data.fontFamilies : `{${data.fontFamilies}}`) : undefined, + fontWeight: isPrimitiveValue(data.fontWeights) ? String(data.fontWeights.startsWith('{') ? data.fontWeights : `{${data.fontWeights}}`) : undefined, + lineHeight: isPrimitiveValue(data.lineHeights) ? String(data.lineHeights.startsWith('{') ? data.lineHeights : `{${data.lineHeights}}`) : undefined, + fontSize: isPrimitiveValue(data.fontSizes) ? String(data.fontSizes.startsWith('{') ? data.fontSizes : `{${data.fontSizes}}`) : undefined, + letterSpacing: isPrimitiveValue(data.letterSpacing) ? String(data.letterSpacing.startsWith('{') ? data.letterSpacing : `{${data.letterSpacing}}`) : undefined, + paragraphSpacing: isPrimitiveValue(data.paragraphSpacing) ? String(data.paragraphSpacing.startsWith('{') ? data.paragraphSpacing : `{${data.paragraphSpacing}}`) : undefined, + textCase: isPrimitiveValue(data.textCase) ? String(data.textCase.startsWith('{') ? data.textCase : `{${data.textCase}}`) : undefined, + textDecoration: isPrimitiveValue(data.textDecoration) ? String(data.textDecoration.startsWith('{') ? data.textDecoration : `{${data.textDecoration}}`) : undefined, + } + const valueObject = { + fontFamily: isPrimitiveValue(values.fontFamilies) ? String(values.fontFamilies) : resolvedToken.value.fontFamily, + fontWeight: isPrimitiveValue(values.fontWeights) ? String(values.fontWeights) : resolvedToken.value.fontWeight, + lineHeight: isPrimitiveValue(values.lineHeights) ? String(values.lineHeights) : resolvedToken.value.lineHeight, + fontSize: isPrimitiveValue(values.fontSizes) ? String(values.fontSizes) : resolvedToken.value.fontSize, + letterSpacing: isPrimitiveValue(values.letterSpacing) ? String(values.letterSpacing) : resolvedToken.value.letterSpacing, + paragraphSpacing: isPrimitiveValue(values.paragraphSpacing) ? String(values.paragraphSpacing) : resolvedToken.value.paragraphSpacing, + textCase: isPrimitiveValue(values.textCase) ? String(values.textCase) : resolvedToken.value.textCase, + textDecoration: isPrimitiveValue(values.textDecoration) ? String(values.textDecoration) : resolvedToken.value.textDecoration, + }; + await tryApplyTypographyCompositeVariable({ + target: node, + value: valueObject, + resolvedValue: resolvedValueObject, + baseFontSize, + }); + } + else + { const resolvedValueObject = { fontFamily: isPrimitiveValue(data.fontFamilies) ? String(data.fontFamilies.startsWith('{') ? data.fontFamilies : `{${data.fontFamilies}}`) : undefined, fontWeight: isPrimitiveValue(data.fontWeights) ? String(data.fontWeights.startsWith('{') ? data.fontWeights : `{${data.fontWeights}}`) : undefined, From a1b61b84a530cfccff534058cf07e91d33fdb053 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Wed, 25 Sep 2024 11:35:54 +0530 Subject: [PATCH 2/7] remove debug statements --- .../src/plugin/applyTypographyTokenOnNode.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts index d9c6bbed9..c4a5c59de 100644 --- a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts +++ b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts @@ -65,9 +65,7 @@ export async function applyTypographyTokenOnNode( || values.textCase || values.textDecoration) && data.typography ) { - console.log("here", data, values); const resolvedToken = defaultTokenValueRetriever.get(data.typography); - console.log("resolved Token is", resolvedToken); const resolvedValueObject = { fontFamily: isPrimitiveValue(data.fontFamilies) ? String(data.fontFamilies.startsWith('{') ? data.fontFamilies : `{${data.fontFamilies}}`) : undefined, fontWeight: isPrimitiveValue(data.fontWeights) ? String(data.fontWeights.startsWith('{') ? data.fontWeights : `{${data.fontWeights}}`) : undefined, From 1117a96acb46190b9d4024b743308f50e47d16dc Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Wed, 25 Sep 2024 16:20:38 +0530 Subject: [PATCH 3/7] refactor code to use utility functions and remove duplication --- .../src/plugin/applyTypographyTokenOnNode.ts | 163 +++++++----------- 1 file changed, 60 insertions(+), 103 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts index c4a5c59de..7c91b34d8 100644 --- a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts +++ b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts @@ -8,6 +8,34 @@ import { NodeTokenRefMap } from '@/types/NodeTokenRefMap'; import { MapValuesToTokensResult } from '@/types'; import { tryApplyTypographyCompositeVariable } from './tryApplyTypographyCompositeVariable'; +function buildResolvedValueObject(data: any) { + return { + fontFamily: isPrimitiveValue(data.fontFamilies) ? String(data.fontFamilies.startsWith('{') ? data.fontFamilies : `{${data.fontFamilies}}`) : undefined, + fontWeight: isPrimitiveValue(data.fontWeights) ? String(data.fontWeights.startsWith('{') ? data.fontWeights : `{${data.fontWeights}}`) : undefined, + lineHeight: isPrimitiveValue(data.lineHeights) ? String(data.lineHeights.startsWith('{') ? data.lineHeights : `{${data.lineHeights}}`) : undefined, + fontSize: isPrimitiveValue(data.fontSizes) ? String(data.fontSizes.startsWith('{') ? data.fontSizes : `{${data.fontSizes}}`) : undefined, + letterSpacing: isPrimitiveValue(data.letterSpacing) ? String(data.letterSpacing.startsWith('{') ? data.letterSpacing : `{${data.letterSpacing}}`) : undefined, + paragraphSpacing: isPrimitiveValue(data.paragraphSpacing) ? String(data.paragraphSpacing.startsWith('{') ? data.paragraphSpacing : `{${data.paragraphSpacing}}`) : undefined, + textCase: isPrimitiveValue(data.textCase) ? String(data.textCase.startsWith('{') ? data.textCase : `{${data.textCase}}`) : undefined, + textDecoration: isPrimitiveValue(data.textDecoration) ? String(data.textDecoration.startsWith('{') ? data.textDecoration : `{${data.textDecoration}}`) : undefined, + }; +} + +function buildValueObject(values: any, resolvedToken: any) { + const tokenValue = resolvedToken?.value || {}; + + return { + fontFamily: isPrimitiveValue(values.fontFamilies) ? String(values.fontFamilies) : tokenValue.fontFamily, + fontWeight: isPrimitiveValue(values.fontWeights) ? String(values.fontWeights) : tokenValue.fontWeight, + lineHeight: isPrimitiveValue(values.lineHeights) ? String(values.lineHeights) : tokenValue.lineHeight, + fontSize: isPrimitiveValue(values.fontSizes) ? String(values.fontSizes) : tokenValue.fontSize, + letterSpacing: isPrimitiveValue(values.letterSpacing) ? String(values.letterSpacing) : tokenValue.letterSpacing, + paragraphSpacing: isPrimitiveValue(values.paragraphSpacing) ? String(values.paragraphSpacing) : tokenValue.paragraphSpacing, + textCase: isPrimitiveValue(values.textCase) ? String(values.textCase) : tokenValue.textCase, + textDecoration: isPrimitiveValue(values.textDecoration) ? String(values.textDecoration) : tokenValue.textDecoration, + }; +} + export async function applyTypographyTokenOnNode( node: BaseNode, data: NodeTokenRefMap, @@ -15,111 +43,40 @@ export async function applyTypographyTokenOnNode( baseFontSize: string, ) { if (!(node.type === 'TEXT')) return; - if (values.typography && data.typography) { - const resolvedToken = defaultTokenValueRetriever.get(data.typography); - let matchingStyleId = resolvedToken.styleId; - // Note: We should remove "backup style id" logic from here (this part). This was relevant before we had Themes, where style ids could not be saved to a token yet. - if (!matchingStyleId && isSingleTypographyValue(resolvedToken.value)) { - const styleIdBackupKey = 'textStyleId_original'; - const nonLocalStyle = getNonLocalStyle(node, styleIdBackupKey, 'typography'); - if (nonLocalStyle) { - if (textStyleMatchesTypographyToken(nonLocalStyle, resolvedToken.value, baseFontSize)) { - // Non-local style matches - use this and clear style id backup: - matchingStyleId = nonLocalStyle.id; - clearStyleIdBackup(node, styleIdBackupKey); - } else if (resolvedToken.adjustedTokenName === nonLocalStyle.name) { - // Non-local style does NOT match, but style name and token path does, - // so we assume selected token value is an override (e.g. dark theme) - // Now backup up style id before overwriting with raw token value, so we - // can re-link the non-local style, when the token value matches again: - setStyleIdBackup(node, styleIdBackupKey, nonLocalStyle.id); - } - } - } + const resolvedToken = data.typography ? defaultTokenValueRetriever.get(data.typography) : null; + let matchingStyleId = resolvedToken?.styleId; - if ( - node.type === 'TEXT' - && (!matchingStyleId || (matchingStyleId && !(await trySetStyleId(node, 'text', matchingStyleId)))) - ) { - if (isSingleTypographyValue(resolvedToken.value) && - !((values.fontFamilies - || values.fontWeights - || values.lineHeights - || values.fontSizes - || values.letterSpacing - || values.paragraphSpacing - || values.textCase - || values.textDecoration))) { - setTextValuesOnTarget(node, data.typography, baseFontSize); - } - } - } - if ( - (values.fontFamilies - || values.fontWeights - || values.lineHeights - || values.fontSizes - || values.letterSpacing - || values.paragraphSpacing - || values.textCase - || values.textDecoration) && data.typography - ) { - const resolvedToken = defaultTokenValueRetriever.get(data.typography); - const resolvedValueObject = { - fontFamily: isPrimitiveValue(data.fontFamilies) ? String(data.fontFamilies.startsWith('{') ? data.fontFamilies : `{${data.fontFamilies}}`) : undefined, - fontWeight: isPrimitiveValue(data.fontWeights) ? String(data.fontWeights.startsWith('{') ? data.fontWeights : `{${data.fontWeights}}`) : undefined, - lineHeight: isPrimitiveValue(data.lineHeights) ? String(data.lineHeights.startsWith('{') ? data.lineHeights : `{${data.lineHeights}}`) : undefined, - fontSize: isPrimitiveValue(data.fontSizes) ? String(data.fontSizes.startsWith('{') ? data.fontSizes : `{${data.fontSizes}}`) : undefined, - letterSpacing: isPrimitiveValue(data.letterSpacing) ? String(data.letterSpacing.startsWith('{') ? data.letterSpacing : `{${data.letterSpacing}}`) : undefined, - paragraphSpacing: isPrimitiveValue(data.paragraphSpacing) ? String(data.paragraphSpacing.startsWith('{') ? data.paragraphSpacing : `{${data.paragraphSpacing}}`) : undefined, - textCase: isPrimitiveValue(data.textCase) ? String(data.textCase.startsWith('{') ? data.textCase : `{${data.textCase}}`) : undefined, - textDecoration: isPrimitiveValue(data.textDecoration) ? String(data.textDecoration.startsWith('{') ? data.textDecoration : `{${data.textDecoration}}`) : undefined, + // Backup logic for non-local styles + if (!matchingStyleId && resolvedToken && isSingleTypographyValue(resolvedToken.value)) { + const styleIdBackupKey = 'textStyleId_original'; + const nonLocalStyle = getNonLocalStyle(node, styleIdBackupKey, 'typography'); + if (nonLocalStyle && textStyleMatchesTypographyToken(nonLocalStyle, resolvedToken.value, baseFontSize)) { + matchingStyleId = nonLocalStyle.id; + clearStyleIdBackup(node, styleIdBackupKey); + } else if (nonLocalStyle && resolvedToken.adjustedTokenName === nonLocalStyle?.name) { + setStyleIdBackup(node, styleIdBackupKey, nonLocalStyle.id); } - const valueObject = { - fontFamily: isPrimitiveValue(values.fontFamilies) ? String(values.fontFamilies) : resolvedToken.value.fontFamily, - fontWeight: isPrimitiveValue(values.fontWeights) ? String(values.fontWeights) : resolvedToken.value.fontWeight, - lineHeight: isPrimitiveValue(values.lineHeights) ? String(values.lineHeights) : resolvedToken.value.lineHeight, - fontSize: isPrimitiveValue(values.fontSizes) ? String(values.fontSizes) : resolvedToken.value.fontSize, - letterSpacing: isPrimitiveValue(values.letterSpacing) ? String(values.letterSpacing) : resolvedToken.value.letterSpacing, - paragraphSpacing: isPrimitiveValue(values.paragraphSpacing) ? String(values.paragraphSpacing) : resolvedToken.value.paragraphSpacing, - textCase: isPrimitiveValue(values.textCase) ? String(values.textCase) : resolvedToken.value.textCase, - textDecoration: isPrimitiveValue(values.textDecoration) ? String(values.textDecoration) : resolvedToken.value.textDecoration, - }; - await tryApplyTypographyCompositeVariable({ - target: node, - value: valueObject, - resolvedValue: resolvedValueObject, - baseFontSize, - }); } - else - { - const resolvedValueObject = { - fontFamily: isPrimitiveValue(data.fontFamilies) ? String(data.fontFamilies.startsWith('{') ? data.fontFamilies : `{${data.fontFamilies}}`) : undefined, - fontWeight: isPrimitiveValue(data.fontWeights) ? String(data.fontWeights.startsWith('{') ? data.fontWeights : `{${data.fontWeights}}`) : undefined, - lineHeight: isPrimitiveValue(data.lineHeights) ? String(data.lineHeights.startsWith('{') ? data.lineHeights : `{${data.lineHeights}}`) : undefined, - fontSize: isPrimitiveValue(data.fontSizes) ? String(data.fontSizes.startsWith('{') ? data.fontSizes : `{${data.fontSizes}}`) : undefined, - letterSpacing: isPrimitiveValue(data.letterSpacing) ? String(data.letterSpacing.startsWith('{') ? data.letterSpacing : `{${data.letterSpacing}}`) : undefined, - paragraphSpacing: isPrimitiveValue(data.paragraphSpacing) ? String(data.paragraphSpacing.startsWith('{') ? data.paragraphSpacing : `{${data.paragraphSpacing}}`) : undefined, - textCase: isPrimitiveValue(data.textCase) ? String(data.textCase.startsWith('{') ? data.textCase : `{${data.textCase}}`) : undefined, - textDecoration: isPrimitiveValue(data.textDecoration) ? String(data.textDecoration.startsWith('{') ? data.textDecoration : `{${data.textDecoration}}`) : undefined, - } - const valueObject = { - fontFamily: isPrimitiveValue(values.fontFamilies) ? String(values.fontFamilies) : undefined, - fontWeight: isPrimitiveValue(values.fontWeights) ? String(values.fontWeights) : undefined, - lineHeight: isPrimitiveValue(values.lineHeights) ? String(values.lineHeights) : undefined, - fontSize: isPrimitiveValue(values.fontSizes) ? String(values.fontSizes) : undefined, - letterSpacing: isPrimitiveValue(values.letterSpacing) ? String(values.letterSpacing) : undefined, - paragraphSpacing: isPrimitiveValue(values.paragraphSpacing) ? String(values.paragraphSpacing) : undefined, - textCase: isPrimitiveValue(values.textCase) ? String(values.textCase) : undefined, - textDecoration: isPrimitiveValue(values.textDecoration) ? String(values.textDecoration) : undefined, - }; - await tryApplyTypographyCompositeVariable({ - target: node, - value: valueObject, - resolvedValue: resolvedValueObject, - baseFontSize, - }); + + // Apply matching style or fallback to applying values + if (matchingStyleId && (await trySetStyleId(node, 'text', matchingStyleId))) return; + + // Apply typography token directly if no other properties exist + if (data.typography && resolvedToken && isSingleTypographyValue(resolvedToken.value) && !Object.keys(values).length) { + setTextValuesOnTarget(node, data.typography, baseFontSize); + return; } -} \ No newline at end of file + + // Build the resolved value and value objects + const resolvedValueObject = buildResolvedValueObject(data); + const valueObject = buildValueObject(values, resolvedToken); + + // Apply the typography token and other values together + await tryApplyTypographyCompositeVariable({ + target: node, + value: valueObject, + resolvedValue: resolvedValueObject, + baseFontSize, + }); +} From 24301a2e9e53d3b3dfceac38e0c5797a2be2d8bf Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Wed, 25 Sep 2024 16:22:14 +0530 Subject: [PATCH 4/7] formatting --- .../src/plugin/applyTypographyTokenOnNode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts index 7c91b34d8..1caeef921 100644 --- a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts +++ b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts @@ -23,7 +23,7 @@ function buildResolvedValueObject(data: any) { function buildValueObject(values: any, resolvedToken: any) { const tokenValue = resolvedToken?.value || {}; - + return { fontFamily: isPrimitiveValue(values.fontFamilies) ? String(values.fontFamilies) : tokenValue.fontFamily, fontWeight: isPrimitiveValue(values.fontWeights) ? String(values.fontWeights) : tokenValue.fontWeight, From f5ece84b3cc9f27c142cb2629f79302ce9c0eb47 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Thu, 26 Sep 2024 19:33:54 +0530 Subject: [PATCH 5/7] add changeset --- .changeset/weak-oranges-buy.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/weak-oranges-buy.md diff --git a/.changeset/weak-oranges-buy.md b/.changeset/weak-oranges-buy.md new file mode 100644 index 000000000..26eec3288 --- /dev/null +++ b/.changeset/weak-oranges-buy.md @@ -0,0 +1,5 @@ +--- +"@tokens-studio/figma-plugin": patch +--- + +fixes a bug where applying a typography token to a text node would override individual property changes (like font size or font family) when "Apply to selection/page/document" is clicked. From eb4ac9cbd8f5898130e8eb210adbefca89527271 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Tue, 1 Oct 2024 15:22:41 +0530 Subject: [PATCH 6/7] update a test in setValuesOnNode.test.ts --- .../src/plugin/__tests__/setValuesOnNode.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/__tests__/setValuesOnNode.test.ts b/packages/tokens-studio-for-figma/src/plugin/__tests__/setValuesOnNode.test.ts index faa6d0195..1c05c4350 100644 --- a/packages/tokens-studio-for-figma/src/plugin/__tests__/setValuesOnNode.test.ts +++ b/packages/tokens-studio-for-figma/src/plugin/__tests__/setValuesOnNode.test.ts @@ -265,7 +265,7 @@ describe('Can set values on node', () => { }, }, ); - expect(setTextValuesOnTargetSpy).toHaveBeenCalled(); + expect(setTextValuesOnTargetSpy).not.toHaveBeenCalled(); }); it('sets textstyle if matching Style is found', async () => { From b946902e4aaecee16c2af7963991c74200634693 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Tue, 1 Oct 2024 15:42:29 +0530 Subject: [PATCH 7/7] update test definition --- .../src/plugin/__tests__/setValuesOnNode.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/__tests__/setValuesOnNode.test.ts b/packages/tokens-studio-for-figma/src/plugin/__tests__/setValuesOnNode.test.ts index 1c05c4350..a784b052a 100644 --- a/packages/tokens-studio-for-figma/src/plugin/__tests__/setValuesOnNode.test.ts +++ b/packages/tokens-studio-for-figma/src/plugin/__tests__/setValuesOnNode.test.ts @@ -235,7 +235,7 @@ describe('Can set values on node', () => { expect(setTextValuesOnTargetSpy).not.toHaveBeenCalled(); }); - it('calls setTextValuesOnTarget if text node and composite typography tokens are given', async () => { + it('does not call setTextValuesOnTarget if text node and composite typography tokens are given', async () => { defaultTokenValueRetriever.initiate({ tokens: [ {