From ee8d82399c38670a59efb05049c81f7f52c865a0 Mon Sep 17 00:00:00 2001 From: Hiroshi Oshiro <103296157+robinhoodie0823@users.noreply.github.com> Date: Fri, 7 Jun 2024 14:33:24 +0200 Subject: [PATCH 01/22] 2814 20 add explainer text to apply to menu 1 (#2819) * feat: add token format option to get exact resolved value * revert the DTCG format * feat: update to show explainer description * update applyto menu options and explainer descriptions * update css of ApplyTo menu to support multiple line --- .../src/app/components/ApplySelector.tsx | 19 +++++++++++++++++-- .../src/i18n/lang/en/tokens.json | 15 ++++++++++----- .../src/i18n/lang/es/tokens.json | 15 ++++++++++----- .../src/i18n/lang/fr/tokens.json | 15 ++++++++++----- .../src/i18n/lang/hi/tokens.json | 15 ++++++++++----- .../src/i18n/lang/nl/tokens.json | 15 ++++++++++----- .../src/i18n/lang/zh/tokens.json | 15 ++++++++++----- 7 files changed, 77 insertions(+), 32 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/app/components/ApplySelector.tsx b/packages/tokens-studio-for-figma/src/app/components/ApplySelector.tsx index 1e19167d5..c39539769 100644 --- a/packages/tokens-studio-for-figma/src/app/components/ApplySelector.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/ApplySelector.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { DotFilledIcon } from '@radix-ui/react-icons'; import { useTranslation } from 'react-i18next'; import { - Button, DropdownMenu, Stack, + Button, DropdownMenu, Stack, Box } from '@tokens-studio/ui'; import { Dispatch } from '../store'; import IconChevronDown from '@/icons/chevrondown.svg'; @@ -78,7 +78,7 @@ export default function ApplySelector() { - + {/* TODO: Use DropdownMenu.Label - first add that to `ds` */} {t('applyTo.applyTo')} @@ -91,12 +91,18 @@ export default function ApplySelector() { {t('applyTo.selection.title')} + + {t('applyTo.selection.description')} + {t('applyTo.page.title')} + + {t('applyTo.page.description')} + {t('applyTo.doc.title')} + + {t('applyTo.doc.description')} + @@ -122,6 +131,9 @@ export default function ApplySelector() { {t('applyTo.variablesStyles.title')} + + {t('applyTo.variablesStyles.description')} + {t('applyTo.rawValues.title')} + + {t('applyTo.rawValues.description')} + diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json b/packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json index 397a46e96..db7a7bb8c 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/en/tokens.json @@ -34,19 +34,24 @@ "applyTo": "Apply to", "applyAs": "Apply as", "selection": { - "title": "Selection (fast)" + "title": "Apply to selection", + "description": "Applies current tokens to current selection (fast!)" }, "page": { - "title": "Page" + "title": "Apply to page", + "description": "Applies current tokens to the current page" }, "doc": { - "title": "Document (slow)" + "title": "Apply to document", + "description": "Applies current tokens to the whole document (slow!)" }, "variablesStyles": { - "title": "Variables & styles" + "title": "Apply as variables & styles", + "description": "If a token is connected to a variable or style, the connected variable or style will be applied to the property" }, "rawValues": { - "title": "Raw values" + "title": "Resolved value", + "description": "The resolved value of the token will be applied to the property" } }, "update": { diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/es/tokens.json b/packages/tokens-studio-for-figma/src/i18n/lang/es/tokens.json index d893083f4..25455efb2 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/es/tokens.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/es/tokens.json @@ -32,19 +32,24 @@ "applyTo": "Aplicar a", "applyAs": "Aplicar como", "selection": { - "title": "Selección (rápido)" + "title": "Aplicar a la selección", + "description": "Aplica tokens actuales a la selección actual (¡rápido!)" }, "page": { - "title": "Página" + "title": "Aplicar a la página", + "description": "Aplica tokens actuales a la página actual" }, "doc": { - "title": "Documento (lento)" + "title": "Aplicar al documento", + "description": "Aplica tokens actuales a todo el documento (¡lento!)" }, "variablesStyles": { - "title": "Variables y estilos" + "title": "Aplicar como variables y estilos", + "description": "Si un token está conectado a una variable o estilo, la variable o estilo conectado se aplicará a la propiedad" }, "rawValues": { - "title": "Valores en bruto" + "title": "Valor resuelto", + "description": "El valor resuelto del token se aplicará a la propiedad." } }, "update": { diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/fr/tokens.json b/packages/tokens-studio-for-figma/src/i18n/lang/fr/tokens.json index 069a1fa35..956f90578 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/fr/tokens.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/fr/tokens.json @@ -32,19 +32,24 @@ "applyTo": "Appliquer à", "applyAs": "Appliquer en tant que", "selection": { - "title": "Sélection (rapide)" + "title": "Appliquer à la sélection", + "description": "Applique les jetons actuels à la sélection actuelle (rapide !)" }, "page": { - "title": "Page" + "title": "Appliquer à la page", + "description": "Applique les jetons actuels à la page actuelle" }, "doc": { - "title": "Document (lent)" + "title": "Appliquer au document", + "description": "Applique les jetons actuels à l'ensemble du document (lent !)" }, "variablesStyles": { - "title": "Variables et styles" + "title": "Appliquer en tant que variables et styles", + "description": "Si un jeton est connecté à une variable ou à un style, la variable ou le style connecté sera appliqué à la propriété" }, "rawValues": { - "title": "Valeurs brutes" + "title": "Valeur résolue", + "description": "La valeur résolue du jeton sera appliquée à la propriété" } }, "update": { diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/hi/tokens.json b/packages/tokens-studio-for-figma/src/i18n/lang/hi/tokens.json index 35c05ab0c..8fee8485e 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/hi/tokens.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/hi/tokens.json @@ -32,19 +32,24 @@ "applyTo": "लागू करें", "applyAs": "लागू करें जैसा", "selection": { - "title": "चयन (त्वरित)" + "title": "चयन के लिए आवेदन करें", + "description": "वर्तमान चयन पर वर्तमान टोकन लागू करता है (तेजी से!)" }, "page": { - "title": "पृष्ठ" + "title": "पेज पर लागू करें", + "description": "वर्तमान टोकन को वर्तमान पृष्ठ पर लागू करता है" }, "doc": { - "title": "दस्तावेज़ (धीमा)" + "title": "दस्तावेज़ पर लागू करें", + "description": "संपूर्ण दस्तावेज़ पर वर्तमान टोकन लागू करता है (धीमा!)" }, "variablesStyles": { - "title": "चर और शैलियों" + "title": "चर और शैलियों के रूप में लागू करें", + "description": "यदि कोई टोकन किसी वेरिएबल या स्टाइल से जुड़ा है, तो कनेक्टेड वेरिएबल या स्टाइल प्रॉपर्टी पर लागू किया जाएगा" }, "rawValues": { - "title": "कच्चे मान" + "title": "हल किया गया मान", + "description": "टोकन का हल किया गया मूल्य संपत्ति पर लागू किया जाएगा" } }, "update": { diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/nl/tokens.json b/packages/tokens-studio-for-figma/src/i18n/lang/nl/tokens.json index d36032ba8..1034a969c 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/nl/tokens.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/nl/tokens.json @@ -32,19 +32,24 @@ "applyTo": "Toepassen op", "applyAs": "Toepassen als", "selection": { - "title": "Selectie (snel)" + "title": "Toepassen op selectie", + "description": "Past huidige tokens toe op huidige selectie (snel!)" }, "page": { - "title": "Pagina" + "title": "Toepassen op pagina", + "description": "Past huidige tokens toe op de huidige pagina" }, "doc": { - "title": "Document (langzaam)" + "title": "Toepassen op document", + "description": "Past huidige tokens toe op het hele document (langzaam!)" }, "variablesStyles": { - "title": "Variabelen & stijlen" + "title": "Toepassen als variabelen en stijlen", + "description": "Als een token aan een variabele of stijl is gekoppeld, wordt de gekoppelde variabele of stijl op de eigenschap toegepast" }, "rawValues": { - "title": "Ruwe waarden" + "title": "Opgeloste waarde", + "description": "De opgeloste waarde van het token wordt op de eigenschap toegepast" } }, "update": { diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/zh/tokens.json b/packages/tokens-studio-for-figma/src/i18n/lang/zh/tokens.json index 4c9310fca..a5aa41a16 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/zh/tokens.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/zh/tokens.json @@ -32,19 +32,24 @@ "applyTo": "应用于", "applyAs": "应用为", "selection": { - "title": "选择 (快速)" + "title": "应用至当前选择", + "description": "将当前标记应用于当前选择(快!)" }, "page": { - "title": "页面" + "title": "应用至页面", + "description": "将当前 Token 应用于当前页面" }, "doc": { - "title": "文档 (较慢)" + "title": "应用至文档", + "description": "将当前标记应用于整个文档(慢!)" }, "variablesStyles": { - "title": "变量和样式" + "title": "作为变量和样式应用", + "description": "如果令牌连接到变量或样式,则连接的变量或样式将应用于属性" }, "rawValues": { - "title": "原始值" + "title": "分辨值", + "description": "令牌的解析值将应用于该属性" } }, "update": { From 7614ab0a9aa1afae0d37444b30679701a53c3951 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Mon, 10 Jun 2024 00:37:48 +0530 Subject: [PATCH 02/22] fix: border token input form color swatch to update on references --- .../components/BorderTokenDownShiftInput.tsx | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/app/components/BorderTokenDownShiftInput.tsx b/packages/tokens-studio-for-figma/src/app/components/BorderTokenDownShiftInput.tsx index 69b802ac3..86c24b455 100644 --- a/packages/tokens-studio-for-figma/src/app/components/BorderTokenDownShiftInput.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/BorderTokenDownShiftInput.tsx @@ -4,6 +4,7 @@ import { ResolveTokenValuesResult } from '@/utils/tokenHelpers'; import DownshiftInput from './DownshiftInput'; import { getLabelForProperty } from '@/utils/getLabelForProperty'; import { styled } from '@/stitches.config'; +import { getAliasValue } from '@/utils/alias'; const StyledButton = styled('button', { display: 'block', @@ -23,16 +24,30 @@ export default function BorderTokenDownShiftInput({ handleToggleInputHelper, onSubmit, }: { - name: string, + name: string; value: string; type: string; resolvedTokens: ResolveTokenValuesResult[]; handleChange: (property: string, value: string) => void; setInputValue: (newInputValue: string, property: string) => void; handleToggleInputHelper?: () => void; - onSubmit: () => void + onSubmit: () => void; }) { - const handleBorderDownShiftInputChange = React.useCallback((newInputValue: string) => setInputValue(newInputValue, name), [name, setInputValue]); + const [resolvedColor, setResolvedColor] = React.useState(value ? String(value) : ''); + + React.useEffect(() => { + if (name === 'color' && value && value.startsWith('{')) { + const aliasValue = getAliasValue(value, resolvedTokens); + setResolvedColor(aliasValue ? String(aliasValue) : ''); + } else { + setResolvedColor(typeof value === 'string' ? value : ''); + } + }, [value, resolvedTokens, name]); + + const handleBorderDownShiftInputChange = React.useCallback( + (newInputValue: string) => setInputValue(newInputValue, name), + [name, setInputValue], + ); const getIconComponent = React.useMemo(() => getLabelForProperty(name), [name]); const { t } = useTranslation(['tokens']); @@ -57,7 +72,7 @@ export default function BorderTokenDownShiftInput({ name === 'color' && ( {value} From cdb57085301fb479105d480954cfd66446221a1f Mon Sep 17 00:00:00 2001 From: Hiroshi Oshiro <103296157+robinhoodie0823@users.noreply.github.com> Date: Mon, 10 Jun 2024 08:42:51 +0200 Subject: [PATCH 03/22] 2707 removing broken references from inspect panel removes all tokens (#2821) * feat: add token format option to get exact resolved value * revert the DTCG format * refactor to remove only selected inspect tokens * Create few-buses-impress.md --------- Co-authored-by: Jan Six --- .changeset/few-buses-impress.md | 5 +++++ .../src/app/components/InspectorMultiView.tsx | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 .changeset/few-buses-impress.md diff --git a/.changeset/few-buses-impress.md b/.changeset/few-buses-impress.md new file mode 100644 index 000000000..b7b7d3eda --- /dev/null +++ b/.changeset/few-buses-impress.md @@ -0,0 +1,5 @@ +--- +"@tokens-studio/figma-plugin": patch +--- + +Using Select all in Inspect view when you were using a filtered view is now correctly only removing tokens that were selected, instead of all. diff --git a/packages/tokens-studio-for-figma/src/app/components/InspectorMultiView.tsx b/packages/tokens-studio-for-figma/src/app/components/InspectorMultiView.tsx index 7e392d1e8..eb7a2be0a 100644 --- a/packages/tokens-studio-for-figma/src/app/components/InspectorMultiView.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/InspectorMultiView.tsx @@ -83,11 +83,11 @@ export default function InspectorMultiView({ resolvedTokens, tokenToSearch }: { const handleSelectAll = React.useCallback(() => { dispatch.inspectState.setSelectedTokens( - inspectState.selectedTokens.length === uiState.selectionValues.length + inspectState.selectedTokens.length === filteredSelectionValues.length ? [] - : uiState.selectionValues.map((v) => `${v.category}-${v.value}`), + : filteredSelectionValues.map((v) => `${v.category}-${v.value}`), ); - }, [dispatch.inspectState, inspectState.selectedTokens.length, uiState.selectionValues]); + }, [dispatch.inspectState, inspectState.selectedTokens.length, filteredSelectionValues]); const closeOnboarding = React.useCallback(() => { dispatch.uiState.setOnboardingExplainerInspect(false); From 9c148358b946559fc583f5199d4d5d81f0bc1460 Mon Sep 17 00:00:00 2001 From: Celia Usero Navarro <114073780+cuserox@users.noreply.github.com> Date: Mon, 10 Jun 2024 07:45:42 +0100 Subject: [PATCH 04/22] fix: prevent errors from appearing while editing JSON (#2824) * fix: remove token parsing when json changes * fix: include token parsing validation on json save --- .../src/app/components/Tokens.tsx | 12 +------- .../src/app/components/TokensBottomBar.tsx | 30 ++++++++++++++----- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/app/components/Tokens.tsx b/packages/tokens-studio-for-figma/src/app/components/Tokens.tsx index 85d6b1ef4..e871c6ac8 100644 --- a/packages/tokens-studio-for-figma/src/app/components/Tokens.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/Tokens.tsx @@ -17,8 +17,6 @@ import JSONEditor from './JSONEditor'; import Box from './Box'; import IconListing from '@/icons/listing.svg'; import useTokens from '../store/useTokens'; -import parseTokenValues from '@/utils/parseTokenValues'; -import parseJson from '@/utils/parseJson'; import AttentionIcon from '@/icons/attention.svg'; import { TokensContext } from '@/context'; import { @@ -127,12 +125,6 @@ function Tokens({ isActive }: { isActive: boolean }) { const handleChangeJSON = React.useCallback((val: string) => { setError(null); - try { - const parsedTokens = parseJson(val); - parseTokenValues(parsedTokens); - } catch (e) { - setError(`Unable to read JSON: ${JSON.stringify(e)}`); - } dispatch.tokenState.setStringTokens(val); }, [dispatch.tokenState]); @@ -311,9 +303,7 @@ function Tokens({ isActive }: { isActive: boolean }) { {manageThemesModalOpen && } - + ); diff --git a/packages/tokens-studio-for-figma/src/app/components/TokensBottomBar.tsx b/packages/tokens-studio-for-figma/src/app/components/TokensBottomBar.tsx index d17e41161..e2ea5c7d3 100644 --- a/packages/tokens-studio-for-figma/src/app/components/TokensBottomBar.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/TokensBottomBar.tsx @@ -1,32 +1,46 @@ import React, { useCallback } from 'react'; import { useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; + +// Components import { Button } from '@tokens-studio/ui'; import ApplySelector from './ApplySelector'; import Box from './Box'; import StylesDropdown from './StylesDropdown'; -import { hasUnsavedChangesSelector } from '@/selectors'; import Stack from './Stack'; import SettingsDropdown from './SettingsDropdown'; +import ToolsDropdown from './ToolsDropdown'; + +// State import useTokens from '../store/useTokens'; +import { hasUnsavedChangesSelector } from '@/selectors'; import { stringTokensSelector } from '@/selectors/stringTokensSelector'; -import ToolsDropdown from './ToolsDropdown'; + +// Utils import { track } from '@/utils/analytics'; +import parseTokenValues from '@/utils/parseTokenValues'; +import parseJson from '@/utils/parseJson'; type Props = { - hasJSONError: boolean; + handleError: (error: string) => void; }; -export default function TokensBottomBar({ hasJSONError }: Props) { +export default function TokensBottomBar({ handleError }: Props) { const hasUnsavedChanges = useSelector(hasUnsavedChangesSelector); const stringTokens = useSelector(stringTokensSelector); const { handleJSONUpdate } = useTokens(); const handleSaveJSON = useCallback(() => { - track('Saved in JSON'); - handleJSONUpdate(stringTokens); - }, [handleJSONUpdate, stringTokens]); + try { + const parsedTokens = parseJson(stringTokens); + parseTokenValues(parsedTokens); + track('Saved in JSON'); + handleJSONUpdate(stringTokens); + } catch (e) { + handleError(`Unable to read JSON: ${JSON.stringify(e)}`); + } + }, [handleError, handleJSONUpdate, stringTokens]); const { t } = useTranslation(['general']); @@ -47,7 +61,7 @@ export default function TokensBottomBar({ hasJSONError }: Props) { }} > {t('unsavedChanges')} - + + + + + {t('addNewSyncProvider')} + + + { + providers.map((provider) => ( + + + + {getProviderIcon(provider.type)} + {provider.text} + {provider.beta && BETA} + + {provider.description && {provider.description}} + + + + )) + } + + + + + {apiProviders.length > 0 && apiProviders.map((item) => ( @@ -142,28 +193,6 @@ const SyncSettings = () => { /> ))} - - - - - - - { - providers.map((provider) => ( - - {getProviderIcon(provider.type)} - {provider.text} - - )) - } - - - diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json b/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json index a9903e85f..794bb2483 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json @@ -66,6 +66,7 @@ "selectALayerToSeeAppliedTokens": "Select a layer to see applied tokens", "localDocument": "Local document", "apply": "Apply", + "choose": "Choose", "confirmDelete": "Do you really want to delete this?", "active": "Active", "edit": "Edit", From 162bba92deb350a6938bf64981aa5a4fb2052c55 Mon Sep 17 00:00:00 2001 From: Luke Finch Date: Mon, 10 Jun 2024 15:29:11 +0100 Subject: [PATCH 12/22] Revert "move options to modal" This reverts commit 33788b92ade46462256e8c4532675a06e008a5d8. --- .../src/app/components/SyncSettings.tsx | 81 ++++++------------- .../src/i18n/lang/en/storage.json | 1 - 2 files changed, 26 insertions(+), 56 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx b/packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx index a11991d6a..0dd22c284 100644 --- a/packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx @@ -3,7 +3,7 @@ import React, { useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import { - Heading, Button, Box, Stack, Text, Dialog, + DropdownMenu, Heading, Button, Box, Stack, } from '@tokens-studio/ui'; import { track } from '@/utils/analytics'; import StorageItem from './StorageItem'; @@ -16,7 +16,6 @@ import useRemoteTokens from '../store/remoteTokens'; import { StorageTypeCredentials } from '@/types/StorageType'; import LocalStorageItem from './LocalStorageItem'; import { getProviderIcon } from '@/utils/getProviderIcon'; -import { StyledBetaBadge } from './SecondScreen'; const SyncSettings = () => { const localApiState = useSelector(localApiStateSelector); @@ -45,9 +44,8 @@ const SyncSettings = () => { type: StorageProviderType.ADO, }, { - text: 'BitBucket', + text: 'BitBucket (Beta)', type: StorageProviderType.BITBUCKET, - beta: true, }, { text: 'Supernova', @@ -56,20 +54,16 @@ const SyncSettings = () => { { text: t('providers.generic.title'), type: StorageProviderType.GENERIC_VERSIONED_STORAGE, - description: t('providers.generic.description'), }, { - text: 'Tokens Studio', + text: 'Tokens Studio (Beta)', type: StorageProviderType.TOKENS_STUDIO, - beta: true, }, ], [t]); const apiProviders = useSelector(apiProvidersSelector); const dispatch = useDispatch(); - const [open, setOpen] = React.useState(false); - const { fetchBranches } = useRemoteTokens(); const [editStorageItemModalVisible, setShowEditStorageModalVisible] = React.useState(Boolean(localApiState.new)); @@ -103,7 +97,6 @@ const SyncSettings = () => { const handleProviderClick = React.useCallback( (provider: StorageProviderType) => () => { - setOpen(false); setStorageProvider(provider); handleShowAddCredentials(provider); }, @@ -138,51 +131,7 @@ const SyncSettings = () => { )} - - {t('syncProviders')} - - - - - - - - {t('addNewSyncProvider')} - - - { - providers.map((provider) => ( - - - - {getProviderIcon(provider.type)} - {provider.text} - {provider.beta && BETA} - - {provider.description && {provider.description}} - - - - )) - } - - - - - + {t('syncProviders')} {apiProviders.length > 0 && apiProviders.map((item) => ( @@ -193,6 +142,28 @@ const SyncSettings = () => { /> ))} + + + + + + + { + providers.map((provider) => ( + + {getProviderIcon(provider.type)} + {provider.text} + + )) + } + + + diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json b/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json index 794bb2483..a9903e85f 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json @@ -66,7 +66,6 @@ "selectALayerToSeeAppliedTokens": "Select a layer to see applied tokens", "localDocument": "Local document", "apply": "Apply", - "choose": "Choose", "confirmDelete": "Do you really want to delete this?", "active": "Active", "edit": "Edit", From 9e867dadeb8480ceb63f83de2f8649fb8796ae24 Mon Sep 17 00:00:00 2001 From: Jan Six Date: Tue, 11 Jun 2024 09:37:41 +0200 Subject: [PATCH 13/22] fix border token input --- .../components/BorderTokenDownShiftInput.tsx | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/app/components/BorderTokenDownShiftInput.tsx b/packages/tokens-studio-for-figma/src/app/components/BorderTokenDownShiftInput.tsx index 86c24b455..b237ad318 100644 --- a/packages/tokens-studio-for-figma/src/app/components/BorderTokenDownShiftInput.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/BorderTokenDownShiftInput.tsx @@ -5,14 +5,7 @@ import DownshiftInput from './DownshiftInput'; import { getLabelForProperty } from '@/utils/getLabelForProperty'; import { styled } from '@/stitches.config'; import { getAliasValue } from '@/utils/alias'; - -const StyledButton = styled('button', { - display: 'block', - width: '1.5rem', - height: '1.5rem', - borderRadius: '$small', - cursor: 'pointer', -}); +import { ColorPickerTrigger } from './ColorPickerTrigger'; export default function BorderTokenDownShiftInput({ name, @@ -70,13 +63,7 @@ export default function BorderTokenDownShiftInput({ placeholder={mapTypeToPlaceHolder[name as keyof typeof mapTypeToPlaceHolder] as unknown as string} prefix={ name === 'color' && ( - - {value} - + ) } suffix From 85ee66cc86f2155f761213e9270bcd10e3b89919 Mon Sep 17 00:00:00 2001 From: Hiroshi Oshiro <103296157+robinhoodie0823@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:18:54 +0200 Subject: [PATCH 14/22] 2820 lineheight variables not binding and applying with styles (#2835) * feat: add token format option to get exact resolved value * revert the DTCG format * add logs to figure out issue * feat: update applyTypographyTokenOnNode function to handle direct typography property tokens * update the logic to pass correct valueObject and resolvedObject * update test case to follow the logic --- .../src/plugin/applyTypographyTokenOnNode.ts | 14 ++++++++++++-- .../tryApplyTypographyCompositeVariable.test.ts | 8 ++++---- .../plugin/tryApplyTypographyCompositeVariable.ts | 6 +++--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts index c98ebf28f..9aa562477 100644 --- a/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts +++ b/packages/tokens-studio-for-figma/src/plugin/applyTypographyTokenOnNode.ts @@ -57,6 +57,16 @@ export async function applyTypographyTokenOnNode( || values.textCase || values.textDecoration ) { + 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, @@ -70,8 +80,8 @@ export async function applyTypographyTokenOnNode( await tryApplyTypographyCompositeVariable({ target: node, value: valueObject, - resolvedValue: valueObject, + resolvedValue: resolvedValueObject, baseFontSize, }); } -} +} \ No newline at end of file diff --git a/packages/tokens-studio-for-figma/src/plugin/tryApplyTypographyCompositeVariable.test.ts b/packages/tokens-studio-for-figma/src/plugin/tryApplyTypographyCompositeVariable.test.ts index 4e92277dd..50d57d330 100644 --- a/packages/tokens-studio-for-figma/src/plugin/tryApplyTypographyCompositeVariable.test.ts +++ b/packages/tokens-studio-for-figma/src/plugin/tryApplyTypographyCompositeVariable.test.ts @@ -45,14 +45,14 @@ describe('tryApplyTypographyCompositeVariable', () => { }, setBoundVariable: jest.fn(), } as TextNode | TextStyle; - resolvedValue = { - fontFamily: '{fontFamily.default}', - fontWeight: '{fontWeight.default}', - }; value = { fontFamily: 'Roboto-raw', fontWeight: 'Bold-raw', }; + resolvedValue = { + fontFamily: '{fontFamily.default}', + fontWeight: '{fontWeight.default}', + }; defaultTokenValueRetriever.getVariableReference = jest.fn().mockResolvedValue('Roboto'); defaultTokenValueRetriever.getVariableReference = jest.fn() .mockResolvedValueOnce('Roboto') diff --git a/packages/tokens-studio-for-figma/src/plugin/tryApplyTypographyCompositeVariable.ts b/packages/tokens-studio-for-figma/src/plugin/tryApplyTypographyCompositeVariable.ts index 1f13a5684..29d92eed9 100644 --- a/packages/tokens-studio-for-figma/src/plugin/tryApplyTypographyCompositeVariable.ts +++ b/packages/tokens-studio-for-figma/src/plugin/tryApplyTypographyCompositeVariable.ts @@ -29,11 +29,11 @@ export async function tryApplyTypographyCompositeVariable({ setFontStyleOnTarget({ target, value: { fontFamily: value.fontFamily, fontWeight: value.fontWeight }, baseFontSize }); } // Then we iterate over all keys of the typography object and apply variables if available, otherwise we apply the value directly - for (const [originalKey, val] of Object.entries(resolvedValue).filter(([_, keyValue]) => typeof keyValue !== 'undefined')) { + for (const [originalKey, val] of Object.entries(value).filter(([_, keyValue]) => typeof keyValue !== 'undefined')) { if (typeof val === 'undefined') return; let successfullyAppliedVariable = false; - if (val.toString().startsWith('{') && val.toString().endsWith('}') && shouldCreateStylesWithVariables) { - const variableToApply = await defaultTokenValueRetriever.getVariableReference(val.toString().slice(1, -1)); + if (resolvedValue[originalKey].toString().startsWith('{') && resolvedValue[originalKey].toString().endsWith('}') && shouldCreateStylesWithVariables) { + const variableToApply = await defaultTokenValueRetriever.getVariableReference(resolvedValue[originalKey].toString().slice(1, -1)); const key = transformTypographyKeyToFigmaVariable(originalKey, variableToApply); if (variableToApply) { if (target.fontName !== figma.mixed) await figma.loadFontAsync(target.fontName); From 4139982641c8bc6e60eb974deedfaf5d13e23a87 Mon Sep 17 00:00:00 2001 From: Hiroshi Oshiro <103296157+robinhoodie0823@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:22:47 +0200 Subject: [PATCH 15/22] 2838 20 no variables created note does not reflect actual created count (#2840) * feat: add token format option to get exact resolved value * revert the DTCG format * update the calculating logic to calculate the newly created variables correctly --- .../src/plugin/createLocalVariablesInPlugin.ts | 12 ++++++++++-- .../createLocalVariablesWithoutModesInPlugin.ts | 13 +++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesInPlugin.ts b/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesInPlugin.ts index 2a92e9fae..c4d0f6c95 100644 --- a/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesInPlugin.ts +++ b/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesInPlugin.ts @@ -33,6 +33,10 @@ export default async function createLocalVariablesInPlugin(tokens: Record 0) { @@ -46,6 +50,7 @@ export default async function createLocalVariablesInPlugin(tokens: Record 0) { allVariableCollectionIds[theme.id] = { collectionId: collection.id, @@ -61,10 +66,13 @@ export default async function createLocalVariablesInPlugin(tokens: Record 0) { allVariableCollectionIds[index] = { collectionId: collection.id, @@ -67,10 +72,14 @@ export default async function createLocalVariablesWithoutModesInPlugin(tokens: R const existingVariables = await mergeVariableReferencesWithLocalVariables(); updatedVariables = await updateVariablesToReference(existingVariables, referenceVariableCandidates); } - if (updatedVariables.length === 0) { + + figmaVariablesAfterCreate += figma.variables.getLocalVariables()?.length; + const figmaVariableCollectionsAfterCreate = figma.variables.getLocalVariableCollections()?.length; + + if (figmaVariablesAfterCreate === figmaVariablesBeforeCreate) { notifyUI('No variables were created'); } else { - notifyUI(`${updatedVariableCollections.length} collections and ${updatedVariables.length} variables created`); + notifyUI(`${figmaVariableCollectionsAfterCreate - figmaVariableCollectionsBeforeCreate} collections and ${figmaVariablesAfterCreate - figmaVariablesBeforeCreate} variables created`); } return { allVariableCollectionIds, From 02981de349f57287623789cc2ce80bda1a7bd28c Mon Sep 17 00:00:00 2001 From: Hiroshi Oshiro <103296157+robinhoodie0823@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:27:20 +0200 Subject: [PATCH 16/22] 2830 20 font weight tokens using numbers like 300 are not created properly (#2836) * feat: add token format option to get exact resolved value * revert the DTCG format * refactor to use correct type of parameter * clean code * update to create fontWeight tokens as numeric variables * add support to create string variables * feat: update function to accept fontWeight value --- .../src/plugin/setValuesOnVariable.test.ts | 13 +++++++++++++ .../src/utils/convertTokenTypeToVariableType.ts | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/setValuesOnVariable.test.ts b/packages/tokens-studio-for-figma/src/plugin/setValuesOnVariable.test.ts index 8fb7a2fbc..fa60875d0 100644 --- a/packages/tokens-studio-for-figma/src/plugin/setValuesOnVariable.test.ts +++ b/packages/tokens-studio-for-figma/src/plugin/setValuesOnVariable.test.ts @@ -70,4 +70,17 @@ describe('SetValuesOnVariable', () => { expect(variablesInFigma[0].name).toEqual('button/primary/height'); expect(mockCreateVariable).not.toBeCalled(); }); + + it('should apply fontWeight token with numeric value', async () => { + const tokens = [{ + name: 'global.fontWeight', + path: 'global/fontWeight', + value: 300, + rawValue: 300, + type: TokenTypes.FONT_WEIGHTS, + variableId: '1234' + }]; + await setValuesOnVariable(variablesInFigma, tokens, collection, mode); + expect(mockCreateVariable).toBeCalledWith('global/fontWeight', collection, 'FLOAT'); + }) }); diff --git a/packages/tokens-studio-for-figma/src/utils/convertTokenTypeToVariableType.ts b/packages/tokens-studio-for-figma/src/utils/convertTokenTypeToVariableType.ts index faf290430..e9e73bdec 100644 --- a/packages/tokens-studio-for-figma/src/utils/convertTokenTypeToVariableType.ts +++ b/packages/tokens-studio-for-figma/src/utils/convertTokenTypeToVariableType.ts @@ -3,7 +3,7 @@ import { SingleToken } from '@/types/tokens'; export function convertTokenTypeToVariableType(type: TokenTypes, value: SingleToken['value']): VariableResolvedDataType { // For numerical font weights we want to create a float variable - if (type === TokenTypes.FONT_WEIGHTS && typeof value === 'number' && parseFloat(value)) { + if (type === TokenTypes.FONT_WEIGHTS && parseFloat(String(value))) { return 'FLOAT'; } From 587f7a0236d6ef46691130b4bad36e7f13952918 Mon Sep 17 00:00:00 2001 From: Hiroshi Oshiro <103296157+robinhoodie0823@users.noreply.github.com> Date: Wed, 12 Jun 2024 22:03:55 +0200 Subject: [PATCH 17/22] 2837 20 unable to create variable collections from sets (#2844) * feat: add token format option to get exact resolved value * revert the DTCG format * feat: add console logs to figure out issue * feat: add logic to create collection based on selected sets * feat: update to create all tokens as variables --- ...LocalVariablesWithoutModesInPlugin.test.ts | 64 +++++++++++++++++++ ...reateLocalVariablesWithoutModesInPlugin.ts | 23 ++++++- 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 packages/tokens-studio-for-figma/src/plugin/createLocalVariablesWithoutModesInPlugin.test.ts diff --git a/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesWithoutModesInPlugin.test.ts b/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesWithoutModesInPlugin.test.ts new file mode 100644 index 000000000..aa523a165 --- /dev/null +++ b/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesWithoutModesInPlugin.test.ts @@ -0,0 +1,64 @@ +import { mockCreateVariableCollection, mockGetLocalVariableCollections } from '../../tests/__mocks__/figmaMock'; +import { TokenSetStatus } from '@/constants/TokenSetStatus'; +import { TokenTypes } from '@/constants/TokenTypes'; +import createLocalVariablesWithoutModesInPlugin from './createLocalVariablesWithoutModesInPlugin'; +import { SingleToken } from '@/types/tokens'; +import { SettingsState } from '@/app/store/models/settings'; + +describe('createLocalVariablesWithoutModesInPlugin', () => { + const mockRenameMode = jest.fn(); + + const tokens = { + global: [ + { + name: 'button.primary.borderRadius', + value: '8', + type: TokenTypes.BORDER_RADIUS, + } as SingleToken, + { + name: 'button.primary.width', + value: '16', + type: TokenTypes.SIZING, + } as SingleToken, + ], + }; + const settings = { + baseFontSize: '16', + } as SettingsState; + + it('when there is no collection which correspond to the theme, then we create a new collection', async () => { + const mockLocalVariableCollections = [ + { + id: 'VariableCollectionId:334:16746', + modes: [ + { + name: 'light', + modeId: '123', + }, + ], + name: 'core', + }, + ]; + const mockNewCollection = { + id: 'VariableCollectionId:334:16723', + modes: [ + { + name: 'new Mode', + modeId: '123', + }, + ], + name: 'color', + renameMode: mockRenameMode, + } as unknown as VariableCollection; + mockGetLocalVariableCollections.mockImplementationOnce(() => mockLocalVariableCollections); + mockCreateVariableCollection.mockImplementationOnce(() => mockNewCollection); + const selectedSets = [{ + set: 'global', + status: TokenSetStatus.ENABLED, + }] + expect(await createLocalVariablesWithoutModesInPlugin(tokens, settings, selectedSets)).toEqual({ + allVariableCollectionIds: {}, + totalVariables: 0, + }); + }); +}); diff --git a/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesWithoutModesInPlugin.ts b/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesWithoutModesInPlugin.ts index 506a84c7e..f511ad751 100644 --- a/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesWithoutModesInPlugin.ts +++ b/packages/tokens-studio-for-figma/src/plugin/createLocalVariablesWithoutModesInPlugin.ts @@ -34,6 +34,18 @@ export default async function createLocalVariablesWithoutModesInPlugin(tokens: R const checkSetting = !settings.variablesBoolean && !settings.variablesColor && !settings.variablesNumber && !settings.variablesString; if (!checkSetting) { + const themesToCreateCollections = selectedSets.reduce((acc: ThemeObject[], curr: ExportTokenSet) => { + if (curr.status === TokenSetStatus.ENABLED) { + acc.push({ + selectedTokenSets: { + [curr.set]: curr.status, + }, + id: curr.set, + name: curr.set + }) + } + return acc; + }, [] as ThemeObject[]); const themeContainer = selectedSets.reduce((acc: ThemeObject, curr: ExportTokenSet) => { acc.selectedTokenSets = { ...acc.selectedTokenSets, @@ -43,12 +55,19 @@ export default async function createLocalVariablesWithoutModesInPlugin(tokens: R }, {} as ThemeObject); const selectedSetIds = selectedSets.map((set) => set.set); - const collections = await createNecessaryVariableCollections([themeContainer], selectedSetIds); + const collections = await createNecessaryVariableCollections(themesToCreateCollections, selectedSetIds); + + const sourceSets = selectedSets.filter((t) => t.status === TokenSetStatus.SOURCE); + const sourceTokenSets = sourceSets.reduce((acc, curr) => { + acc[curr.set] = tokens[curr.set]; + return acc; + }, {}); await Promise.all(selectedSets.map(async (set: ExportTokenSet, index) => { if (set.status === TokenSetStatus.ENABLED) { const setTokens: Record = { - [set.set]: tokens[set.set], + ...sourceTokenSets, + [set.set]: tokens[set.set] }; const { collection, modeId } = findCollectionAndModeIdForTheme(set.set, set.set, collections); From 4d95bba1467415ba1ee39938ff304de9dd1f628a Mon Sep 17 00:00:00 2001 From: Celia Usero Navarro <114073780+cuserox@users.noreply.github.com> Date: Thu, 13 Jun 2024 09:07:25 +0100 Subject: [PATCH 18/22] fix: sync provider "Read more" links now open in browser (#2849) fix: opens all read more links for storage providers in new browser window --- .../src/app/components/StorageItemForm/ADOForm.tsx | 2 +- .../src/app/components/StorageItemForm/GenericVersioned.tsx | 2 +- .../src/app/components/StorageItemForm/GitForm.tsx | 2 +- .../src/app/components/StorageItemForm/JSONBinForm.tsx | 2 +- .../src/app/components/StorageItemForm/SupernovaForm.tsx | 2 +- .../src/app/components/StorageItemForm/URLForm.tsx | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/ADOForm.tsx b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/ADOForm.tsx index f60c755ad..6475bf34a 100644 --- a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/ADOForm.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/ADOForm.tsx @@ -60,7 +60,7 @@ export default function ADOForm({ {t('providers.ado.description')} {' '} - {t('readMore')} + {t('readMore')} diff --git a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/GenericVersioned.tsx b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/GenericVersioned.tsx index 61a9192a6..3f6bd7dc7 100644 --- a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/GenericVersioned.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/GenericVersioned.tsx @@ -125,7 +125,7 @@ export default function GenericVersionedForm({ {t('providers.generic.description')} {' '} - {t('readMore')} + {t('readMore')} diff --git a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/GitForm.tsx b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/GitForm.tsx index 37a3a71ab..c7af3b937 100644 --- a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/GitForm.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/GitForm.tsx @@ -65,7 +65,7 @@ export default function GitForm({ {t('gitExplained')} {' '} - {t('readMore')} + {t('readMore')} diff --git a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/JSONBinForm.tsx b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/JSONBinForm.tsx index 46b7aa5cf..1782bde39 100644 --- a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/JSONBinForm.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/JSONBinForm.tsx @@ -59,7 +59,7 @@ export default function JSONBinForm({ {t('providers.jsonbin.description')} {' '} - {t('readMore')} + {t('readMore')} diff --git a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/SupernovaForm.tsx b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/SupernovaForm.tsx index abce20603..47f010ab7 100644 --- a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/SupernovaForm.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/SupernovaForm.tsx @@ -67,7 +67,7 @@ export default function SupernovaForm({ {t('providers.supernova.description')} {' '} - {t('readMore', { ns: 'general' })} + {t('readMore', { ns: 'general' })} diff --git a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/URLForm.tsx b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/URLForm.tsx index 867dd96ac..9c865090b 100644 --- a/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/URLForm.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/StorageItemForm/URLForm.tsx @@ -59,7 +59,7 @@ export default function URLForm({ {t('providers.url.description')} {' '} - {t('readMore')} + {t('readMore')} From 1a7809efb97dd1220d8898e5b9a5b21dc01ea30e Mon Sep 17 00:00:00 2001 From: Luke Finch Date: Sat, 15 Jun 2024 11:12:54 +0200 Subject: [PATCH 19/22] Fix/ min&max width&height on instances (#2842) * fix logic for applying minmax width or height * remove console logs * unused import * refactor to apply token on instance children --------- Co-authored-by: hiroshi --- .../src/plugin/applySizingValuesOnNode.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/plugin/applySizingValuesOnNode.ts b/packages/tokens-studio-for-figma/src/plugin/applySizingValuesOnNode.ts index dcb10d249..711d28b7c 100644 --- a/packages/tokens-studio-for-figma/src/plugin/applySizingValuesOnNode.ts +++ b/packages/tokens-studio-for-figma/src/plugin/applySizingValuesOnNode.ts @@ -3,8 +3,8 @@ import { NodeTokenRefMap } from '@/types/NodeTokenRefMap'; import { isPrimitiveValue } from '@/utils/is'; import { tryApplyVariableId } from '@/utils/tryApplyVariableId'; import { transformValue } from './helpers'; -import { isPartOfInstance } from '@/utils/is/isPartOfInstance'; import { isAutoLayout } from '@/utils/isAutoLayout'; +import { isPartOfInstance } from '@/utils/is/isPartOfInstance'; export async function applySizingValuesOnNode( node: BaseNode, @@ -54,7 +54,6 @@ export async function applySizingValuesOnNode( if ( node.type !== 'DOCUMENT' && node.type !== 'PAGE' - && node.type !== 'INSTANCE' && !isPartOfInstance(node.id) && (isAutoLayout(node) || (node.parent && node.parent.type !== 'DOCUMENT' && node.parent.type !== 'PAGE' && isAutoLayout(node.parent))) From f38aa4d3113f569e2b1adf1bdde0f359b2bcf90e Mon Sep 17 00:00:00 2001 From: Hiroshi Oshiro <103296157+robinhoodie0823@users.noreply.github.com> Date: Sat, 15 Jun 2024 11:25:50 +0200 Subject: [PATCH 20/22] 2822 gitlab sync is not allowing users to push tokens (#2854) * feat: add token format option to get exact resolved value * revert the DTCG format * feat: add error handler message to remote token storage * fix typo error * remove un-necessary code --- .../src/app/store/providers/gitlab/gitlab.tsx | 8 +++++- .../src/app/store/remoteTokens.tsx | 17 +++++++----- .../src/constants/ErrorMessages.ts | 1 + .../src/storage/GitlabTokenStorage.ts | 27 ++++++++++++------- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/app/store/providers/gitlab/gitlab.tsx b/packages/tokens-studio-for-figma/src/app/store/providers/gitlab/gitlab.tsx index 1ac9935bd..25fd5b6ee 100644 --- a/packages/tokens-studio-for-figma/src/app/store/providers/gitlab/gitlab.tsx +++ b/packages/tokens-studio-for-figma/src/app/store/providers/gitlab/gitlab.tsx @@ -101,7 +101,7 @@ export function useGitLab() { themes, metadata: {}, }; - } catch (e) { + } catch (e: any) { closePushDialog(); console.log('Error pushing to GitLab', e); if (e instanceof Error && e.message === ErrorMessages.GIT_MULTIFILE_PERMISSION_ERROR) { @@ -110,6 +110,12 @@ export function useGitLab() { errorMessage: ErrorMessages.GIT_MULTIFILE_PERMISSION_ERROR, }; } + if (e instanceof Error && e.message === ErrorMessages.GITLAB_PUSH_TO_PROTECTED_BRANCH_ERROR) { + return { + status: 'failure', + errorMessage: ErrorMessages.GITLAB_PUSH_TO_PROTECTED_BRANCH_ERROR, + }; + } return { status: 'failure', errorMessage: ErrorMessages.GITLAB_CREDENTIAL_ERROR, diff --git a/packages/tokens-studio-for-figma/src/app/store/remoteTokens.tsx b/packages/tokens-studio-for-figma/src/app/store/remoteTokens.tsx index 82c1568cf..923bf6cb0 100644 --- a/packages/tokens-studio-for-figma/src/app/store/remoteTokens.tsx +++ b/packages/tokens-studio-for-figma/src/app/store/remoteTokens.tsx @@ -30,6 +30,7 @@ import { isEqual } from '@/utils/isEqual'; import usePullDialog from '../hooks/usePullDialog'; import { Tabs } from '@/constants/Tabs'; import { useTokensStudio } from './providers/tokens-studio'; +import { notifyToUI } from '@/plugin/notifiers'; export type PushOverrides = { branch: string, commitMessage: string }; @@ -325,34 +326,38 @@ export default function useRemoteTokens() { async ({ context = api, overrides }: { context?: StorageTypeCredentials, overrides?: PushOverrides } = {}) => { const isFolder = 'filePath' in context && !context.filePath?.endsWith('.json'); track('pushTokens', { provider: context.provider, isFolder }); + let pushResult; switch (context.provider) { case StorageProviderType.GITHUB: { - await pushTokensToGitHub(context, overrides); + pushResult = await pushTokensToGitHub(context, overrides); break; } case StorageProviderType.GITLAB: { - await pushTokensToGitLab(context); + pushResult = await pushTokensToGitLab(context); break; } case StorageProviderType.BITBUCKET: { - await pushTokensToBitbucket(context); + pushResult = await pushTokensToBitbucket(context); break; } case StorageProviderType.ADO: { - await pushTokensToADO(context); + pushResult = await pushTokensToADO(context); break; } case StorageProviderType.SUPERNOVA: { - await pushTokensToSupernova(context); + pushResult = await pushTokensToSupernova(context); break; } case StorageProviderType.TOKENS_STUDIO: { - await pushTokensToTokensStudio(context); + pushResult = await pushTokensToTokensStudio(context); break; } default: throw new Error('Not implemented'); } + if (pushResult.status && pushResult.status === 'failure') { + notifyToUI(pushResult.errorMessage, { error: true }); + } }, [ api, diff --git a/packages/tokens-studio-for-figma/src/constants/ErrorMessages.ts b/packages/tokens-studio-for-figma/src/constants/ErrorMessages.ts index 5c3a4df49..86b0406f3 100644 --- a/packages/tokens-studio-for-figma/src/constants/ErrorMessages.ts +++ b/packages/tokens-studio-for-figma/src/constants/ErrorMessages.ts @@ -18,4 +18,5 @@ export enum ErrorMessages { ID_NON_EXIST_ERROR = 'ID or Secret should be exist', JSONBIN_CREATE_ERROR = 'Error creating JSONbin token storage', GIT_MULTIFILE_PERMISSION_ERROR = 'You try to save a multi-file project as a free user. Upgrade to Pro or add a json file at the end of the filepath (tokens.json)', + GITLAB_PUSH_TO_PROTECTED_BRANCH_ERROR = '403 Forbidden - You are not allowed to push into this branch', } diff --git a/packages/tokens-studio-for-figma/src/storage/GitlabTokenStorage.ts b/packages/tokens-studio-for-figma/src/storage/GitlabTokenStorage.ts index 2fb9abd00..8d5492a55 100644 --- a/packages/tokens-studio-for-figma/src/storage/GitlabTokenStorage.ts +++ b/packages/tokens-studio-for-figma/src/storage/GitlabTokenStorage.ts @@ -229,16 +229,23 @@ export class GitlabTokenStorage extends GitTokenStorage { }))); } - const response = await this.gitlabClient.Commits.create( - this.projectId, - branch, - message, - gitlabActions, - shouldCreateBranch ? { - startBranch: branches[0], - } : undefined, - ); - return !!response; + try { + const response = await this.gitlabClient.Commits.create( + this.projectId, + branch, + message, + gitlabActions, + shouldCreateBranch ? { + startBranch: branches[0], + } : undefined, + ); + return !!response; + } catch (e: any) { + if (e.cause.description && String(e.cause.description).includes(ErrorMessages.GITLAB_PUSH_TO_PROTECTED_BRANCH_ERROR)) { + throw new Error(ErrorMessages.GITLAB_PUSH_TO_PROTECTED_BRANCH_ERROR); + } + throw new Error(e); + } } public async getLatestCommitDate(): Promise { From 56f622572d5cd29a50ada5b2b621ef3d05487994 Mon Sep 17 00:00:00 2001 From: Luke Finch Date: Sat, 15 Jun 2024 11:31:29 +0200 Subject: [PATCH 21/22] 2623 20 add new sync provider change to modal (#2833) * Add sonarqube integration (#2771) * move providers to modal * update test case to follow the updates * update branch * techdocs? * remove file --------- Co-authored-by: SorsOps <80043879+SorsOps@users.noreply.github.com> Co-authored-by: hiroshi Co-authored-by: Jan Six --- .../src/app/components/SyncSettings.test.tsx | 2 +- .../src/app/components/SyncSettings.tsx | 79 +++++++++++++------ .../src/i18n/lang/en/storage.json | 1 + .../src/i18n/lang/es/storage.json | 1 + .../src/i18n/lang/fr/storage.json | 1 + .../src/i18n/lang/hi/storage.json | 1 + .../src/i18n/lang/nl/storage.json | 1 + .../src/i18n/lang/zh/storage.json | 1 + 8 files changed, 60 insertions(+), 27 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/app/components/SyncSettings.test.tsx b/packages/tokens-studio-for-figma/src/app/components/SyncSettings.test.tsx index 48ce0d855..4c0e6aefb 100644 --- a/packages/tokens-studio-for-figma/src/app/components/SyncSettings.test.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/SyncSettings.test.tsx @@ -111,7 +111,7 @@ describe('ConfirmDialog', () => { ); await act(async () => { - const trigger = await result.getByTestId('add-storage-item-dropdown'); + const trigger = await result.getByTestId('add-storage-item-button'); trigger?.focus(); await userEvent.keyboard('[Enter]'); }); diff --git a/packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx b/packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx index 0dd22c284..6523a4151 100644 --- a/packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/SyncSettings.tsx @@ -3,7 +3,7 @@ import React, { useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import { - DropdownMenu, Heading, Button, Box, Stack, + Heading, Button, Box, Stack, Text, Dialog, } from '@tokens-studio/ui'; import { track } from '@/utils/analytics'; import StorageItem from './StorageItem'; @@ -16,6 +16,7 @@ import useRemoteTokens from '../store/remoteTokens'; import { StorageTypeCredentials } from '@/types/StorageType'; import LocalStorageItem from './LocalStorageItem'; import { getProviderIcon } from '@/utils/getProviderIcon'; +import { StyledBetaBadge } from './SecondScreen'; const SyncSettings = () => { const localApiState = useSelector(localApiStateSelector); @@ -44,8 +45,9 @@ const SyncSettings = () => { type: StorageProviderType.ADO, }, { - text: 'BitBucket (Beta)', + text: 'BitBucket', type: StorageProviderType.BITBUCKET, + beta: true, }, { text: 'Supernova', @@ -56,14 +58,17 @@ const SyncSettings = () => { type: StorageProviderType.GENERIC_VERSIONED_STORAGE, }, { - text: 'Tokens Studio (Beta)', + text: 'Tokens Studio', type: StorageProviderType.TOKENS_STUDIO, + beta: true, }, ], [t]); const apiProviders = useSelector(apiProvidersSelector); const dispatch = useDispatch(); + const [open, setOpen] = React.useState(false); + const { fetchBranches } = useRemoteTokens(); const [editStorageItemModalVisible, setShowEditStorageModalVisible] = React.useState(Boolean(localApiState.new)); @@ -97,6 +102,7 @@ const SyncSettings = () => { const handleProviderClick = React.useCallback( (provider: StorageProviderType) => () => { + setOpen(false); setStorageProvider(provider); handleShowAddCredentials(provider); }, @@ -131,7 +137,50 @@ const SyncSettings = () => { )} - {t('syncProviders')} + + {t('syncProviders')} + + + + + + + + {t('addNewSyncProvider')} + + + { + providers.map((provider) => ( + + + + {getProviderIcon(provider.type)} + {provider.text} + {provider.beta && BETA} + + + + + )) + } + + + + + {apiProviders.length > 0 && apiProviders.map((item) => ( @@ -142,28 +191,6 @@ const SyncSettings = () => { /> ))} - - - - - - - { - providers.map((provider) => ( - - {getProviderIcon(provider.type)} - {provider.text} - - )) - } - - - diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json b/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json index a9903e85f..979c16eed 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/en/storage.json @@ -3,6 +3,7 @@ "youCanAlwaysGoBack": "You can always go back to remote storage.", "addNewSyncProvider": "Add new sync provider", "editCredentials": "Edit sync provider credentials", + "choose": "Choose", "name": "Name", "pat": "Personal Access Token", "gitExplained": "Read your tokens that are stored on your repository, close to your code.", diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/es/storage.json b/packages/tokens-studio-for-figma/src/i18n/lang/es/storage.json index 13903b861..ad52f3283 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/es/storage.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/es/storage.json @@ -3,6 +3,7 @@ "youCanAlwaysGoBack": "Siempre puedes volver al almacenamiento remoto.", "addNewSyncProvider": "Agregar nuevo proveedor de sincronización", "editCredentials": "Editar credenciales del proveedor de sincronización", + "choose": "Elegir", "name": "Nombre", "pat": "Token de acceso personal", "gitExplained": "Lea sus tokens que están almacenados en su repositorio, cerca de su código.", diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/fr/storage.json b/packages/tokens-studio-for-figma/src/i18n/lang/fr/storage.json index 7601aae27..4f3cdd579 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/fr/storage.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/fr/storage.json @@ -3,6 +3,7 @@ "youCanAlwaysGoBack": "Vous pouvez toujours revenir au stockage distant.", "addNewSyncProvider": "Ajouter un nouveau fournisseur de synchronisation", "editCredentials": "Modifier les informations d'identification du fournisseur de synchronisation", + "choose": "Choisir", "name": "Nom", "pat": "Jeton d'accès personnel", "gitExplained": "Lisez vos jetons stockés sur votre référentiel, à proximité de votre code.", diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/hi/storage.json b/packages/tokens-studio-for-figma/src/i18n/lang/hi/storage.json index 3e94d4c51..d34996bc6 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/hi/storage.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/hi/storage.json @@ -3,6 +3,7 @@ "youCanAlwaysGoBack": "आप कभी भी रिमोट स्टोरेज पर वापस जा सकते हैं।", "addNewSyncProvider": "नया सिंक प्रदाता जोड़ें", "editCredentials": "सिंक प्रदाता क्रेडेंशियल संपादित करें", + "choose": "चुनें", "name": "नाम", "pat": "व्यक्तिगत पहुँच टोकन", "gitExplained": "अपने टोकन पढ़ें जो आपके रिपॉजिटरी पर, आपके कोड के पास संग्रहीत हैं।", diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/nl/storage.json b/packages/tokens-studio-for-figma/src/i18n/lang/nl/storage.json index cac75a6be..b71316ae0 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/nl/storage.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/nl/storage.json @@ -3,6 +3,7 @@ "youCanAlwaysGoBack": "U kunt altijd teruggaan naar externe opslag.", "addNewSyncProvider": "Nieuwe synchronisatieprovider toevoegen", "editCredentials": "Bewerk de inloggegevens van de synchronisatieprovider", + "choose": "Kiezen", "name": "Naam", "pat": "Persoonlijk toegangstoken", "gitExplained": "Lees uw tokens die in uw repository zijn opgeslagen, dichtbij uw code.", diff --git a/packages/tokens-studio-for-figma/src/i18n/lang/zh/storage.json b/packages/tokens-studio-for-figma/src/i18n/lang/zh/storage.json index 3aec38714..f3e36ddc2 100644 --- a/packages/tokens-studio-for-figma/src/i18n/lang/zh/storage.json +++ b/packages/tokens-studio-for-figma/src/i18n/lang/zh/storage.json @@ -3,6 +3,7 @@ "youCanAlwaysGoBack": "您可以随时返回远程存储。", "addNewSyncProvider": "添加新的同步提供商", "editCredentials": "编辑同步提供商凭据", + "choose": "选择", "name": "姓名", "pat": "个人访问令牌", "gitExplained": "读取存储在存储库中靠近代码的令牌。", From a66f76153330998cee98bb852be9a8c73f2ec25a Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Sat, 15 Jun 2024 15:04:52 +0530 Subject: [PATCH 22/22] =?UTF-8?q?Fix/=20when=20switching=20to=20input=20mo?= =?UTF-8?q?de=20from=20a=20referenced=20Border=20token,=20all=E2=80=A6=20(?= =?UTF-8?q?#2850)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix/ when switching to input mode from a referenced Border token, all fields are now editable * Create polite-impalas-hug.md --------- Co-authored-by: Jan Six --- .changeset/polite-impalas-hug.md | 5 + .../src/app/components/BorderTokenForm.tsx | 83 ++-- .../src/app/components/EditTokenForm.tsx | 360 ++++++++++-------- 3 files changed, 256 insertions(+), 192 deletions(-) create mode 100644 .changeset/polite-impalas-hug.md diff --git a/.changeset/polite-impalas-hug.md b/.changeset/polite-impalas-hug.md new file mode 100644 index 000000000..f2edd0f6d --- /dev/null +++ b/.changeset/polite-impalas-hug.md @@ -0,0 +1,5 @@ +--- +"@tokens-studio/figma-plugin": patch +--- + +When editing a border token and switching from reference to input mode we now populate the contents. diff --git a/packages/tokens-studio-for-figma/src/app/components/BorderTokenForm.tsx b/packages/tokens-studio-for-figma/src/app/components/BorderTokenForm.tsx index 66629fb32..8b24c92c9 100644 --- a/packages/tokens-studio-for-figma/src/app/components/BorderTokenForm.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/BorderTokenForm.tsx @@ -4,7 +4,7 @@ import get from 'just-safe-get'; import { TokensIcon, LinkBreak2Icon } from '@radix-ui/react-icons'; import { useTranslation } from 'react-i18next'; import { IconButton, Heading } from '@tokens-studio/ui'; -import { EditTokenObject } from '@/types/tokens'; +import { EditTokenObject, SingleBorderToken } from '@/types/tokens'; import { TokenTypes } from '@/constants/TokenTypes'; import { ResolveTokenValuesResult } from '@/utils/tokenHelpers'; import Stack from './Stack'; @@ -28,6 +28,7 @@ export default function BorderTokenForm({ handleBorderValueDownShiftInputChange, handleBorderAliasValueChange, handleDownShiftInputChange, + setBorderValue, onSubmit, }: { internalEditToken: Extract; @@ -36,61 +37,65 @@ export default function BorderTokenForm({ handleBorderValueDownShiftInputChange: (newInputValue: string, property: string) => void; handleBorderAliasValueChange: (property: string, value: string) => void; handleDownShiftInputChange: (newInputValue: string) => void; - onSubmit: () => void + setBorderValue: (newBorderValue: SingleBorderToken['value']) => void; + onSubmit: () => void; }) { const seed = useUIDSeed(); - const isAliasMode = (internalEditToken.value && typeof internalEditToken.value === 'string'); + const isAliasMode = internalEditToken.value && typeof internalEditToken.value === 'string'; const [mode, setMode] = useState(isAliasMode ? 'alias' : 'input'); const [alias, setAlias] = useState(''); const { t } = useTranslation(['tokens']); const [inputHelperOpen, setInputHelperOpen] = useState(false); - const selectedToken = React.useMemo(() => { + const selectedToken = React.useMemo(() => { const search = findReferences(String(internalEditToken.value)); if (search && search.length > 0) { const foundToken = resolvedTokens.find((t) => t.name === search[0]); - if (foundToken) return foundToken; + if (foundToken) return foundToken as SingleBorderToken; } return null; }, [internalEditToken, resolvedTokens]); const handleToggleInputHelper = React.useCallback(() => setInputHelperOpen(!inputHelperOpen), [inputHelperOpen]); - const onColorChange = React.useCallback((color: string) => { - handleBorderValueDownShiftInputChange(color, 'color'); - }, [handleBorderValueDownShiftInputChange]); + const onColorChange = React.useCallback( + (color: string) => { + handleBorderValueDownShiftInputChange(color, 'color'); + }, + [handleBorderValueDownShiftInputChange], + ); const handleMode = React.useCallback(() => { - const changeMode = (mode === 'input') ? 'alias' : 'input'; - setMode(changeMode); + if (mode === 'alias' && typeof internalEditToken.value === 'string') { + setBorderValue(selectedToken?.rawValue ?? {}); + } + setMode(mode === 'input' ? 'alias' : 'input'); setAlias(''); - }, [mode]); + }, [mode, selectedToken, internalEditToken, setBorderValue]); return ( {t('value')} - { - mode === 'input' ? ( - } - /> - ) : ( - } - /> - ) - } + {mode === 'input' ? ( + } + /> + ) : ( + } + /> + )} - {(mode === 'input' && internalEditToken.schema.schemas.value.type === 'object') ? ( + {mode === 'input' && internalEditToken.schema.schemas.value.type === 'object' ? ( {Object.entries(internalEditToken.schema.schemas.value.properties ?? {}).map(([key], keyIndex) => ( - <> + {inputHelperOpen && key === 'color' && ( - + )} - + ))} ) : ( @@ -124,12 +132,11 @@ export default function BorderTokenForm({ onSubmit={onSubmit} /> - {isAliasMode && typeof internalEditToken.value === 'string' && checkIfContainsAlias(internalEditToken.value) && ( - - )} + {isAliasMode && + typeof internalEditToken.value === 'string' && + checkIfContainsAlias(internalEditToken.value) && ( + + )} )} diff --git a/packages/tokens-studio-for-figma/src/app/components/EditTokenForm.tsx b/packages/tokens-studio-for-figma/src/app/components/EditTokenForm.tsx index deccf3477..ed7935c68 100644 --- a/packages/tokens-studio-for-figma/src/app/components/EditTokenForm.tsx +++ b/packages/tokens-studio-for-figma/src/app/components/EditTokenForm.tsx @@ -1,9 +1,7 @@ import * as React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; -import { - Button, Heading, Textarea, Label, Stack, -} from '@tokens-studio/ui'; +import { Button, Heading, Textarea, Label, Stack } from '@tokens-studio/ui'; import { track } from '@/utils/analytics'; import { useShortcut } from '@/hooks/useShortcut'; import { Dispatch } from '../store'; @@ -14,13 +12,16 @@ import Text from './Text'; import useConfirm from '../hooks/useConfirm'; import useTokens from '../store/useTokens'; import { - EditTokenObject, SingleBoxShadowToken, SingleDimensionToken, SingleToken, SingleTypographyToken, + EditTokenObject, + SingleBorderToken, + SingleBoxShadowToken, + SingleDimensionToken, + SingleToken, + SingleTypographyToken, } from '@/types/tokens'; import { checkIfAlias, checkIfContainsAlias, getAliasValue } from '@/utils/alias'; import { ResolveTokenValuesResult } from '@/utils/tokenHelpers'; -import { - activeTokenSetSelector, editTokenSelector, themesListSelector, tokensSelector, -} from '@/selectors'; +import { activeTokenSetSelector, editTokenSelector, themesListSelector, tokensSelector } from '@/selectors'; import { TokenTypes } from '@/constants/TokenTypes'; import TypographyInput from './TypographyInput'; import DownshiftInput from './DownshiftInput'; @@ -46,7 +47,7 @@ type Props = { resolvedTokens: ResolveTokenValuesResult[]; }; -type Choice = { key: string; label: string; enabled?: boolean, unique?: boolean }; +type Choice = { key: string; label: string; enabled?: boolean; unique?: boolean }; // @TODO this needs to be reviewed from a typings perspective + performance function EditTokenForm({ resolvedTokens }: Props) { @@ -56,17 +57,20 @@ function EditTokenForm({ resolvedTokens }: Props) { const editToken = useSelector(editTokenSelector); const themes = useSelector(themesListSelector); const [selectedTokenSets, setSelectedTokenSets] = React.useState([activeTokenSet]); - const { - editSingleToken, createSingleToken, duplicateSingleToken, renameTokensAcrossSets, - } = useManageTokens(); - const { - remapToken, renameStylesFromTokens, renameVariablesFromToken, updateVariablesFromToken, - } = useTokens(); + const { editSingleToken, createSingleToken, duplicateSingleToken, renameTokensAcrossSets } = useManageTokens(); + const { remapToken, renameStylesFromTokens, renameVariablesFromToken, updateVariablesFromToken } = useTokens(); const dispatch = useDispatch(); const [error, setError] = React.useState(null); const [internalEditToken, setInternalEditToken] = React.useState(editToken); const { confirm } = useConfirm(); - const isValidDimensionToken = React.useMemo(() => internalEditToken.type === TokenTypes.DIMENSION && (internalEditToken.value?.endsWith('px') || internalEditToken.value?.endsWith('rem') || checkIfAlias(internalEditToken as SingleDimensionToken, resolvedTokens)), [internalEditToken, resolvedTokens, checkIfAlias]); + const isValidDimensionToken = React.useMemo( + () => + internalEditToken.type === TokenTypes.DIMENSION && + (internalEditToken.value?.endsWith('px') || + internalEditToken.value?.endsWith('rem') || + checkIfAlias(internalEditToken as SingleDimensionToken, resolvedTokens)), + [internalEditToken, resolvedTokens, checkIfAlias], + ); const isValidColorToken = React.useMemo(() => { if (internalEditToken?.$extensions?.['studio.tokens']?.modify?.type === ColorModifierTypes.MIX) { return !!internalEditToken?.$extensions?.['studio.tokens']?.modify?.color; @@ -75,8 +79,11 @@ function EditTokenForm({ resolvedTokens }: Props) { }, [internalEditToken]); const isValid = React.useMemo(() => { - if (internalEditToken?.type === TokenTypes.COMPOSITION && internalEditToken.value - && (internalEditToken.value.hasOwnProperty('') || Object.keys(internalEditToken.value).length === 0)) { + if ( + internalEditToken?.type === TokenTypes.COMPOSITION && + internalEditToken.value && + (internalEditToken.value.hasOwnProperty('') || Object.keys(internalEditToken.value).length === 0) + ) { return false; } if (internalEditToken.type === TokenTypes.DIMENSION) { @@ -88,26 +95,24 @@ function EditTokenForm({ resolvedTokens }: Props) { return internalEditToken?.value && internalEditToken.name && !error; }, [internalEditToken, error, isValidColorToken, isValidDimensionToken]); - const hasNameThatExistsAlready = React.useMemo( - () => { - const editToken = resolvedTokens - .filter((t) => selectedTokenSets.includes(t.internal__Parent ?? '')) - .find((t) => t.name === internalEditToken?.name); + const hasNameThatExistsAlready = React.useMemo(() => { + const editToken = resolvedTokens + .filter((t) => selectedTokenSets.includes(t.internal__Parent ?? '')) + .find((t) => t.name === internalEditToken?.name); - if (editToken) { - editToken.description = internalEditToken.description; - } + if (editToken) { + editToken.description = internalEditToken.description; + } - return editToken; - }, - [internalEditToken, resolvedTokens, activeTokenSet, selectedTokenSets], - ); + return editToken; + }, [internalEditToken, resolvedTokens, activeTokenSet, selectedTokenSets]); const hasAnotherTokenThatStartsWithName = React.useMemo( - () => resolvedTokens - .filter((t) => t.internal__Parent === activeTokenSet) - .filter((t) => t.name !== internalEditToken?.initialName) - .find((t) => t.name.startsWith(`${internalEditToken?.name}.`)), + () => + resolvedTokens + .filter((t) => t.internal__Parent === activeTokenSet) + .filter((t) => t.name !== internalEditToken?.initialName) + .find((t) => t.name.startsWith(`${internalEditToken?.name}.`)), [internalEditToken, resolvedTokens, activeTokenSet], ); @@ -125,30 +130,34 @@ function EditTokenForm({ resolvedTokens }: Props) { return false; }, [internalEditToken]); - const hasPriorTokenName = React.useMemo( - () => { - const tokensWithSameParent = resolvedTokens.filter((t) => t.internal__Parent === activeTokenSet); - if (internalEditToken?.status === EditTokenFormStatus.CREATE) { - // If we are creating a new token, disallow naming it as a prefix of an existing token - return tokensWithSameParent.find((t) => internalEditToken.name?.startsWith(`${t.name}.`)); - } if (internalEditToken?.status === EditTokenFormStatus.EDIT) { - // If we are editing a token, only disallow the name if it's prefix matches another token and it is not the token we are currently editing - return tokensWithSameParent.find((t) => internalEditToken.name?.startsWith(`${t.name}.`) && internalEditToken.initialName !== t.name); - } - return false; - }, - [internalEditToken, resolvedTokens, activeTokenSet], - ); + const hasPriorTokenName = React.useMemo(() => { + const tokensWithSameParent = resolvedTokens.filter((t) => t.internal__Parent === activeTokenSet); + if (internalEditToken?.status === EditTokenFormStatus.CREATE) { + // If we are creating a new token, disallow naming it as a prefix of an existing token + return tokensWithSameParent.find((t) => internalEditToken.name?.startsWith(`${t.name}.`)); + } + if (internalEditToken?.status === EditTokenFormStatus.EDIT) { + // If we are editing a token, only disallow the name if it's prefix matches another token and it is not the token we are currently editing + return tokensWithSameParent.find( + (t) => internalEditToken.name?.startsWith(`${t.name}.`) && internalEditToken.initialName !== t.name, + ); + } + return false; + }, [internalEditToken, resolvedTokens, activeTokenSet]); - const nameWasChanged = React.useMemo(() => internalEditToken?.initialName !== internalEditToken?.name, [ - internalEditToken, - ]); + const nameWasChanged = React.useMemo( + () => internalEditToken?.initialName !== internalEditToken?.name, + [internalEditToken], + ); React.useEffect(() => { if ((internalEditToken?.status !== EditTokenFormStatus.EDIT || nameWasChanged) && hasNameThatExistsAlready) { setError(t('tokenNamesMustBeUnique', { ns: 'errors' })); } - if ((internalEditToken?.status !== EditTokenFormStatus.EDIT || nameWasChanged) && hasAnotherTokenThatStartsWithName) { + if ( + (internalEditToken?.status !== EditTokenFormStatus.EDIT || nameWasChanged) && + hasAnotherTokenThatStartsWithName + ) { setError(t('mustNotUseNameOfAnotherGroup', { ns: 'errors' })); } if ((internalEditToken?.status || nameWasChanged) && hasPriorTokenName) { @@ -182,14 +191,11 @@ function EditTokenForm({ resolvedTokens }: Props) { [internalEditToken], ); - const handleBlur = React.useCallback( - () => { - if (internalEditToken.type === TokenTypes.DIMENSION && !isValidDimensionToken) { - setError(t('valueMustIncludePxOrRem', { ns: 'errors' })); - } - }, - [internalEditToken, isValidDimensionToken], - ); + const handleBlur = React.useCallback(() => { + if (internalEditToken.type === TokenTypes.DIMENSION && !isValidDimensionToken) { + setError(t('valueMustIncludePxOrRem', { ns: 'errors' })); + } + }, [internalEditToken, isValidDimensionToken]); const handleBoxShadowValueChange = React.useCallback( (shadow: SingleBoxShadowToken['value']) => { @@ -232,23 +238,29 @@ function EditTokenForm({ resolvedTokens }: Props) { [internalEditToken], ); - const handleTypographyValueDownShiftInputChange = React.useCallback((newInputValue: string, property: string) => { - if (internalEditToken?.type === TokenTypes.TYPOGRAPHY && typeof internalEditToken?.value !== 'string') { - setInternalEditToken({ - ...internalEditToken, - value: { ...internalEditToken.value, [property]: newInputValue }, - }); - } - }, [internalEditToken]); + const handleTypographyValueDownShiftInputChange = React.useCallback( + (newInputValue: string, property: string) => { + if (internalEditToken?.type === TokenTypes.TYPOGRAPHY && typeof internalEditToken?.value !== 'string') { + setInternalEditToken({ + ...internalEditToken, + value: { ...internalEditToken.value, [property]: newInputValue }, + }); + } + }, + [internalEditToken], + ); - const setTypographyValue = React.useCallback((newTypographyValue: SingleTypographyToken['value']) => { - if (internalEditToken?.type === TokenTypes.TYPOGRAPHY && typeof newTypographyValue === 'object') { - setInternalEditToken({ - ...internalEditToken, - value: { ...newTypographyValue }, - }); - } - }, [internalEditToken]); + const setTypographyValue = React.useCallback( + (newTypographyValue: SingleTypographyToken['value']) => { + if (internalEditToken?.type === TokenTypes.TYPOGRAPHY && typeof newTypographyValue === 'object') { + setInternalEditToken({ + ...internalEditToken, + value: { ...newTypographyValue }, + }); + } + }, + [internalEditToken], + ); const handleBorderValueChange = React.useCallback( (property: string, value: string) => { @@ -265,14 +277,29 @@ function EditTokenForm({ resolvedTokens }: Props) { [internalEditToken], ); - const handleBorderValueDownShiftInputChange = React.useCallback((newInputValue: string, property: string) => { - if (internalEditToken?.type === TokenTypes.BORDER && typeof internalEditToken?.value !== 'string') { - setInternalEditToken({ - ...internalEditToken, - value: { ...internalEditToken.value, [property]: newInputValue }, - }); - } - }, [internalEditToken]); + const handleBorderValueDownShiftInputChange = React.useCallback( + (newInputValue: string, property: string) => { + if (internalEditToken?.type === TokenTypes.BORDER && typeof internalEditToken?.value !== 'string') { + setInternalEditToken({ + ...internalEditToken, + value: { ...internalEditToken.value, [property]: newInputValue }, + }); + } + }, + [internalEditToken], + ); + + const setBorderValue = React.useCallback( + (newBorderValue: SingleBorderToken['value']) => { + if (internalEditToken?.type === TokenTypes.BORDER && typeof newBorderValue === 'object') { + setInternalEditToken({ + ...internalEditToken, + value: { ...newBorderValue }, + }); + } + }, + [internalEditToken], + ); const removeColorModify = React.useCallback(() => { const newValue = { ...internalEditToken.$extensions?.['studio.tokens'] }; @@ -286,25 +313,31 @@ function EditTokenForm({ resolvedTokens }: Props) { }); }, [internalEditToken]); - const handleColorModifyChange = React.useCallback((newModify: ColorModifier) => { - setInternalEditToken({ - ...internalEditToken, - $extensions: { - ...internalEditToken.$extensions, - 'studio.tokens': { - ...internalEditToken.$extensions?.['studio.tokens'], - modify: newModify, - }, - } as SingleToken['$extensions'], - }); - }, [internalEditToken]); + const handleColorModifyChange = React.useCallback( + (newModify: ColorModifier) => { + setInternalEditToken({ + ...internalEditToken, + $extensions: { + ...internalEditToken.$extensions, + 'studio.tokens': { + ...internalEditToken.$extensions?.['studio.tokens'], + modify: newModify, + }, + } as SingleToken['$extensions'], + }); + }, + [internalEditToken], + ); - const handleDownShiftInputChange = React.useCallback((newInputValue: string) => { - setInternalEditToken({ - ...internalEditToken, - value: newInputValue, - } as typeof editToken); - }, [internalEditToken]); + const handleDownShiftInputChange = React.useCallback( + (newInputValue: string) => { + setInternalEditToken({ + ...internalEditToken, + value: newInputValue, + } as typeof editToken); + }, + [internalEditToken], + ); const handleDescriptionChange = React.useCallback( (value: string) => { @@ -328,9 +361,7 @@ function EditTokenForm({ resolvedTokens }: Props) { }, [internalEditToken, resolvedTokens]); // @TODO update to useCallback - const submitTokenValue = async ({ - type, value, name, $extensions, - }: EditTokenObject) => { + const submitTokenValue = async ({ type, value, name, $extensions }: EditTokenObject) => { if (internalEditToken && value && name) { let oldName: string | undefined; if (internalEditToken.initialName !== name && internalEditToken.initialName) { @@ -344,10 +375,7 @@ function EditTokenForm({ resolvedTokens }: Props) { if (internalEditToken.status === EditTokenFormStatus.CREATE) { track('Create token', { type: internalEditToken.type, isModifier: !!$extensions?.['studio.tokens']?.modify }); createSingleToken({ - description: ( - internalEditToken.description - ?? internalEditToken.oldDescription - ), + description: internalEditToken.description ?? internalEditToken.oldDescription, parent: activeTokenSet, name: newName, type, @@ -356,10 +384,7 @@ function EditTokenForm({ resolvedTokens }: Props) { }); } else if (internalEditToken.status === EditTokenFormStatus.EDIT) { editSingleToken({ - description: ( - internalEditToken.description - ?? internalEditToken.oldDescription - ), + description: internalEditToken.description ?? internalEditToken.oldDescription, parent: activeTokenSet, name: newName, oldName, @@ -382,23 +407,38 @@ function EditTokenForm({ resolvedTokens }: Props) { track('Edit token', { renamed: true, type: internalEditToken.type }); const choices: Choice[] = [ { - key: UpdateMode.SELECTION, label: 'Selection', unique: true, enabled: UpdateMode.SELECTION === lastUsedRenameOption, + key: UpdateMode.SELECTION, + label: 'Selection', + unique: true, + enabled: UpdateMode.SELECTION === lastUsedRenameOption, }, { - key: UpdateMode.PAGE, label: 'Page', unique: true, enabled: UpdateMode.PAGE === lastUsedRenameOption, + key: UpdateMode.PAGE, + label: 'Page', + unique: true, + enabled: UpdateMode.PAGE === lastUsedRenameOption, }, { - key: UpdateMode.DOCUMENT, label: 'Document', unique: true, enabled: UpdateMode.DOCUMENT === lastUsedRenameOption, + key: UpdateMode.DOCUMENT, + label: 'Document', + unique: true, + enabled: UpdateMode.DOCUMENT === lastUsedRenameOption, }, ]; - if (themes.length > 0 && [TokenTypes.COLOR, TokenTypes.TYPOGRAPHY, TokenTypes.BOX_SHADOW].includes(internalEditToken.type)) { + if ( + themes.length > 0 && + [TokenTypes.COLOR, TokenTypes.TYPOGRAPHY, TokenTypes.BOX_SHADOW].includes(internalEditToken.type) + ) { choices.push({ - key: StyleOptions.RENAME, label: 'Rename styles', enabled: lastUsedRenameStyles, + key: StyleOptions.RENAME, + label: 'Rename styles', + enabled: lastUsedRenameStyles, }); } if (themes.length > 0 && tokenTypesToCreateVariable.includes(internalEditToken.type)) { choices.push({ - key: ModalOptions.RENAME_VARIABLE, label: 'Rename variable', + key: ModalOptions.RENAME_VARIABLE, + label: 'Rename variable', }); } const tokenSetsContainsSameToken: string[] = []; @@ -409,7 +449,8 @@ function EditTokenForm({ resolvedTokens }: Props) { }); if (tokenSetsContainsSameToken.length > 1) { choices.push({ - key: ModalOptions.RENAME_ACROSS_SETS, label: 'Rename in other sets', + key: ModalOptions.RENAME_ACROSS_SETS, + label: 'Rename in other sets', }); } const confirmData = await confirm({ @@ -418,7 +459,11 @@ function EditTokenForm({ resolvedTokens }: Props) { choices, }); if (confirmData && confirmData.result) { - if (confirmData.data.some((data: string) => [UpdateMode.DOCUMENT, UpdateMode.PAGE, UpdateMode.SELECTION].includes(data as UpdateMode))) { + if ( + confirmData.data.some((data: string) => + [UpdateMode.DOCUMENT, UpdateMode.PAGE, UpdateMode.SELECTION].includes(data as UpdateMode), + ) + ) { remapToken(oldName, newName, confirmData.data[0]); lastUsedRenameOption = confirmData.data[0] as UpdateMode; } @@ -439,10 +484,7 @@ function EditTokenForm({ resolvedTokens }: Props) { } else if (internalEditToken.status === EditTokenFormStatus.DUPLICATE) { oldName = internalEditToken.initialName?.slice(0, internalEditToken.initialName?.lastIndexOf('-copy')); duplicateSingleToken({ - description: ( - internalEditToken.description - ?? internalEditToken.oldDescription - ), + description: internalEditToken.description ?? internalEditToken.oldDescription, parent: activeTokenSet, newName, oldName, @@ -466,11 +508,14 @@ function EditTokenForm({ resolvedTokens }: Props) { } }, [dispatch, isValid, internalEditToken, submitTokenValue, isValidDimensionToken]); - const handleSaveShortcut = React.useCallback((e: KeyboardEvent) => { - if (e.metaKey || e.ctrlKey) { - checkAndSubmitTokenValue(); - } - }, [checkAndSubmitTokenValue]); + const handleSaveShortcut = React.useCallback( + (e: KeyboardEvent) => { + if (e.metaKey || e.ctrlKey) { + checkAndSubmitTokenValue(); + } + }, + [checkAndSubmitTokenValue], + ); useShortcut(['Enter'], handleSaveShortcut); @@ -532,6 +577,7 @@ function EditTokenForm({ resolvedTokens }: Props) { handleBorderValueDownShiftInputChange={handleBorderValueDownShiftInputChange} handleBorderAliasValueChange={handleChange} handleDownShiftInputChange={handleDownShiftInputChange} + setBorderValue={setBorderValue} onSubmit={checkAndSubmitTokenValue} /> ); @@ -568,18 +614,19 @@ function EditTokenForm({ resolvedTokens }: Props) { /> {checkIfContainsAlias(internalEditToken.value) && ( - {resolvedValue?.toString()} @@ -607,7 +654,11 @@ function EditTokenForm({ resolvedTokens }: Props) { /> {renderTokenForm()} - {internalEditToken?.schema?.explainer && {internalEditToken.schema.explainer}} + {internalEditToken?.schema?.explainer && ( + + {internalEditToken.schema.explainer} + + )} {t('description')}