diff --git a/packages/forma-36-codemod/README.md b/packages/forma-36-codemod/README.md index 9ea906495a..70d3d62399 100644 --- a/packages/forma-36-codemod/README.md +++ b/packages/forma-36-codemod/README.md @@ -31,6 +31,6 @@ f36-codemod [...options] ### Included Transforms -#### `v5/icons` +#### `v7/replace-shape-radius-tokens.ts` -Updates dependency and usage of icons to the v5-alpha icons +replaces base tokens with new numeric ramp naming diff --git a/packages/forma-36-codemod/__testfixtures__/replace-shape-radius-tokens.input.js b/packages/forma-36-codemod/__testfixtures__/replace-shape-radius-tokens.input.js new file mode 100644 index 0000000000..c344183fbf --- /dev/null +++ b/packages/forma-36-codemod/__testfixtures__/replace-shape-radius-tokens.input.js @@ -0,0 +1,4 @@ +import { borderRadiusSmall, borderRadiusMedium, borderRadiusLarge } from '@contentful/f36-tokens'; + +const small = borderRadiusSmall; +const medium = borderRadiusMedium; diff --git a/packages/forma-36-codemod/__testfixtures__/replace-shape-radius-tokens.output.js b/packages/forma-36-codemod/__testfixtures__/replace-shape-radius-tokens.output.js new file mode 100644 index 0000000000..2c5af6ef83 --- /dev/null +++ b/packages/forma-36-codemod/__testfixtures__/replace-shape-radius-tokens.output.js @@ -0,0 +1,4 @@ +import { radius100, radius200} from '@contentful/f36-tokens'; + +const small = radius100; +const medium = radius200; diff --git a/packages/forma-36-codemod/jest/replace-radius-tokens.test.ts b/packages/forma-36-codemod/jest/replace-radius-tokens.test.ts new file mode 100644 index 0000000000..14b9b0fb2a --- /dev/null +++ b/packages/forma-36-codemod/jest/replace-radius-tokens.test.ts @@ -0,0 +1,12 @@ +const defineTest = require('jscodeshift/dist/testUtils').defineTest; + +/** + * Runs the test fixture for the replace-shape-radius-tokens transform. + * This assumes a flat test fixture structure under __testfixtures__/. + */ +defineTest( + __dirname, + 'replace-shape-radius-tokens', + null, + 'replace-shape-radius-tokens', +); diff --git a/packages/forma-36-codemod/transforms/__testfixtures__/v5/icons.input.js b/packages/forma-36-codemod/transforms/__testfixtures__/v5/icons.input.js deleted file mode 100644 index 7ba05eca5a..0000000000 --- a/packages/forma-36-codemod/transforms/__testfixtures__/v5/icons.input.js +++ /dev/null @@ -1,31 +0,0 @@ -import { - CloseIcon, - ChevronUpIcon, - ChevronDownTrimmedIcon, - DeleteIcon, - InfoCircleIcon, - PreviewOffIcon, - IconProps, -} from '@contentful/f36-icons'; -import { Icon } from '@contentful/f36-components'; -import { IconButton } from '@contentful/f36-button'; - -; -; -; -; -; - -const largeIcon = true; -; -; - -; -; - -const size = 'medium'; -; - -; - - diff --git a/packages/forma-36-codemod/transforms/__testfixtures__/v5/icons.output.js b/packages/forma-36-codemod/transforms/__testfixtures__/v5/icons.output.js deleted file mode 100644 index eeccabd6e3..0000000000 --- a/packages/forma-36-codemod/transforms/__testfixtures__/v5/icons.output.js +++ /dev/null @@ -1,34 +0,0 @@ -import { - CaretDownIcon, - CaretUpIcon, - XIcon, - TrashSimpleIcon, - InfoIcon, - EyeClosedIcon, - IconProps, -} from '@contentful/f36-icons'; -import { IconButton } from '@contentful/f36-button'; - -import { Icon } from "@contentful/f36-icon"; - -import tokens from "@contentful/f36-tokens"; - -; -; -; -; -; - -const largeIcon = true; -; -; - -; -; - -const size = 'medium'; -; - -; - - diff --git a/packages/forma-36-codemod/transforms/v5/icons.js b/packages/forma-36-codemod/transforms/v5/icons.js deleted file mode 100644 index 6bf73e22b6..0000000000 --- a/packages/forma-36-codemod/transforms/v5/icons.js +++ /dev/null @@ -1,425 +0,0 @@ -const { - getComponentLocalName, - changeComponentName, - changeImport, - changeProperties, - deleteProperty, - updateTernaryValues, - updatePropertyValue, - getProperty, - hasProperty, - changeIdentifier, - pipe, - addImport, - renameProperties, -} = require('../../utils'); -const { shouldSkipUpdateImport, getImport } = require('../../utils/config'); -const { isConditionalExpression } = require('../../utils/updateTernaryValues'); - -// V4 icon name : V5 icon name -const iconsMap = { - App: 'Puzzle', - Apis: 'CloudArrowUpDown', - Appearance: 'CircleHalf', - Archive: 'FileArchive', - ArrowBackward: 'ArrowLeft', - ArrowDown: 'CaretDown', - ArrowDownward: 'ArrowDown', - ArrowForward: 'ArrowRight', - ArrowUp: 'CaretUp', - ArrowUpward: 'ArrowUp', - Asset: 'ImageSquare', - Audio: 'FileAudio', - Billing: 'Receipt', - Calendar: 'CalendarBlank', - ChatBubble: 'Chat', - CheckCircle: 'CheckCircle', - ChevronDown: 'CaretDown', - ChevronLeft: 'CaretLeft', - ChevronRight: 'CaretRight', - ChevronUp: 'CaretUp', - Clock: 'Clock', - Close: 'X', - CloudUpload: 'CloudArrowUp', - Code: 'CodeSimple', - CodeIllustration: 'BracketsCurly', - Content: 'PenNib', - ContentModel: 'Wrench', - Copy: 'CopySimple', - Cycle: 'Repeat', - Delete: 'TrashSimple', - Diamond: 'SketchLogo', - Done: 'Check', - DoubleArrow: 'CaretUpDown', - Download: 'DownloadSimple', - Drag: 'DotsSixVertical', - Edit: 'PencilSimple', - EmbeddedEntryBlock: 'EmbeddedBlock', - EmbeddedEntryInline: 'EmbeddedLine', - Entry: 'Entry', - Environment: 'Environment', - EnvironmentAlias: 'EnvironmentAlias', - ErrorCircle: 'WarningOctagon', - ErrorCircleOutline: 'WarningOctagon', - ExternalLink: 'ArrowSquareOut', - FaceHappy: 'Smiley', - Filter: 'FunnelSimple', - Folder: 'FolderSimple', - FolderCreate: 'FolderSimplePlus', - FolderOpen: 'FolderOpen', - FormatBold: 'TextB', - FormatItalic: 'TextItalic', - FormatUnderlined: 'TextUnderline', - Gift: 'Gift', - Heading: 'TextH', - HeadingOne: 'TextHOne', - HeadingTwo: 'TextHTwo', - HelpCircle: 'Info', - HelpCircleInverted: 'Info', - Home: 'House', - HorizontalRule: 'Minus', - Image: 'ImageSquare', - InfoCircle: 'Info', - Language: 'Globe', - Link: 'LinkSimple', - LinkAlternate: 'LinkSimple', - ListBulleted: 'ListBullets', - ListNumbered: 'ListNumbers', - Lock: 'LockSimple', - LooksOne: 'NumberOne', - LooksTwo: 'NumberTwo', - Logout: 'SignOut', - Markup: 'FileCode', - Media: 'FileImage', - Menu: 'List', - Minus: 'Minus', - MoreHorizontal: 'DotsThree', - MoreVertical: 'DotsThreeVertical', - Oauth: 'Fingerprint', - Orginfo: 'Bulding', - Organizations: 'Building', - Page: 'File', - Pdf: 'FilePdf', - PaintBrush: 'PaintBrush', - Person: 'User', - Plaintext: 'FileText', - Plus: 'Plus', - PlusCircle: 'Plus', - Presentation: 'Presentation', - Preview: 'Eye', - PreviewOff: 'EyeClosed', - Purchase: 'ShoppingCartSimple', - Puzzle: 'PuzzlePiece', - Quote: 'Quotes', - Receipt: 'Receipt', - References: 'Swap', - Release: 'Release', - Richtext: 'RichText', - Search: 'MagnifyingGlass', - Settings: 'GearSix', - ShoppingCart: 'ShoppingCartSimple', - Sort: 'CaretUpDown', - SortAscending: 'SortAscending', - SortDescending: 'SortDescending', - Spaces: 'Cube', - Spreadsheet: 'Table', - Sso: 'Keyhole', - Star: 'Star', - Subscript: 'TextSubscript', - Subscription: 'ShoppingCartSimple', - Superscript: 'TextSuperscript', - Tab: 'Tabs', - Table: 'Table', - Tags: 'Tag', - Teams: 'UsersThree', - Text: 'TextT', - ThumbDown: 'ThumbsDown', - ThumbUp: 'ThumbsUp', - Toggle: 'ArrowsLeftRight', - Token: 'Key', - TriangleOutline: 'Triangle', - Users: 'Users', - UserProfile: 'User', - Usage: 'Gauge', - Video: 'FileVideo', - Warning: 'Warning', - Workflows: 'Workflows', -}; - -const variantToColorTokenMap = (j, tokensImportName) => ({ - primary: j.identifier(`${tokensImportName}.colorPrimary`), - positive: j.identifier(`${tokensImportName}.colorPositive`), - negative: j.identifier(`${tokensImportName}.colorNegative`), - warning: j.identifier(`${tokensImportName}.colorWarning`), - secondary: j.identifier(`${tokensImportName}.gray900`), - muted: j.identifier(`${tokensImportName}.gray600`), - white: j.identifier(`${tokensImportName}.colorWhite`), - premium: j.identifier(`${tokensImportName}.purple500`), -}); - -const replaceTrimmedIcons = function (file, api) { - const j = api.jscodeshift; - - let source = file.source; - - const importName = getImport('f36-icons'); - - const components = Object.keys(iconsMap) - .map((v4IconName) => { - return { - localName: getComponentLocalName(j, source, { - componentName: `${v4IconName}TrimmedIcon`, - importName, - }), - v4IconName, - }; - }) - .filter(({ localName }) => !!localName); - - components.forEach(({ localName, v4IconName }) => { - const newComponentName = `${v4IconName}Icon`; - source = changeComponentName(j, source, { - componentName: localName, - outputComponentName: newComponentName, - }); - - if (!shouldSkipUpdateImport()) { - source = changeImport(j, source, { - componentName: localName, - from: importName, - to: '@contentful/f36-icons', - outputComponentName: newComponentName, - }); - } - - source = changeIdentifier(j, source, { - from: localName, - to: newComponentName, - }); - }); - - return source; -}; - -const updateToV5Icons = function (file, api) { - const j = api.jscodeshift; - - let source = file.source; - - const iconsImportName = getImport('f36-icons'); - const iconImportName = getImport('f36-icon'); - const componentsImportName = getImport('f36-components'); - const tokensImportName = getImport('f36-tokens'); - - const iconComponent = [iconImportName, componentsImportName] - .map((importName) => ({ - importName, - localName: getComponentLocalName(j, source, { - componentName: 'Icon', - importName, - }), - })) - .find(({ localName }) => Boolean(localName)); - - const components = Object.keys(iconsMap) - .map((v4IconName) => { - return { - localName: getComponentLocalName(j, source, { - componentName: `${v4IconName}Icon`, - importName: iconsImportName, - }), - v4IconName, - }; - }) - .concat([ - { - localName: iconComponent?.localName, - v4IconName: 'Icon', - }, - ]) - .filter(({ localName }) => !!localName); - - let addTokensImport = false; - - components.forEach(({ localName, v4IconName }) => { - const isIconComponent = v4IconName === 'Icon'; - const newComponentName = isIconComponent - ? 'Icon' - : `${iconsMap[v4IconName]}Icon`; - const importName = isIconComponent - ? iconComponent.importName - : iconsImportName; - - source = changeProperties(j, source, { - componentName: localName, - fn(attributes) { - let modifiedAttributes = attributes; - - // Replace variant with color prop - if (hasProperty(modifiedAttributes, { propertyName: 'variant' })) { - const variant = getProperty(modifiedAttributes, { - propertyName: 'variant', - }); - - const { result } = addImport(j, source, [ - j.template.statement([`import tokens from "${tokensImportName}"`]), - ]); - addTokensImport = true; - - if (isConditionalExpression(variant.value, j)) { - modifiedAttributes = updatePropertyValue(modifiedAttributes, { - j, - propertyName: 'variant', - propertyValue: (value) => { - const valueMap = variantToColorTokenMap(j, result.tokens); - - const updatedValue = updateTernaryValues(value, { - j, - valueMap, - }); - - return updatedValue; - }, - }); - } else { - modifiedAttributes = updatePropertyValue(modifiedAttributes, { - j, - propertyName: 'variant', - propertyValue: (value) => { - if (value.type === 'JSXExpressionContainer') { - const expressionValue = value.expression.value; - if ( - expressionValue in variantToColorTokenMap(j, result.tokens) - ) { - return j.jsxExpressionContainer( - variantToColorTokenMap(j, result.tokens)[expressionValue], - ); - } - return value; - } else { - if (value.value in variantToColorTokenMap(j, result.tokens)) { - return j.jsxExpressionContainer( - variantToColorTokenMap(j, result.tokens)[value.value], - ); - } - } - }, - }); - } - - modifiedAttributes = renameProperties(modifiedAttributes, { - renameMap: { - variant: 'color', - }, - }); - } - - // Update size prop - if (hasProperty(modifiedAttributes, { propertyName: 'size' })) { - let size = getProperty(modifiedAttributes, { - propertyName: 'size', - }); - - // update conditional expressions - if (isConditionalExpression(size.value, j)) { - modifiedAttributes = updatePropertyValue(modifiedAttributes, { - j, - propertyName: 'size', - propertyValue: (value) => { - const valueMap = { - large: 'medium', - xlarge: 'medium', - }; - - const updatedValue = updateTernaryValues(value, { - j, - valueMap, - }); - - return updatedValue; - }, - }); - } - - // update JSXExpressionContainer - if (size.value.type === 'JSXExpressionContainer') { - modifiedAttributes = updatePropertyValue(modifiedAttributes, { - j, - propertyName: 'size', - propertyValue: () => { - if (size.value?.expression?.type === 'Identifier') { - return size.value; - } - return j.literal(size.value.expression.value); - }, - }); - } - - size = getProperty(modifiedAttributes, { - propertyName: 'size', - }); - - // Remove size prop if value is 'large', 'xlarge' or 'medium' - if (['large', 'xlarge', 'medium'].includes(size.value.value)) { - modifiedAttributes = deleteProperty(modifiedAttributes, { - propertyName: 'size', - file, - }); - } - } - - return modifiedAttributes; - }, - }); - - source = changeComponentName(j, source, { - componentName: localName, - outputComponentName: newComponentName, - }); - - if (!shouldSkipUpdateImport()) { - source = changeImport(j, source, { - componentName: localName, - from: importName, - to: - v4IconName === 'Icon' - ? '@contentful/f36-icon' - : '@contentful/f36-icons', - outputComponentName: newComponentName, - }); - } - - source = changeIdentifier(j, source, { - from: localName, - to: newComponentName, - }); - }); - - const iconProps = [iconsImportName, componentsImportName] - .map((importName) => ({ - importName, - localName: getComponentLocalName(j, source, { - componentName: 'IconProps', - importName, - }), - })) - .find(({ localName }) => Boolean(localName)); - - if (addTokensImport) { - source = addImport(j, source, [ - j.template.statement([`import tokens from "${tokensImportName}"`]), - ]).source; - } - if (iconProps?.localName) { - source = changeImport(j, source, { - componentName: iconProps.localName, - from: iconProps.importName, - to: '@contentful/f36-icons', - outputComponentName: iconProps.localName, - }); - } - - return source; -}; - -module.exports = pipe([replaceTrimmedIcons, updateToV5Icons]); diff --git a/packages/forma-36-codemod/transforms/v7/replace-shape-radius-tokens.ts b/packages/forma-36-codemod/transforms/v7/replace-shape-radius-tokens.ts new file mode 100644 index 0000000000..349119bdcc --- /dev/null +++ b/packages/forma-36-codemod/transforms/v7/replace-shape-radius-tokens.ts @@ -0,0 +1,36 @@ +/** + * Codemod: replace-shape-radius-tokens + * + * Replaces deprecated Forma 36 border radius token names + * (tokens.borderRadiusSmall/Medium/Large) with the new numeric ramp + * (tokens.radius100/200/300). + */ + +module.exports = function replaceShapeRadiusTokens(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + + const radiusMap = { + borderRadiusSmall: 'radius100', + borderRadiusMedium: 'radius200', + }; + + // Find all tokens. and replace with tokens. + root + .find(j.MemberExpression, { + object: { name: 'tokens' }, + }) + .forEach(path => { + const prop = path.node.property; + + if (prop.type === 'Identifier' && radiusMap[prop.name]) { + prop.name = radiusMap[prop.name]; + } + + if (prop.type === 'Literal' && radiusMap[prop.value]) { + prop.value = radiusMap[prop.value]; + } + }); + + return root.toSource(); +}; diff --git a/packages/forma-36-tokens/README.md b/packages/forma-36-tokens/README.md index be23f058ad..bb34b95716 100644 --- a/packages/forma-36-tokens/README.md +++ b/packages/forma-36-tokens/README.md @@ -46,8 +46,9 @@ All tokens are kept in the `src/tokens` directory and organised as so: ``` . -├── border-radius -│   ├── border-radius.js +├── shape-radius +│   ├── radius-semantic.js +│   ├── scale-radius.js ├── box-shadows │   ├── box-shadows.js │   └── glows.js diff --git a/packages/forma-36-tokens/src/tokens/border-radius/border-radius.js b/packages/forma-36-tokens/src/tokens/border-radius [deprecated]/border-radius similarity index 100% rename from packages/forma-36-tokens/src/tokens/border-radius/border-radius.js rename to packages/forma-36-tokens/src/tokens/border-radius [deprecated]/border-radius diff --git a/packages/forma-36-tokens/src/tokens/shape-radius/radius-semantic.js b/packages/forma-36-tokens/src/tokens/shape-radius/radius-semantic.js new file mode 100644 index 0000000000..466992723e --- /dev/null +++ b/packages/forma-36-tokens/src/tokens/shape-radius/radius-semantic.js @@ -0,0 +1,16 @@ +const scaleRadius = require('./scale-radius'); + +const radiusSemantic = { + // Control radii + 'shape-radius-control-small': scaleRadius['radius-100'], + 'shape-radius-control-medium': scaleRadius['radius-200'], + 'shape-radius-control-pill': scaleRadius['radius-full'], + + // Container radii + 'shape-radius-container-tight': scaleRadius['radius-200'], + 'shape-radius-container-small': scaleRadius['radius-300'], + 'shape-radius-container-medium': scaleRadius['radius-400'], + 'shape-radius-container-full': scaleRadius['radius-full'], +}; + +module.exports = radiusSemantic; diff --git a/packages/forma-36-tokens/src/tokens/shape-radius/scale-radius.js b/packages/forma-36-tokens/src/tokens/shape-radius/scale-radius.js new file mode 100644 index 0000000000..8f8d06fde7 --- /dev/null +++ b/packages/forma-36-tokens/src/tokens/shape-radius/scale-radius.js @@ -0,0 +1,9 @@ +const scaleRadius = { + 'border-radius-100': '0.25rem', + 'border-radius-200': '0.5rem', + 'border-radius-300': '0.75rem', + 'border-radius-400': '1rem', + 'border-radius-full': '999rem', +}; + +module.exports = scaleRadius; diff --git a/packages/forma-36-tokens/src/tokens/spacing.js b/packages/forma-36-tokens/src/tokens/spacing.js deleted file mode 100644 index 0246d5f068..0000000000 --- a/packages/forma-36-tokens/src/tokens/spacing.js +++ /dev/null @@ -1,13 +0,0 @@ -const spacing = { - 'spacing-2xs': '0.25rem', - 'spacing-xs': '0.5rem', - 'spacing-s': '0.75rem', - 'spacing-m': '1rem', - 'spacing-l': '1.5rem', - 'spacing-xl': '2rem', - 'spacing-2xl': '3rem', - 'spacing-3xl': '4rem', - 'spacing-4xl': '5rem', -}; - -module.exports = spacing; diff --git a/packages/forma-36-tokens/src/tokens/spacing/padding-semantic.js b/packages/forma-36-tokens/src/tokens/spacing/padding-semantic.js new file mode 100644 index 0000000000..1c32ec39c3 --- /dev/null +++ b/packages/forma-36-tokens/src/tokens/spacing/padding-semantic.js @@ -0,0 +1,30 @@ +const scaleSpace = require('./space-scale'); + +const paddingSemantic = { + // Control paddings + 'space-control-padding-horizontal-tight': scaleSpace['space-100'], + 'space-control-padding-horizontal-small': scaleSpace['space-200'], + 'space-control-padding-vertical-tight': scaleSpace['space-50'], + + // Container paddings + 'space-container-padding-horizontal-tight': scaleSpace['space-100'], + 'space-container-padding-horizontal-small': scaleSpace['space-200'], + 'space-container-padding-horizontal-medium': scaleSpace['space-300'], + 'space-container-padding-horizontal-large': scaleSpace['space-400'], + 'space-container-padding-horizontal-xlarge': scaleSpace['space-500'], + 'space-container-padding-horizontal-2xlarge': scaleSpace['space-600'], + 'space-container-padding-horizontal-3xlarge': scaleSpace['space-700'], + 'space-container-padding-horizontal-4xlarge': scaleSpace['space-800'], + 'space-container-padding-horizontal-5xlarge': scaleSpace['space-900'], + 'space-container-padding-vertical-tight': scaleSpace['space-100'], + 'space-container-padding-vertical-small': scaleSpace['space-200'], + 'space-container-padding-vertical-medium': scaleSpace['space-300'], + 'space-container-padding-vertical-large': scaleSpace['space-400'], + 'space-container-padding-vertical-xlarge': scaleSpace['space-500'], + 'space-container-padding-vertical-2xlarge': scaleSpace['space-600'], + 'space-container-padding-vertical-3xlarge': scaleSpace['space-700'], + 'space-container-padding-vertical-4xlarge': scaleSpace['space-800'], + 'space-container-padding-vertical-5xlarge': scaleSpace['space-900'], +}; + +module.exports = paddingSemantic; diff --git a/packages/forma-36-tokens/src/tokens/spacing/scale-spacing.js b/packages/forma-36-tokens/src/tokens/spacing/scale-spacing.js new file mode 100644 index 0000000000..a68235b064 --- /dev/null +++ b/packages/forma-36-tokens/src/tokens/spacing/scale-spacing.js @@ -0,0 +1,14 @@ +const scaleSpace = { + 'spacing-50': '0.125rem', + 'spacing-100': '0.25rem', + 'spacing-200': '0.5rem', + 'spacing-300': '0.75rem', + 'spacing-400': '1rem', + 'spacing-500': '1.5rem', + 'spacing-600': '2rem', + 'spacing-700': '3rem', + 'spacing-800': '4rem', + 'spacing-900': '5rem', +}; + +module.exports = scaleSpace;