From 0b525a634de6d429a464cde5a51d1523f6f29ea9 Mon Sep 17 00:00:00 2001 From: Jan Six Date: Wed, 4 Jan 2023 10:13:42 +0100 Subject: [PATCH 01/13] bump release --- package.json | 2 +- script/release.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 964f9f22d..2ab9390aa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tokens-studio-for-figma", "version": "1.0.0", - "plugin_version": "132", + "plugin_version": "133", "description": "Tokens Studio for Figma", "license": "MIT", "scripts": { diff --git a/script/release.sh b/script/release.sh index e08406d61..d0350fc56 100755 --- a/script/release.sh +++ b/script/release.sh @@ -1,4 +1,4 @@ -VERSION=figma-tokens@132 +VERSION=figma-tokens@133 sentry-cli releases -p figma-tokens files "$VERSION" upload-sourcemaps --ext ts --ext tsx --ext map --ext js --ignore-file .sentryignore . sentry-cli releases set-commits "$VERSION" --auto sentry-cli releases finalize "$VERSION" \ No newline at end of file From 300e3c8ca8915068c4eed25a83522bcd17f6d1a9 Mon Sep 17 00:00:00 2001 From: SorsOps <80043879+SorsOps@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:26:01 +0200 Subject: [PATCH 02/13] 1504 Sensitive header support (#1512) This resolves https://github.com/tokens-studio/figma-plugin/issues/1504 . It also fixes a minor "bug" if an input ref was not provided but needed for masking Co-authored-by: andrewx82 <80043879+andrewx82@users.noreply.github.com> --- src/app/components/Input.tsx | 10 +++++----- .../components/StorageItemForm/GenericVersioned.tsx | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/components/Input.tsx b/src/app/components/Input.tsx index 6c3480a22..ad1b00381 100644 --- a/src/app/components/Input.tsx +++ b/src/app/components/Input.tsx @@ -166,16 +166,16 @@ const Input = React.forwardRef(({ }, ref) => { // if isMasked is true, then we need to handle toggle visibility const [show, setShow] = React.useState(false); + const htmlInputRef = React.useRef(null); + const reifiedRef = inputRef || htmlInputRef; const handleVisibility = useCallback((e: React.MouseEvent) => { e.preventDefault(); setShow(!show); - if (inputRef?.current?.type) { - inputRef.current.type = inputRef?.current?.type === 'password' ? 'text' : 'password'; + if (reifiedRef?.current?.type) { + reifiedRef.current.type = reifiedRef?.current?.type === 'password' ? 'text' : 'password'; } - }, [show, inputRef]); - - const htmlInputRef = React.useRef(null); + }, [show, reifiedRef]); React.useEffect(() => { if (autofocus && htmlInputRef && htmlInputRef.current) { diff --git a/src/app/components/StorageItemForm/GenericVersioned.tsx b/src/app/components/StorageItemForm/GenericVersioned.tsx index 6d8f38c94..2f26bb752 100644 --- a/src/app/components/StorageItemForm/GenericVersioned.tsx +++ b/src/app/components/StorageItemForm/GenericVersioned.tsx @@ -177,8 +177,9 @@ export default function GenericVersionedForm({ label="Value" value={x.value} disabled={!x.name} + isMasked onChange={onHeaderChange} - type="text" + type="password" name="value" data-index={i} /> From 4049d9762ac21b07f74df813ced565cb10e05065 Mon Sep 17 00:00:00 2001 From: Jan Six Date: Wed, 4 Jan 2023 10:25:11 +0100 Subject: [PATCH 03/13] decrease missing font weight to notify --- src/plugin/setTextValuesOnTarget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin/setTextValuesOnTarget.ts b/src/plugin/setTextValuesOnTarget.ts index 04faed75c..a66f89040 100644 --- a/src/plugin/setTextValuesOnTarget.ts +++ b/src/plugin/setTextValuesOnTarget.ts @@ -78,7 +78,7 @@ export default async function setTextValuesOnTarget( } } if (hasErrored) { - notifyUI(`Error setting font family/weight combination for ${family}/${style}`, { error: true }); + notifyUI(`Error setting font family/weight combination for ${family}/${style}`); trackFromPlugin('Font not found', { family, style }); } } From 1626fe771242e426458c4db3a300499722d3f296 Mon Sep 17 00:00:00 2001 From: Jan Six Date: Wed, 4 Jan 2023 14:18:41 +0100 Subject: [PATCH 04/13] fixes swap styles in component sets and sections --- src/plugin/asyncMessageHandlers/applySiblingStyle.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugin/asyncMessageHandlers/applySiblingStyle.ts b/src/plugin/asyncMessageHandlers/applySiblingStyle.ts index ec01598fb..e81f68d35 100644 --- a/src/plugin/asyncMessageHandlers/applySiblingStyle.ts +++ b/src/plugin/asyncMessageHandlers/applySiblingStyle.ts @@ -45,12 +45,14 @@ export async function applySiblingStyleId(node: BaseNode, styleIds: StyleIdMap, case 'VECTOR': case 'COMPONENT': case 'INSTANCE': + case 'COMPONENT_SET': case 'FRAME': + case 'SECTION': case 'BOOLEAN_OPERATION': { - const newFillStyleId = await getNewStyleId(node.fillStyleId as string, styleIds, styleMap, newTheme); - const newStrokeStyleId = await getNewStyleId(node.strokeStyleId as string, styleIds, styleMap, newTheme); - const newEffectStyleId = await getNewStyleId(node.effectStyleId as string, styleIds, styleMap, newTheme); + const newFillStyleId = 'fillStyleId' in node && await getNewStyleId(node.fillStyleId as string, styleIds, styleMap, newTheme); + const newStrokeStyleId = 'strokeStyleId' in node && await getNewStyleId(node.strokeStyleId as string, styleIds, styleMap, newTheme); + const newEffectStyleId = 'effectStyleId' in node && await getNewStyleId(node.effectStyleId as string, styleIds, styleMap, newTheme); if (newFillStyleId) { node.fillStyleId = newFillStyleId; } @@ -60,7 +62,7 @@ export async function applySiblingStyleId(node: BaseNode, styleIds: StyleIdMap, if (newEffectStyleId) { node.effectStyleId = newEffectStyleId; } - if (['COMPONENT', 'INSTANCE', 'FRAME', 'BOOLEAN_OPERATION'].includes(node.type) && 'children' in node) { + if (['COMPONENT', 'COMPONENT_SET', 'SECTION', 'INSTANCE', 'FRAME', 'BOOLEAN_OPERATION'].includes(node.type) && 'children' in node) { await Promise.all(node.children.map((child) => applySiblingStyleId(child, styleIds, styleMap, newTheme))); } } From a5a02e4155c63bed9b2b5f42d1f01ca020da44d4 Mon Sep 17 00:00:00 2001 From: Hiroshi Date: Thu, 5 Jan 2023 19:22:30 +0900 Subject: [PATCH 05/13] 1507-border-tokens-dont-work-in-composition-tokens (#1519) https://www.loom.com/share/0267bd50e1d44bbab46b5c2b761e1522 --- src/types/CompositionTokenProperty.ts | 6 ++--- .../alias/__tests__/getAliasValue.test.ts | 25 ++++++++++++++++++- src/utils/alias/getAliasValue.ts | 8 +++--- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/types/CompositionTokenProperty.ts b/src/types/CompositionTokenProperty.ts index 44e9d132c..d600e68b4 100644 --- a/src/types/CompositionTokenProperty.ts +++ b/src/types/CompositionTokenProperty.ts @@ -1,10 +1,10 @@ import { Properties } from '@/constants/Properties'; import type { TokenTypes } from '@/constants/TokenTypes'; -import { TokenBoxshadowValue, TokenTypographyValue } from './values'; +import { TokenBoxshadowValue, TokenTypographyValue, TokenBorderValue } from './values'; export type CompositionTokenProperty = keyof typeof Properties; export type CompositionTokenValue = Partial< -Record> -& Record> +Record> +& Record> >; diff --git a/src/utils/alias/__tests__/getAliasValue.test.ts b/src/utils/alias/__tests__/getAliasValue.test.ts index f12a78ffb..44c1518ef 100644 --- a/src/utils/alias/__tests__/getAliasValue.test.ts +++ b/src/utils/alias/__tests__/getAliasValue.test.ts @@ -402,7 +402,30 @@ describe('getAliasValue', () => { value: 'false', input: '{other.false}', }, - + { + name: 'border-token', + input: { + color: '#ffffff', + width: '10px', + style: 'solid', + }, + value: { + color: '#ffffff', + width: '10px', + style: 'solid', + }, + type: TokenTypes.BORDER, + }, + { + name: 'border-alias', + input: '{border-token}', + value: { + color: '#ffffff', + width: '10px', + style: 'solid', + }, + type: TokenTypes.BORDER, + }, ]; allTokens.forEach((token) => { diff --git a/src/utils/alias/getAliasValue.ts b/src/utils/alias/getAliasValue.ts index e80fc9d76..d39261cf3 100644 --- a/src/utils/alias/getAliasValue.ts +++ b/src/utils/alias/getAliasValue.ts @@ -1,6 +1,6 @@ import { TokenTypes } from '@/constants/TokenTypes'; import { SingleToken } from '@/types/tokens'; -import { TokenBoxshadowValue, TokenTypographyValue } from '@/types/values'; +import { TokenBorderValue, TokenBoxshadowValue, TokenTypographyValue } from '@/types/values'; import { convertToRgb } from '../color'; import { findReferences } from '../findReferences'; import { isSingleTokenValueObject } from '../is'; @@ -9,7 +9,7 @@ import { checkAndEvaluateMath } from '../math'; type TokenNameNodeType = string | undefined; function getReturnedValue(token: SingleToken | string | number) { - if (typeof token === 'object' && typeof token.value === 'object' && (token?.type === TokenTypes.BOX_SHADOW || token?.type === TokenTypes.TYPOGRAPHY)) { + if (typeof token === 'object' && typeof token.value === 'object' && (token?.type === TokenTypes.BOX_SHADOW || token?.type === TokenTypes.TYPOGRAPHY || token?.type === TokenTypes.BORDER)) { return token.value; } if (isSingleTokenValueObject(token)) { @@ -19,7 +19,7 @@ function getReturnedValue(token: SingleToken | string | number) { } function replaceAliasWithResolvedReference( - token: string | TokenTypographyValue | TokenBoxshadowValue | TokenBoxshadowValue[] | null, + token: string | TokenTypographyValue | TokenBoxshadowValue | TokenBoxshadowValue[] | TokenBorderValue | null, reference: string, resolvedReference: string | number | TokenBoxshadowValue | TokenBoxshadowValue[] | Record | null, ) { @@ -35,7 +35,7 @@ function replaceAliasWithResolvedReference( } // @TODO This function logic needs to be explained to improve it. It is unclear at this time which cases it needs to handle and how -export function getAliasValue(token: SingleToken | string | number, tokens: SingleToken[] = []): string | number | TokenTypographyValue | TokenBoxshadowValue | Array | null { +export function getAliasValue(token: SingleToken | string | number, tokens: SingleToken[] = []): string | number | TokenTypographyValue | TokenBoxshadowValue | TokenBorderValue | Array | null { // @TODO not sure how this will handle typography and boxShadow values. I don't believe it works. // The logic was copied from the original function in aliases.tsx let returnedValue: ReturnType | null = getReturnedValue(token); From ba9af9a9eb10e02adf3ba8431dd13481e4fec26d Mon Sep 17 00:00:00 2001 From: Hiroshi Date: Fri, 6 Jan 2023 00:16:49 +0900 Subject: [PATCH 06/13] 1517-importing-paragraphindent-not-working-base-release-113 (#1521) --- src/plugin/pullStyles.test.ts | 17 +++++++++++++++++ src/plugin/pullStyles.ts | 6 ++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/plugin/pullStyles.test.ts b/src/plugin/pullStyles.test.ts index c6bb4db06..681e29213 100644 --- a/src/plugin/pullStyles.test.ts +++ b/src/plugin/pullStyles.test.ts @@ -172,6 +172,23 @@ describe('pullStyles', () => { { name: 'paragraphSpacing.2', type: 'paragraphSpacing', value: '7' }], textCase: [{ name: 'textCase.none', type: 'textCase', value: 'none' }], textDecoration: [{ name: 'textDecoration.none', type: 'textDecoration', value: 'none' }], + paragraphIndent: [ + { + name: 'paragraphIndent.0', + type: 'dimension', + value: '0px', + }, + { + name: 'paragraphIndent.1', + type: 'dimension', + value: '3px', + }, + { + name: 'paragraphIndent.2', + type: 'dimension', + value: '7px', + }, + ], }); }); diff --git a/src/plugin/pullStyles.ts b/src/plugin/pullStyles.ts index 858f24151..951c831ee 100644 --- a/src/plugin/pullStyles.ts +++ b/src/plugin/pullStyles.ts @@ -136,7 +136,8 @@ export default function pullStyles(styleTypes: PullStyleOptions): void { .sort((a, b) => a - b) .map((size, idx) => ({ name: `paragraphIndent.${idx}`, - value: size.toString(), + value: `${size.toString()}px`, + type: TokenTypes.DIMENSION, })); letterSpacing = rawLetterSpacing @@ -175,7 +176,7 @@ export default function pullStyles(styleTypes: PullStyleOptions): void { (el: SingleToken) => el.value === style.paragraphSpacing.toString(), ); const foundParagraphIndent = paragraphIndent.find( - (el: SingleToken) => el.value === style.paragraphIndent.toString(), + (el: SingleToken) => el.value === `${style.paragraphIndent.toString()}px`, ); const foundTextCase = textCase.find( (el: SingleToken) => el.value === convertFigmaToTextCase(style.textCase.toString()), @@ -265,6 +266,7 @@ export default function pullStyles(styleTypes: PullStyleOptions): void { typography, textCase, textDecoration, + paragraphIndent, }; type ResultObject = Record; From dcd329b6e54f666fd0091164cef005a64f4fe8fc Mon Sep 17 00:00:00 2001 From: Jan Six Date: Fri, 6 Jan 2023 10:34:24 +0100 Subject: [PATCH 07/13] fixes #1516 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2ab9390aa..7427fa5a8 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "buffer": "^6.0.3", "case": "^1.6.3", "classnames": "^2.3.1", - "color2k": "^1.2.4", + "color2k": "^2.0.1", "core-js": "^3.9.1", "cypress-react-selector": "^2.3.6", "dnd-core": "^12.0.1", diff --git a/yarn.lock b/yarn.lock index 4560fc631..573cb57c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7597,10 +7597,10 @@ color-support@^1.1.2: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color2k@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/color2k/-/color2k-1.2.4.tgz#af34950ac58e23cf224a01cb8dd0c9911a79605e" - integrity sha512-DiwdBwc0BryPFFXoCrW8XQGXl2rEtMToODybxZjKnN5IJXt/tV/FsN02pCK/b7KcWvJEioz3c74lQSmayFvS4Q== +color2k@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color2k/-/color2k-2.0.1.tgz#bea59670b323265cc13a745f239c6bd6a89d9a89" + integrity sha512-iCg+xrEqtYISsSJZN1z44fyhv4EfX8lSkcDhodt6VnMf1+iMwZxAtmGXchTCeMUnTbXunGvUVK6E3skkApPnZw== colorette@^1.2.1, colorette@^1.2.2: version "1.2.2" From c97945c8e08556e5b640c8caccb9a0307c0d37c7 Mon Sep 17 00:00:00 2001 From: Hiroshi Date: Tue, 10 Jan 2023 04:49:49 +0900 Subject: [PATCH 08/13] 1523 border tokens do not offer a way to add alias tokens base 133 (#1530) --- src/app/components/BorderTokenForm.tsx | 111 ++++++++++++++---- src/app/components/EditTokenForm.tsx | 28 +---- .../components/ResolvedBorderValueDisplay.tsx | 50 ++++++++ src/app/components/ResolvedTokenDisplay.tsx | 13 +- src/utils/is/isSingleBorderToken.ts | 2 +- 5 files changed, 156 insertions(+), 48 deletions(-) create mode 100644 src/app/components/ResolvedBorderValueDisplay.tsx diff --git a/src/app/components/BorderTokenForm.tsx b/src/app/components/BorderTokenForm.tsx index 57f1b81b0..947b6b463 100644 --- a/src/app/components/BorderTokenForm.tsx +++ b/src/app/components/BorderTokenForm.tsx @@ -1,6 +1,7 @@ -import React from 'react'; +import React, { useState } from 'react'; import { useUIDSeed } from 'react-uid'; import get from 'just-safe-get'; +import { TokensIcon, LinkBreak2Icon } from '@radix-ui/react-icons'; import { EditTokenObject } from '@/types/tokens'; import Heading from './Heading'; import { TokenTypes } from '@/constants/TokenTypes'; @@ -8,6 +9,11 @@ import { ResolveTokenValuesResult } from '@/plugin/tokenHelpers'; import Stack from './Stack'; import BorderTokenDownShiftInput from './BorderTokenDownShiftInput'; import ColorPicker from './ColorPicker'; +import IconButton from './IconButton'; +import DownshiftInput from './DownshiftInput'; +import ResolvedTokenDisplay from './ResolvedTokenDisplay'; +import { checkIfContainsAlias } from '@/utils/alias'; +import { findReferences } from '@/utils/findReferences'; const propertyTypes = { color: TokenTypes.COLOR, @@ -20,45 +26,108 @@ export default function BorderTokenForm({ resolvedTokens, handleBorderValueChange, handleBorderValueDownShiftInputChange, + handleBorderAliasValueChange, + handleDownShiftInputChange, }: { internalEditToken: Extract; resolvedTokens: ResolveTokenValuesResult[]; handleBorderValueChange: React.ChangeEventHandler; handleBorderValueDownShiftInputChange: (newInputValue: string, property: string) => void; + handleBorderAliasValueChange: (e: React.ChangeEvent) => void; + handleDownShiftInputChange: (newInputValue: string) => void; }) { const seed = useUIDSeed(); - const [inputHelperOpen, setInputHelperOpen] = React.useState(false); + const isAliasMode = (internalEditToken.value && typeof internalEditToken.value === 'string'); + const [mode, setMode] = useState(isAliasMode ? 'alias' : 'input'); + const [alias, setAlias] = useState(''); + const [inputHelperOpen, setInputHelperOpen] = useState(false); + + const selectedToken = React.useMemo(() => { + const search = findReferences(String(internalEditToken.value)); + if (search && search.length > 0) { + const nameToLookFor = search[0].slice(1, search[0].length - 1); + const foundToken = resolvedTokens.find((t) => t.name === nameToLookFor); + if (foundToken) return foundToken; + } + return null; + }, [internalEditToken, resolvedTokens]); const handleToggleInputHelper = React.useCallback(() => setInputHelperOpen(!inputHelperOpen), [inputHelperOpen]); const onColorChange = React.useCallback((color: string) => { handleBorderValueDownShiftInputChange(color, 'color'); - }, [handleBorderValueChange]); + }, [handleBorderValueDownShiftInputChange]); + + const handleMode = React.useCallback(() => { + const changeMode = (mode === 'input') ? 'alias' : 'input'; + setMode(changeMode); + setAlias(''); + }, [mode]); return ( Value - - - {Object.entries(internalEditToken.schema.schemas.value.properties ?? {}).map(([key], keyIndex) => ( - <> - } /> - {inputHelperOpen && key === 'color' && ( + ) : ( + } + /> + ) + } + + {(mode === 'input' && internalEditToken.schema.schemas.value.type === 'object') ? ( + + {Object.entries(internalEditToken.schema.schemas.value.properties ?? {}).map(([key], keyIndex) => ( + <> + + {inputHelperOpen && key === 'color' && ( - )} - - ))} + )} + + ))} + + ) : ( + + - + {isAliasMode && typeof internalEditToken.value === 'string' && checkIfContainsAlias(internalEditToken.value) && ( + + )} + + )} ); } diff --git a/src/app/components/EditTokenForm.tsx b/src/app/components/EditTokenForm.tsx index 9df0076ef..e3183d6d9 100644 --- a/src/app/components/EditTokenForm.tsx +++ b/src/app/components/EditTokenForm.tsx @@ -147,17 +147,6 @@ function EditTokenForm({ resolvedTokens }: Props) { [internalEditToken], ); - const handleBoxShadowAliasValueChange = React.useCallback>( - (e) => { - setError(null); - e.persist(); - if (internalEditToken) { - setInternalEditToken({ ...internalEditToken, [e.target.name]: e.target.value }); - } - }, - [internalEditToken], - ); - const handleColorValueChange = React.useCallback( (color: string) => { setError(null); @@ -184,17 +173,6 @@ function EditTokenForm({ resolvedTokens }: Props) { [internalEditToken], ); - const handleTypographyAliasValueChange = React.useCallback>( - (e) => { - setError(null); - e.persist(); - if (internalEditToken) { - setInternalEditToken({ ...internalEditToken, [e.target.name]: e.target.value }); - } - }, - [internalEditToken], - ); - const handleTypographyValueDownShiftInputChange = React.useCallback((newInputValue: string, property: string) => { if (internalEditToken?.type === TokenTypes.TYPOGRAPHY && typeof internalEditToken?.value !== 'string') { setInternalEditToken({ @@ -384,7 +362,7 @@ function EditTokenForm({ resolvedTokens }: Props) { return ( ); } diff --git a/src/app/components/ResolvedBorderValueDisplay.tsx b/src/app/components/ResolvedBorderValueDisplay.tsx new file mode 100644 index 000000000..5a79cd22c --- /dev/null +++ b/src/app/components/ResolvedBorderValueDisplay.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { useUIDSeed } from 'react-uid'; +import { SingleBorderToken } from '@/types/tokens'; +import { styled } from '@/stitches.config'; +import Box from './Box'; + +type Props = { + value: SingleBorderToken['value'] +}; + +const StyledPropertyItem = styled('div', { + color: '$textSubtle', + marginBottom: '$2', +}); + +const StyledValueItem = styled('div', { + marginBottom: '$2', +}); + +const properties = { + color: 'Border Color', + width: 'Border Width', + style: 'Border Style', +}; + +export const ResolvedBorderValueDisplay: React.FC = ({ value }) => { + const seed = useUIDSeed(); + + return ( + + + {Object.values(properties).map((value) => ( + {value} + ))} + + + + {Object.keys(properties).map((key) => ( + + {value[key as keyof typeof value]} +   + + ))} + + + ); +}; diff --git a/src/app/components/ResolvedTokenDisplay.tsx b/src/app/components/ResolvedTokenDisplay.tsx index 94e54bbd6..08f7361a1 100644 --- a/src/app/components/ResolvedTokenDisplay.tsx +++ b/src/app/components/ResolvedTokenDisplay.tsx @@ -1,11 +1,12 @@ import React from 'react'; import { ResolveTokenValuesResult } from '@/plugin/tokenHelpers'; -import { isSingleBoxShadowToken, isSingleTypographyToken } from '@/utils/is'; -import { SingleTypographyToken } from '@/types/tokens'; +import { isSingleBorderToken, isSingleBoxShadowToken, isSingleTypographyToken } from '@/utils/is'; +import { SingleBorderToken, SingleTypographyToken } from '@/types/tokens'; import Box from './Box'; import { ResolvedShadowValueDisplay } from './ResolvedShadowValueDisplay'; import { ResolvedTypographyValueDisplay } from './ResolvedTypographyValueDisplay'; import { TokenBoxshadowValue } from '@/types/values'; +import { ResolvedBorderValueDisplay } from './ResolvedBorderValueDisplay'; export default function ResolvedTokenDisplay({ alias, @@ -31,6 +32,14 @@ export default function ResolvedTokenDisplay({ return ; } + if (selectedToken && isSingleBorderToken(selectedToken)) { + return ( + + ); + } + if (typeof valueToCheck !== 'string' && typeof valueToCheck !== 'number') { return
{JSON.stringify(valueToCheck, null, 2)}
; } diff --git a/src/utils/is/isSingleBorderToken.ts b/src/utils/is/isSingleBorderToken.ts index e1a9ad6d0..bb8814a3f 100644 --- a/src/utils/is/isSingleBorderToken.ts +++ b/src/utils/is/isSingleBorderToken.ts @@ -4,5 +4,5 @@ import { SingleBorderToken, SingleToken } from '@/types/tokens'; export function isSingleBorderToken(token: SingleToken | any): token is SingleBorderToken { if (typeof token !== 'object') return false; return token.type === TokenTypes.BORDER - && (typeof token.value === 'object' && !('value' in token.value)); + && (typeof token.value === 'string' || (typeof token.value === 'object' && !('value' in token.value))); } From 9837ef8c1605bb52ecee01910c48de9374179402 Mon Sep 17 00:00:00 2001 From: Hiroshi Date: Tue, 10 Jan 2023 04:50:23 +0900 Subject: [PATCH 09/13] 1522-when-applying-border-tokens-we-always-apply-color-as-a-hex-instead-of-style-when-available (#1529) --- src/plugin/__tests__/setValuesOnNode.test.ts | 14 +++---------- src/plugin/node.test.ts | 6 +++--- src/plugin/node.ts | 22 ++++++++++++++++---- src/plugin/setBorderValuesOnTarget.ts | 7 ++----- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/plugin/__tests__/setValuesOnNode.test.ts b/src/plugin/__tests__/setValuesOnNode.test.ts index 1103f78c1..124f9a931 100644 --- a/src/plugin/__tests__/setValuesOnNode.test.ts +++ b/src/plugin/__tests__/setValuesOnNode.test.ts @@ -56,6 +56,7 @@ describe('Can set values on node', () => { effectStyleId: '', fillStyleId: '', strokeStyleId: '', + dashPattern: [], getSharedPluginData: () => '', setSharedPluginData: () => undefined, } as unknown) as RectangleNode; @@ -703,16 +704,7 @@ describe('Can set values on node', () => { }; const data = { border: 'border.regular' }; await setValuesOnNode(solidNodeMock, values, data, emptyStylesMap, emptyThemeInfo); - expect(solidNodeMock.strokes).toEqual([ - { - color: { - b: 1, - g: 1, - r: 1, - }, - opacity: 1, - type: 'SOLID', - }, - ]); + expect(solidNodeMock.strokeWeight).toEqual(12); + expect(solidNodeMock.dashPattern).toEqual([0, 0]); }); }); diff --git a/src/plugin/node.test.ts b/src/plugin/node.test.ts index 9578253ac..5198a4ec6 100644 --- a/src/plugin/node.test.ts +++ b/src/plugin/node.test.ts @@ -1,7 +1,7 @@ import { mockRootSetSharedPluginData } from '../../tests/__mocks__/figmaMock'; import { StorageProviderType } from '@/constants/StorageProviderType'; import { - destructureCompositionToken, mapValuesToTokens, returnValueToLookFor, saveStorageType, saveOnboardingExplainerSets, saveOnboardingExplainerInspect, saveOnboardingExplainerSyncProviders, + destructureToken, mapValuesToTokens, returnValueToLookFor, saveStorageType, saveOnboardingExplainerSets, saveOnboardingExplainerInspect, saveOnboardingExplainerSyncProviders, } from './node'; import getOnboardingExplainer from '@/utils/getOnboardingExplainer'; @@ -183,10 +183,10 @@ describe('mapValuesToTokens', () => { }); }); -describe('destructureCompositionToken', () => { +describe('destructureToken', () => { it('return properties in compositionToken', () => { mappedTokens.forEach((token, index) => { - expect(destructureCompositionToken(token)).toEqual(applyProperties[index]); + expect(destructureToken(token)).toEqual(applyProperties[index]); }); }); }); diff --git a/src/plugin/node.ts b/src/plugin/node.ts index ac169c95b..8b75cfc6a 100644 --- a/src/plugin/node.ts +++ b/src/plugin/node.ts @@ -141,11 +141,14 @@ export function selectNodes(ids: string[]) { figma.currentPage.selection = nodes; } -export function destructureCompositionToken(values: MapValuesToTokensResult): MapValuesToTokensResult { +export function destructureToken(values: MapValuesToTokensResult): MapValuesToTokensResult { const tokensInCompositionToken: Partial< Record & Record > = {}; + if (values && values.border && typeof values.border === 'object' && 'color' in values.border && values.border.color) { + values = { ...values, ...(values.borderColor ? { } : { borderColor: values.border.color }) }; + } if (values && values.composition) { Object.entries(values.composition).forEach(([property, value]) => { tokensInCompositionToken[property as CompositionTokenProperty] = value; @@ -156,7 +159,18 @@ export function destructureCompositionToken(values: MapValuesToTokensResult): Ma return values; } -export function destructureCompositionTokenForAlias(tokens: Map, values: NodeTokenRefMap): NodeTokenRefMap { +export function destructureTokenForAlias(tokens: Map, values: NodeTokenRefMap): MapValuesToTokensResult { + if (values && values.border) { + const resolvedToken = tokens.get(values.border); + if (resolvedToken?.rawValue && typeof resolvedToken.rawValue === 'object' && 'color' in resolvedToken.rawValue && resolvedToken.rawValue.color) { + const { color } = resolvedToken.rawValue; + const { borderColor } = values; + let colorTokenName = color; + if (String(color).startsWith('$')) colorTokenName = String(color).slice(1, String(color).length); + if (String(color).startsWith('{')) colorTokenName = String(color).slice(1, String(color).length - 1); + values = { ...values, ...(borderColor ? { } : { borderColor: String(colorTokenName) }) }; + } + } if (values && values.composition) { const resolvedToken = tokens.get(values.composition); const tokensInCompositionToken: NodeTokenRefMap = {}; @@ -216,9 +230,9 @@ export async function updateNodes( defaultWorker.schedule(async () => { try { if (entry.tokens) { - const mappedTokens = destructureCompositionTokenForAlias(tokens, entry.tokens); + const mappedTokens = destructureTokenForAlias(tokens, entry.tokens); let mappedValues = mapValuesToTokens(tokens, entry.tokens); - mappedValues = destructureCompositionToken(mappedValues); + mappedValues = destructureToken(mappedValues); await migrateTokens(entry, mappedValues, mappedTokens); setValuesOnNode( entry.node, diff --git a/src/plugin/setBorderValuesOnTarget.ts b/src/plugin/setBorderValuesOnTarget.ts index f1f440dde..19056b475 100644 --- a/src/plugin/setBorderValuesOnTarget.ts +++ b/src/plugin/setBorderValuesOnTarget.ts @@ -2,11 +2,11 @@ import { SingleBorderToken } from '@/types/tokens'; import { isPrimitiveValue } from '@/utils/is'; import { transformValue } from './helpers'; -import setColorValuesOnTarget from './setColorValuesOnTarget'; export default function setBorderValuesOnTarget(target: BaseNode, token: Pick, side?: 'top' | 'right' | 'bottom' | 'left') { + // we don't apply borderColor here. we extract borderColor and apply as a normal borderColor token const { value } = token; - const { color, width, style } = value; + const { width, style } = value; try { if ('strokeWeight' in target && typeof width !== 'undefined' && isPrimitiveValue(width) && !side) { target.strokeWeight = transformValue(String(width), 'borderWidth'); @@ -24,9 +24,6 @@ export default function setBorderValuesOnTarget(target: BaseNode, token: Pick Date: Mon, 9 Jan 2023 21:26:05 +0100 Subject: [PATCH 10/13] update wording --- src/app/components/BorderTokenForm.tsx | 4 ++-- src/app/components/BoxShadowInput.tsx | 4 ++-- src/app/components/TypographyInput.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/components/BorderTokenForm.tsx b/src/app/components/BorderTokenForm.tsx index 947b6b463..7c6380c3a 100644 --- a/src/app/components/BorderTokenForm.tsx +++ b/src/app/components/BorderTokenForm.tsx @@ -70,14 +70,14 @@ export default function BorderTokenForm({ { mode === 'input' ? ( } /> ) : ( } diff --git a/src/app/components/BoxShadowInput.tsx b/src/app/components/BoxShadowInput.tsx index 446e4dad2..d0c809946 100644 --- a/src/app/components/BoxShadowInput.tsx +++ b/src/app/components/BoxShadowInput.tsx @@ -75,14 +75,14 @@ export default function BoxShadowInput({ {mode === 'input' ? ( } /> ) : ( } diff --git a/src/app/components/TypographyInput.tsx b/src/app/components/TypographyInput.tsx index bda9c4588..6b7dbf856 100644 --- a/src/app/components/TypographyInput.tsx +++ b/src/app/components/TypographyInput.tsx @@ -69,14 +69,14 @@ export default function TypographyInput({ { mode === 'input' ? ( } /> ) : ( } From aa2246a05ce024b94cd2ea49ab36dc2f4a663416 Mon Sep 17 00:00:00 2001 From: Hiroshi Date: Wed, 11 Jan 2023 07:10:26 +0900 Subject: [PATCH 11/13] 1522 when applying border tokens we always apply color as a hex instead of style when available (#1531) --- .../extractColorInBorderTokenForAlias.ts | 16 +++++++++ src/plugin/node.test.ts | 30 ++++++++++++++++ src/plugin/node.ts | 35 ++++++++++++++----- 3 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 src/plugin/extractColorInBorderTokenForAlias.ts diff --git a/src/plugin/extractColorInBorderTokenForAlias.ts b/src/plugin/extractColorInBorderTokenForAlias.ts new file mode 100644 index 000000000..571ac4f32 --- /dev/null +++ b/src/plugin/extractColorInBorderTokenForAlias.ts @@ -0,0 +1,16 @@ +import { MapValuesToTokensResult } from '@/types'; +import { NodeTokenRefMap } from '@/types/NodeTokenRefMap'; +import { AnyTokenList } from '@/types/tokens'; + +export function extractColorInBorderTokenForAlias(tokens: Map, values: NodeTokenRefMap, borderToken: string): MapValuesToTokensResult { + const resolvedToken = tokens.get(borderToken); + if (resolvedToken?.rawValue && typeof resolvedToken.rawValue === 'object' && 'color' in resolvedToken.rawValue && resolvedToken.rawValue.color) { + const { color } = resolvedToken.rawValue; + const { borderColor } = values; + let colorTokenName = color; + if (String(color).startsWith('$')) colorTokenName = String(color).slice(1, String(color).length); + if (String(color).startsWith('{')) colorTokenName = String(color).slice(1, String(color).length - 1); + values = { ...values, ...(borderColor ? { } : { borderColor: String(colorTokenName) }) }; + } + return values; +} diff --git a/src/plugin/node.test.ts b/src/plugin/node.test.ts index 5198a4ec6..273967c5e 100644 --- a/src/plugin/node.test.ts +++ b/src/plugin/node.test.ts @@ -4,6 +4,7 @@ import { destructureToken, mapValuesToTokens, returnValueToLookFor, saveStorageType, saveOnboardingExplainerSets, saveOnboardingExplainerInspect, saveOnboardingExplainerSyncProviders, } from './node'; import getOnboardingExplainer from '@/utils/getOnboardingExplainer'; +import { TokenTypes } from '@/constants/TokenTypes'; const singleShadowToken = { type: 'boxShadow', @@ -49,6 +50,15 @@ const multipleShadowToken = { ], }; +const borderToken = { + type: TokenTypes.BORDER, + value: { + color: '#ff0000', + width: '12px', + type: 'solid', + }, +}; + const tokens = new Map([ ['global.colors.blue', { @@ -163,6 +173,21 @@ const mappedTokens = [ { boxShadow: multipleShadowToken.value, }, + { + border: borderToken.value, + }, + { + borderTop: borderToken.value, + }, + { + borderRight: borderToken.value, + }, + { + borderLeft: borderToken.value, + }, + { + borderBottom: borderToken.value, + }, ]; const applyProperties = [ @@ -173,6 +198,11 @@ const applyProperties = [ { boxShadow: multipleShadowToken.value }, { boxShadow: singleShadowToken.value }, { boxShadow: multipleShadowToken.value }, + { border: borderToken.value, borderColor: '#ff0000' }, + { borderTop: borderToken.value, borderColor: '#ff0000' }, + { borderRight: borderToken.value, borderColor: '#ff0000' }, + { borderLeft: borderToken.value, borderColor: '#ff0000' }, + { borderBottom: borderToken.value, borderColor: '#ff0000' }, ]; describe('mapValuesToTokens', () => { diff --git a/src/plugin/node.ts b/src/plugin/node.ts index 8b75cfc6a..a4c4b3876 100644 --- a/src/plugin/node.ts +++ b/src/plugin/node.ts @@ -25,6 +25,7 @@ import { import { AsyncMessageChannel } from '@/AsyncMessageChannel'; import { AsyncMessageTypes } from '@/types/AsyncMessages'; import { updatePluginData } from './pluginData'; +import { extractColorInBorderTokenForAlias } from './extractColorInBorderTokenForAlias'; // @TODO fix typings @@ -149,6 +150,18 @@ export function destructureToken(values: MapValuesToTokensResult): MapValuesToTo if (values && values.border && typeof values.border === 'object' && 'color' in values.border && values.border.color) { values = { ...values, ...(values.borderColor ? { } : { borderColor: values.border.color }) }; } + if (values && values.borderTop && typeof values.borderTop === 'object' && 'color' in values.borderTop && values.borderTop.color) { + values = { ...values, ...(values.borderColor ? { } : { borderColor: values.borderTop.color }) }; + } + if (values && values.borderRight && typeof values.borderRight === 'object' && 'color' in values.borderRight && values.borderRight.color) { + values = { ...values, ...(values.borderColor ? { } : { borderColor: values.borderRight.color }) }; + } + if (values && values.borderLeft && typeof values.borderLeft === 'object' && 'color' in values.borderLeft && values.borderLeft.color) { + values = { ...values, ...(values.borderColor ? { } : { borderColor: values.borderLeft.color }) }; + } + if (values && values.borderBottom && typeof values.borderBottom === 'object' && 'color' in values.borderBottom && values.borderBottom.color) { + values = { ...values, ...(values.borderColor ? { } : { borderColor: values.borderBottom.color }) }; + } if (values && values.composition) { Object.entries(values.composition).forEach(([property, value]) => { tokensInCompositionToken[property as CompositionTokenProperty] = value; @@ -161,15 +174,19 @@ export function destructureToken(values: MapValuesToTokensResult): MapValuesToTo export function destructureTokenForAlias(tokens: Map, values: NodeTokenRefMap): MapValuesToTokensResult { if (values && values.border) { - const resolvedToken = tokens.get(values.border); - if (resolvedToken?.rawValue && typeof resolvedToken.rawValue === 'object' && 'color' in resolvedToken.rawValue && resolvedToken.rawValue.color) { - const { color } = resolvedToken.rawValue; - const { borderColor } = values; - let colorTokenName = color; - if (String(color).startsWith('$')) colorTokenName = String(color).slice(1, String(color).length); - if (String(color).startsWith('{')) colorTokenName = String(color).slice(1, String(color).length - 1); - values = { ...values, ...(borderColor ? { } : { borderColor: String(colorTokenName) }) }; - } + values = extractColorInBorderTokenForAlias(tokens, values, values.border); + } + if (values && values.borderTop) { + values = extractColorInBorderTokenForAlias(tokens, values, values.borderTop); + } + if (values && values.borderRight) { + values = extractColorInBorderTokenForAlias(tokens, values, values.borderRight); + } + if (values && values.borderLeft) { + values = extractColorInBorderTokenForAlias(tokens, values, values.borderLeft); + } + if (values && values.borderBottom) { + values = extractColorInBorderTokenForAlias(tokens, values, values.borderBottom); } if (values && values.composition) { const resolvedToken = tokens.get(values.composition); From 8879f3fe725e4a379181e303ca7eea037b7da525 Mon Sep 17 00:00:00 2001 From: Jan Six Date: Tue, 10 Jan 2023 23:17:02 +0100 Subject: [PATCH 12/13] fix default json showing a broken reference --- src/config/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default.json b/src/config/default.json index 4e1284925..6add05a6f 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -690,7 +690,7 @@ } }, "borderRadius": { - "value": "{borderRadius.md}", + "value": "{borderRadius.lg}", "type": "borderRadius" }, "borderWidth": { From 66608a0cd808521233fcf679bfa62b1200d6d222 Mon Sep 17 00:00:00 2001 From: Jan Six Date: Tue, 10 Jan 2023 23:32:54 +0100 Subject: [PATCH 13/13] add intro texts for storage providers --- src/app/components/Link.tsx | 21 +++++++++++++++++++ .../components/StorageItemForm/ADOForm.tsx | 13 ++++++++++++ .../StorageItemForm/GenericVersioned.tsx | 9 ++++++++ .../components/StorageItemForm/GitForm.tsx | 18 ++++++++++++++++ .../StorageItemForm/JSONBinForm.tsx | 11 ++++++++++ .../components/StorageItemForm/URLForm.tsx | 11 ++++++++++ 6 files changed, 83 insertions(+) create mode 100644 src/app/components/Link.tsx diff --git a/src/app/components/Link.tsx b/src/app/components/Link.tsx new file mode 100644 index 000000000..0ad18b323 --- /dev/null +++ b/src/app/components/Link.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { styled } from '@/stitches.config'; + +const StyledLink = styled('a', { + color: '$fgAccent', + textDecoration: 'none', + ':hover': { + textDecoration: 'underline', + }, +}); + +type Props = { + href: string + children: React.ReactNode +}; + +export default function Link({ href, children }: Props) { + return ( + {children} + ); +} diff --git a/src/app/components/StorageItemForm/ADOForm.tsx b/src/app/components/StorageItemForm/ADOForm.tsx index 18ef3c450..c3d23208a 100644 --- a/src/app/components/StorageItemForm/ADOForm.tsx +++ b/src/app/components/StorageItemForm/ADOForm.tsx @@ -6,8 +6,11 @@ import Box from '../Box'; import Button from '../Button'; import Input from '../Input'; import Stack from '../Stack'; +import Text from '../Text'; import { generateId } from '@/utils/generateId'; import { ChangeEventHandler } from './types'; +import Heading from '../Heading'; +import Link from '../Link'; type ValidatedFormValues = Extract, { provider: StorageProviderType.ADO; }>; type Props = { @@ -50,6 +53,16 @@ export default function ADOForm({ return (
+ + + Add new Azure DevOps credentials + + + Access tokens stored on your repository, push and pull tokens in a two-way sync. + {' '} + Read more + + , { provider: StorageProviderType.GENERIC_VERSIONED_STORAGE; }>; type Props = { @@ -120,6 +121,14 @@ export default function GenericVersionedForm({ return ( + + Add a new generic storage provider + + Access tokens stored on your own storage provider, allowing two-way sync, create and read-only operations. + {' '} + Read more + + , { provider: StorageProviderType.GITHUB | StorageProviderType.GITLAB }>; type Props = { @@ -52,6 +56,20 @@ export default function GitForm({ return ( + + + Add new + {' '} + {transformProviderName(values.provider)} + {' '} + credentials + + + Access tokens stored on your repository, push and pull tokens in a two-way sync. + {' '} + Read more + + , { provider: StorageProviderType.JSONBIN; }>; type Props = { @@ -46,6 +49,14 @@ export default function JSONBinForm({ return ( + + Add new JSONBin.io credentials + + Access tokens stored on JSONBin.io, a free JSON storage service for two-way sync. + {' '} + Read more + + , { provider: StorageProviderType.URL; }>; type Props = { @@ -45,6 +48,14 @@ export default function URLForm({ return ( + + Add a new URL provider + + Tokens stored on a server allow you to add them as a read-only provider. + {' '} + Read more + +