From ed5585f3a3ccb17dcce96c7b0bcb551f2279d0e3 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 8 Apr 2025 10:37:24 -0500 Subject: [PATCH 01/10] initial codemod refactor --- .../src/s1-to-s2/src/codemods/button.ts | 31 - .../src/s1-to-s2/src/codemods/changes.ts | 1386 ---------------- .../src/s1-to-s2/src/codemods/codemod.ts | 39 +- .../components/ActionGroup/transform.ts | 190 +++ .../components/ActionMenu/transform.ts | 19 + .../codemods/components/Avatar/transform.ts | 50 + .../codemods/components/Badge/transform.ts | 18 + .../components/Breadcrumbs/transform.ts | 41 + .../codemods/components/Button/transform.ts | 61 + .../components/CheckboxGroup/transform.ts | 13 + .../components/ColorField/transform.ts | 32 + .../components/ColorSlider/transform.ts | 13 + .../codemods/components/Column/transform.ts | 13 + .../codemods/components/ComboBox/transform.ts | 53 + .../components/ContextualHelp/transform.ts | 17 + .../components/DialogContainer/transform.ts | 18 + .../components/DialogTrigger/transform.ts | 77 + .../codemods/components/Divider/transform.ts | 13 + .../src/codemods/components/Form/transform.ts | 28 + .../components/InlineAlert/transform.ts | 18 + .../src/codemods/components/Item/transform.ts | 34 + .../src/codemods/components/Link/transform.ts | 40 + .../components/NumberField/transform.ts | 27 + .../codemods/components/Picker/transform.ts | 48 + .../components/ProgressBar/transform.ts | 27 + .../components/ProgressCircle/transform.ts | 17 + .../components/RadioGroup/transform.ts | 27 + .../components/RangeSlider/transform.ts | 23 + .../src/codemods/components/Row/transform.ts | 107 ++ .../components/SearchField/transform.ts | 37 + .../codemods/components/Section/transform.ts | 39 + .../codemods/components/Slider/transform.ts | 33 + .../components/StatusLight/transform.ts | 23 + .../codemods/components/Table/transform.ts | 176 ++ .../src/codemods/components/Tabs/transform.ts | 101 ++ .../codemods/components/TagGroup/transform.ts | 31 + .../codemods/components/TextArea/transform.ts | 37 + .../components/TextField/transform.ts | 37 + .../codemods/components/Tooltip/transform.ts | 34 + .../components/TooltipTrigger/transform.ts | 14 + .../src/s1-to-s2/src/codemods/dialog.ts | 47 - .../src/{ => codemods/icons}/iconMap.ts | 0 .../src/codemods/{ => shared}/colors.ts | 0 .../src/codemods/{ => shared}/dimensions.ts | 0 .../src/codemods/{ => shared}/styleProps.ts | 4 +- .../src/codemods/shared/transforms.ts | 794 +++++++++ .../src/codemods/{ => shared}/unsafeStyle.ts | 2 +- .../src/codemods/{ => shared}/utils.ts | 0 .../src/s1-to-s2/src/codemods/transforms.ts | 1446 ----------------- 49 files changed, 2404 insertions(+), 2931 deletions(-) delete mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/button.ts delete mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/changes.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Column/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Row/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Table/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts delete mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/dialog.ts rename packages/dev/codemods/src/s1-to-s2/src/{ => codemods/icons}/iconMap.ts (100%) rename packages/dev/codemods/src/s1-to-s2/src/codemods/{ => shared}/colors.ts (100%) rename packages/dev/codemods/src/s1-to-s2/src/codemods/{ => shared}/dimensions.ts (100%) rename packages/dev/codemods/src/s1-to-s2/src/codemods/{ => shared}/styleProps.ts (99%) create mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts rename packages/dev/codemods/src/s1-to-s2/src/codemods/{ => shared}/unsafeStyle.ts (99%) rename packages/dev/codemods/src/s1-to-s2/src/codemods/{ => shared}/utils.ts (100%) delete mode 100644 packages/dev/codemods/src/s1-to-s2/src/codemods/transforms.ts diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/button.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/button.ts deleted file mode 100644 index 62bbbebb0f7..00000000000 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/button.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {getPropValue} from './utils'; -import {NodePath} from '@babel/traverse'; -import * as t from '@babel/types'; - -export function transformButton(path: NodePath) { - path.traverse({ - JSXAttribute(path) { - let value = getPropValue(path.node.value); - if (path.node.name.type !== 'JSXIdentifier' || !value) { - return; - } - - switch (path.node.name.name) { - case 'variant': { - if (value.type === 'StringLiteral') { - if (value.value === 'cta') { - value.value = 'accent'; - } else if (value.value === 'overBackground') { - value.value = 'primary'; - path.insertAfter(t.jsxAttribute( - t.jsxIdentifier('staticColor'), - t.stringLiteral('white') - )); - } - } - break; - } - } - } - }); -} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/changes.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/changes.ts deleted file mode 100644 index 4f23d59e83d..00000000000 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/changes.ts +++ /dev/null @@ -1,1386 +0,0 @@ -import { - AddCommentToElementOptions, - CommentOutPropOptions, - ConvertDimensionValueToPxOptions, - MovePropToNewChildComponentOptions, - MovePropToParentComponentOptions, - MoveRenderPropsOptions, - RemoveComponentIfWithinParentOptions, - RemovePropOptions, - UpdateComponentIfPropPresentOptions, - UpdateComponentWithinCollectionOptions, - UpdatePlacementToSingleValueProps, - UpdatePropNameAndValueOptions, - UpdatePropNameOptions, - UpdatePropValueAndAddNewPropOptions, - UpdateToNewComponentOptions -} from './transforms'; - -type FunctionInfo = - | { name: 'commentOutProp', args: CommentOutPropOptions } - | { name: 'removeProp', args: RemovePropOptions } - | { name: 'updatePropNameAndValue', args: UpdatePropNameAndValueOptions } - | { - name: 'updatePropValueAndAddNewProp', - args: UpdatePropValueAndAddNewPropOptions - } - | { name: 'updatePropName', args: UpdatePropNameOptions } - | { - name: 'updateComponentIfPropPresent', - args: UpdateComponentIfPropPresentOptions - } - | { name: 'updateToNewComponent', args: UpdateToNewComponentOptions } - | { name: 'moveRenderPropsToChild', args: MoveRenderPropsOptions } - | { name: 'removeProp', args: RemovePropOptions } - | { - name: 'updateComponentWithinCollection', - args: UpdateComponentWithinCollectionOptions - } - | { - name: 'updateTabs', - args: {} - } - | { - name: 'movePropToNewChildComponent', - args: MovePropToNewChildComponentOptions - } - | { - name: 'movePropToParentComponent', - args: MovePropToParentComponentOptions - } - | { - name: 'convertDimensionValueToPx', - args: ConvertDimensionValueToPxOptions - } - | { - name: 'updatePlacementToSingleValue', - args: UpdatePlacementToSingleValueProps - } - | { - name: 'removeComponentIfWithinParent', - args: RemoveComponentIfWithinParentOptions - } - | { - name: 'addCommentToElement', - args: AddCommentToElementOptions - } - | { - name: 'commentIfParentCollectionNotDetected', - args: {} - } - | { - name: 'updateAvatarSize', - args: {} - } - | { - name: 'updateLegacyLink', - args: {} - } - | { - name: 'addColumnsPropToRow', - args: {} - } - | { - name: 'updateRowFunctionArg', - args: {} - } - | { - name: 'updateKeyToId', - args: {} - } - | { - name: 'updateDialogChild', - args: {} - } - | { - name: 'updateActionGroup', - args: {} - } | { - name: 'commentIfNestedColumns', - args: {} - } | { - name: 'addRowHeader', - args: {} - } - -type Change = { - description: string, - reason: string, - function: FunctionInfo -}; - -type ComponentChanges = { - changes: Change[] -}; - -type ChangesJSON = { - [component: string]: ComponentChanges -}; - -export const changes: ChangesJSON = { - Avatar: { - changes: [ - { - description: 'Comment out isDisabled', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'isDisabled'} - } - }, - { - description: 'Update size prop', - reason: 'Updated naming convention', - function: { - name: 'updateAvatarSize', - args: {} - } - } - ] - }, - ActionGroup: { - changes: [ - { - description: 'Comment out overflowMode', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'overflowMode'} - } - }, - { - description: 'Comment out buttonLabelBehavior', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'buttonLabelBehavior'} - } - }, - { - description: 'Comment out summaryIcon', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'summaryIcon'} - } - }, - { - description: 'Replace with ActionButtonGroup or ToggleButtonGroup', - reason: 'The API has changed', - function: {name: 'updateActionGroup', args: {}} - } - ] - }, - ActionMenu: { - changes: [ - { - description: 'Comment out closeOnSelect', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'closeOnSelect'} - } - }, - { - description: 'Comment out trigger', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'trigger'} - } - } - ] - }, - Badge: { - changes: [ - { - description: "Change variant='info' to variant='informative'", - reason: 'Updated naming convention', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'variant', - oldValue: 'info', - newProp: 'variant', - newValue: 'informative' - } - } - } - ] - }, - Breadcrumbs: { - changes: [ - { - description: 'Comment out showRoot', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'showRoot'} - } - }, - { - description: 'Comment out isMultiline', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'isMultiline'} - } - }, - { - description: 'Comment out autoFocusCurrent', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'autoFocusCurrent'} - } - }, - { - description: 'Remove size="S"', - reason: 'Small is no longer a supported size in Spectrum 2', - function: { - name: 'removeProp', - args: {propToRemove: 'size', propValue: 'S'} - } - }, - { - description: 'Add comment to wrap in nav element if needed', - reason: 'A nav element is no longer included inside Breadcrumbs by default. You can wrap the Breadcrumbs component in a nav element if needed.', - function: { - name: 'addCommentToElement', - args: {comment: 'S2 Breadcrumbs no longer includes a nav element by default. You can wrap the Breadcrumbs component in a nav element if needed.'} - } - } - ] - }, - Button: { - changes: [ - { - description: 'Change variant="cta" to variant="accent"', - reason: 'Call-to-action was deprecated', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'variant', - oldValue: 'cta', - newProp: 'variant', - newValue: 'accent' - } - } - }, - { - description: - 'Change variant="overBackground" to variant="primary" staticColor="white"', - reason: 'Updated design guidelines', - function: { - name: 'updatePropValueAndAddNewProp', - args: { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'variant', - newValue: 'primary', - additionalProp: 'staticColor', - additionalValue: 'white' - } - } - }, - { - description: 'Change style to fillStyle', - reason: 'To avoid confusion with HTMLElement style attribute', - function: { - name: 'updatePropName', - args: {oldProp: 'style', newProp: 'fillStyle'} - } - }, - { - description: 'Comment out isPending', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'isPending'} - } - }, - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - }, - { - description: - 'If href is present, Button should be converted to a LinkButton', - reason: 'Improved API and behavior for links', - function: { - name: 'updateComponentIfPropPresent', - args: { - newComponent: 'LinkButton', - propToCheck: 'href' - } - } - }, - { - description: 'Remove elementType', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'elementType'}} - } - ] - }, - CheckboxGroup: { - changes: [ - { - description: 'Remove showErrorIcon', - reason: 'It has been removed for accessibility reasons', - function: { - name: 'removeProp', - args: {propToRemove: 'showErrorIcon'} - } - } - ] - }, - ColorArea: { - changes: [] - }, - ColorWheel: { - changes: [] - }, - ColorSlider: { - changes: [ - { - description: 'Remove showValueLabel', - reason: 'It has been removed for accessibility reasons', - function: { - name: 'removeProp', - args: {propToRemove: 'showValueLabel'} - } - } - ] - }, - ColorField: { - changes: [ - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'isQuiet'} - } - }, - { - description: 'Remove placeholder', - reason: 'It has been removed for accessibility reasons', - function: { - name: 'removeProp', - args: {propToRemove: 'placeholder'} - } - }, - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - } - ] - }, - ComboBox: { - changes: [ - { - description: - 'Change menuWidth value from a DimensionValue to a pixel value', - reason: 'Updated design guidelines', - function: { - name: 'convertDimensionValueToPx', - args: { - propToConvertValue: 'menuWidth' - } - } - }, - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - }, - { - description: 'Comment out loadingState', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'loadingState'} - } - }, - { - description: 'Remove placeholder', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'placeholder'}} - }, - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - }, - { - description: 'Comment out onLoadMore', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'onLoadMore'} - } - } - ] - }, - Dialog: { - changes: [] - }, - DialogTrigger: { - changes: [ - { - description: "Comment out type='tray'", - reason: 'Tray has not been implemented yet', - function: {name: 'commentOutProp', args: {propToComment: 'type', propValue: 'tray'}} - }, - { - description: "Comment out mobileType='tray'", - reason: 'mobileType has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'mobileType'} - } - }, - { - description: 'Remove targetRef', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'targetRef'}} - }, - { - description: - 'Update children to move render props from being the second child of DialogTrigger to being a child of Dialog', - reason: 'Updated API', - function: { - name: 'moveRenderPropsToChild', - args: {newChildComponent: 'Dialog'} - } - }, - { - description: 'Rename isDismissable to isDismissible', - reason: 'Fixed spelling', - function: {name: 'updatePropName', args: {oldProp: 'isDismissable', newProp: 'isDismissible'}} - }, - { - description: 'Update Dialog child to Popover or FullscreenDialog depending on type prop', - reason: 'Updated API', - function: {name: 'updateDialogChild', args: {}} - } - ] - }, - DialogContainer: { - changes: [ - { - description: 'Rename isDismissable to isDismissible', - reason: 'Fixed spelling', - function: {name: 'updatePropName', args: {oldProp: 'isDismissable', newProp: 'isDismissible'}} - }, - { - description: 'Update Dialog child to Popover or FullscreenDialog depending on type prop', - reason: 'Updated API', - function: {name: 'updateDialogChild', args: {}} - } - ] - }, - Divider: { - changes: [ - { - description: 'Remove Divider if within a Dialog', - reason: 'Updated design', - function: { - name: 'removeComponentIfWithinParent', - args: {parentComponent: 'Dialog'} - } - } - ] - }, - Form: { - changes: [ - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - }, - { - description: 'Remove isReadOnly', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isReadOnly'}} - }, - { - description: 'Remove validationState', - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState'} - } - } - ] - }, - InlineAlert: { - changes: [ - { - description: "Change variant='info' to variant='informative'", - reason: 'Updated naming convention', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'variant', - oldValue: 'info', - newProp: 'variant', - newValue: 'informative' - } - } - } - ] - }, - Item: { - changes: [ - { - description: 'If within Menu, update Item to be a MenuItem', - reason: 'Updated collections API', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'Menu', - newComponent: 'MenuItem' - } - } - }, - { - description: 'If within ActionMenu, update Item to be a MenuItem', - reason: 'Updated collections API', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'ActionMenu', - newComponent: 'MenuItem' - } - } - }, - { - description: 'If within TagGroup, update Item to be a Tag', - reason: 'Updated collections API', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'TagGroup', - newComponent: 'Tag' - } - } - }, - { - description: 'If within Breadcrumbs, update Item to be a Breadcrumb', - reason: 'Updated collections API', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'Breadcrumbs', - newComponent: 'Breadcrumb' - } - } - }, - { - description: 'If within Picker, update Item to be a PickerItem', - reason: 'Updated collections API', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'Picker', - newComponent: 'PickerItem' - } - } - }, - { - description: 'If within ComboBox, update Item to be a ComboBoxItem', - reason: 'Updated collections API', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'ComboBox', - newComponent: 'ComboBoxItem' - } - } - }, - { - description: 'Leave comment if we cannot determine parent collection component', - reason: 'No collection parent detected', - function: { - name: 'commentIfParentCollectionNotDetected', - args: {} - } - } - // TODO: Not yet implemented in S2 - // { - // description: 'If within ListBox, update Item to be a ListBoxItem', - // reason: 'Updated collections API', - // function: { - // name: 'updateComponentWithinCollection', - // args: { - // parentComponent: 'ListBox', - // newComponent: 'ListBoxItem' - // } - // } - // }, - ] - }, - Link: { - changes: [ - { - description: "Change variant='overBackground' to staticColor='white'", - reason: 'Updated design guidelines', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'staticColor', - newValue: 'white' - } - } - }, - { - description: 'Remove inner anchor element if used (legacy API)', - reason: 'Updated API', - function: { - name: 'updateLegacyLink', - args: {} - } - } - ] - }, - MenuTrigger: { - changes: [ - { - description: 'Comment out closeOnSelect', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'closeOnSelect'} - } - } - ] - }, - SubmenuTrigger: { - changes: [ - { - description: 'Remove targetKey', - reason: 'Potential v3 bug or API differ bug', - function: {name: 'removeProp', args: {propToRemove: 'targetKey'}} - } - ] - }, - NumberField: { - changes: [ - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - }, - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - } - ] - }, - Picker: { - changes: [ - { - description: - 'Change menuWidth value from a DimensionValue to a pixel value', - reason: 'Updated design guidelines', - function: { - name: 'convertDimensionValueToPx', - args: { - propToConvertValue: 'menuWidth' - } - } - }, - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - }, - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - }, - { - description: 'Comment out isLoading', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'isLoading'} - } - }, - { - description: 'Comment out onLoadMore', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'onLoadMore'} - } - } - ] - }, - ProgressBar: { - changes: [ - { - description: "Change variant='overBackground' to staticColor='white'", - reason: 'Updated design guidelines', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'staticColor', - newValue: 'white' - } - } - }, - { - description: 'Comment out labelPosition', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'labelPosition'} - } - }, - { - description: 'Remove showValueLabel', - reason: 'It was removed for accessibility reasons', - function: {name: 'removeProp', args: {propToRemove: 'showValueLabel'}} - } - ] - }, - ProgressCircle: { - changes: [ - { - description: "Change variant='overBackground' to staticColor='white'", - reason: 'Updated design guidelines', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'staticColor', - newValue: 'white' - } - } - } - ] - }, - RadioGroup: { - changes: [ - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - }, - { - description: 'Remove showErrorIcon', - reason: 'It has been removed for accessibility reasons', - function: { - name: 'removeProp', - args: {propToRemove: 'showErrorIcon'} - } - } - ] - }, - RangeSlider: { - changes: [ - { - description: 'Remove showValueLabel', - reason: 'It was removed for accessibility reasons', - function: {name: 'removeProp', args: {propToRemove: 'showValueLabel'}} - }, - { - description: 'Comment out getValueLabel', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'getValueLabel'} - } - }, - { - description: 'Comment out orientation', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'orientation'} - } - } - ] - }, - SearchField: { - changes: [ - { - description: 'Remove placeholder', - reason: 'It has been removed for accessibility reasons', - function: {name: 'removeProp', args: {propToRemove: 'placeholder'}} - }, - { - description: 'Comment out icon', - reason: 'It has not been implemented yet', - function: {name: 'commentOutProp', args: {propToComment: 'icon'}} - }, - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - }, - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - } - ] - }, - Section: { - changes: [ - { - description: 'If within Menu, update Section to be a MenuSection', - reason: 'Updated component structure', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'Menu', - newComponent: 'MenuSection' - } - } - }, - { - description: 'If within Picker, update Section to be a PickerSection', - reason: 'Updated component structure', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'Picker', - newComponent: 'PickerSection' - } - } - }, - { - description: 'If within ComboBox, update Section to be a ComboBoxSection', - reason: 'Updated component structure', - function: { - name: 'updateComponentWithinCollection', - args: { - parentComponent: 'ComboBox', - newComponent: 'ComboBoxSection' - } - } - }, - { - description: - 'Move title prop string to be a child of new Heading within a Header', - reason: 'Updated API', - function: { - name: 'movePropToNewChildComponent', - args: { - parentComponent: 'Menu', - childComponent: 'MenuSection', - propToMove: 'title', - newChildComponent: 'Header' - } - } - }, - { - description: - 'Move title prop string to be a child of new Heading within a Header', - reason: 'Updated API', - function: { - name: 'movePropToNewChildComponent', - args: { - parentComponent: 'Picker', - childComponent: 'PickerSection', - propToMove: 'title', - newChildComponent: 'Header' - } - } - }, - { - description: - 'Move title prop string to be a child of new Heading within a Header', - reason: 'Updated API', - function: { - name: 'movePropToNewChildComponent', - args: { - parentComponent: 'ComboBox', - childComponent: 'ComboBoxSection', - propToMove: 'title', - newChildComponent: 'Header' - } - } - }, - { - description: 'Leave comment if we cannot determine parent collection component', - reason: 'No collection parent detected', - function: { - name: 'commentIfParentCollectionNotDetected', - args: {} - } - } - ] - }, - Slider: { - changes: [ - { - description: 'Remove isFilled', - reason: 'Slider is always filled in Spectrum 2', - function: {name: 'removeProp', args: {propToRemove: 'isFilled'}} - }, - { - description: 'Remove trackGradient', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'trackGradient'}} - }, - { - description: 'Remove showValueLabel', - reason: 'It was removed for accessibility reasons', - function: {name: 'removeProp', args: {propToRemove: 'showValueLabel'}} - }, - { - description: 'Comment out getValueLabel', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'getValueLabel'} - } - }, - { - description: 'Comment out orientation', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'orientation'} - } - } - ] - }, - StatusLight: { - changes: [ - { - description: 'Remove isDisabled', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isDisabled'}} - }, - { - description: "Change variant='info' to variant='informative'", - reason: 'Updated naming convention', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'variant', - oldValue: 'info', - newProp: 'variant', - newValue: 'informative' - } - } - } - ] - }, - Tabs: { - changes: [ - { - description: 'Remove TabPanels components and keep individual TabPanel components inside.', - reason: 'Updated collections API', - function: { - name: 'updateTabs', - args: {} - } - }, - { - description: 'Remove isEmphasized', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isEmphasized'}} - }, - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - } - ] - }, - TagGroup: { - changes: [ - { - description: 'Change actionLabel to groupActionLabel', - reason: 'To match new onGroupAction prop', - function: { - name: 'updatePropName', - args: {oldProp: 'actionLabel', newProp: 'groupActionLabel'} - } - }, - { - description: 'Change onAction to onGroupAction', - reason: 'To avoid confusion with existing onAction prop on other collection components', - function: { - name: 'updatePropName', - args: {oldProp: 'onAction', newProp: 'onGroupAction'} - } - }, - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - } - ] - }, - TextArea: { - changes: [ - { - description: 'Comment out icon', - reason: 'It has not been implemented yet', - function: {name: 'commentOutProp', args: {propToComment: 'icon'}} - }, - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - }, - { - description: 'Remove placeholder', - reason: 'It has been removed for accessibility reasons', - function: {name: 'removeProp', args: {propToRemove: 'placeholder'}} - }, - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - } - ] - }, - TextField: { - changes: [ - { - description: 'Comment out icon', - reason: 'It has not been implemented yet', - function: {name: 'commentOutProp', args: {propToComment: 'icon'}} - }, - { - description: 'Remove isQuiet', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'isQuiet'}} - }, - { - description: 'Remove placeholder', - reason: 'It has been removed for accessibility reasons', - function: {name: 'removeProp', args: {propToRemove: 'placeholder'}} - }, - { - description: "Change validationState='invalid' to isInvalid", - reason: 'Updated API', - function: { - name: 'updatePropNameAndValue', - args: { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true - } - } - }, - { - description: "Remove validationState='valid'", - reason: 'It is no longer supported', - function: { - name: 'removeProp', - args: {propToRemove: 'validationState', propValue: 'valid'} - } - } - ] - }, - Tooltip: { - changes: [ - { - description: 'Remove variant', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'variant'}} - }, - { - description: - 'Remove placement and add to the parent TooltipTrigger instead', - reason: 'Updated API', - function: { - name: 'movePropToParentComponent', - args: { - parentComponent: 'TooltipTrigger', - childComponent: 'Tooltip', - propToMove: 'placement' - } - } - }, - { - description: 'Remove showIcon', - reason: 'It is no longer supported', - function: {name: 'removeProp', args: {propToRemove: 'showIcon'}} - }, - { - description: - 'Remove isOpen and add to the parent TooltipTrigger instead', - reason: 'Updated API', - function: { - name: 'movePropToParentComponent', - args: { - parentComponent: 'TooltipTrigger', - childComponent: 'Tooltip', - propToMove: 'isOpen' - } - } - } - ] - }, - TooltipTrigger: { - changes: [ - { - description: 'Update placement to use single value', - reason: 'Updated API', - function: { - name: 'updatePlacementToSingleValue', - args: { - propToUpdate: 'placement', - childComponent: 'Tooltip' - } - } - } - ] - }, - TableView: { - changes: [ - { - description: 'Add columns prop to Row', - reason: 'Rows now require a columns prop from TableHeader', - function: { - name: 'addColumnsPropToRow', - args: {} - } - }, - { - description: 'Leave comment if nested columns are used', - reason: 'Nested columns are not supported yet', - function: { - name: 'commentIfNestedColumns', - args: {} - } - }, - { - description: 'Comment out dragAndDropHooks', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'dragAndDropHooks'} - } - }, - { - description: 'Comment out selectionStyle="highlight"', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'selectionStyle'} - } - }, - { - description: 'Comment out UNSTABLE_allowsExpandableRows', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'UNSTABLE_allowsExpandableRows'} - } - }, - { - description: 'Comment out UNSTABLE_defaultExpandedKeys', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'UNSTABLE_defaultExpandedKeys'} - } - }, - { - description: 'Comment out UNSTABLE_expandedKeys', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'UNSTABLE_expandedKeys'} - } - }, - { - description: 'Comment out UNSTABLE_onExpandedChange', - reason: 'It has not been implemented yet', - function: { - name: 'commentOutProp', - args: {propToComment: 'UNSTABLE_onExpandedChange'} - } - }, - { - description: 'Add isRowHeader prop to fist Column if one doesn\'t eixst already', - reason: 'Updated API', - function: { - name: 'addRowHeader', - args: {} - } - } - ] - }, - Column: { - changes: [ - { - description: 'Update key prop to id', - reason: 'Updated API', - function: { - name: 'updateKeyToId', - args: {} - } - } - ] - }, - Row: { - changes: [ - { - description: 'Update key prop to id', - reason: 'Updated API', - function: { - name: 'updateKeyToId', - args: {} - } - }, - { - description: 'Update child function to receive column object instead of column key', - reason: 'Updated API', - function: { - name: 'updateRowFunctionArg', - args: {} - } - } - ] - } -}; diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts index 1ed7c570c1c..c9abf54b2cb 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts @@ -1,13 +1,11 @@ /* eslint-disable max-depth */ -import {addComment} from './utils'; +import {addComment} from './shared/utils'; import {API, FileInfo} from 'jscodeshift'; -import {changes as changesJSON} from './changes'; -import {functionMap} from './transforms'; import {getComponents} from '../getComponents'; -import {iconMap} from '../iconMap'; +import {iconMap} from './icons/iconMap'; import {parse as recastParse} from 'recast'; import * as t from '@babel/types'; -import {transformStyleProps} from './styleProps'; +import transformStyleProps from './shared/styleProps'; import traverse, {Binding, NodePath} from '@babel/traverse'; // Determine list of available components in S2 from index.ts @@ -192,20 +190,25 @@ export default function transformer(file: FileInfo, api: API, options: Options) addComment(path.node, ' TODO(S2-upgrade): Could not transform style prop automatically: ' + error); } - const componentInfo = changesJSON[elementName]; - if (!componentInfo) { - return; - } - const {changes} = componentInfo; - - changes.forEach((change) => { - const {function: functionInfo} = change; - let {name: functionName, args: functionArgs} = functionInfo; - // Call the respective transformation function - if (functionMap[functionName]) { - functionMap[functionName](path, functionArgs as any); + // Try to find a specific transform + try { + // Dynamically import the transform based on elementName + const transformPath = `./components/${elementName}/transform`; + // Use require for dynamic import in CommonJS context + const componentTransform = require(transformPath); + if (componentTransform && typeof componentTransform.default === 'function') { + componentTransform.default(path); + } else { + // Optional: Add a comment if a transform is expected but not found or not a function + // addComment(path.node, ` TODO(S2-upgrade): Transform for ${elementName} not found or invalid.`); } - }); + } catch (error) { + // Handle cases where the transform file might not exist + if (error.code !== 'MODULE_NOT_FOUND') { + addComment(path.node, ` TODO(S2-upgrade): Error applying transform for ${elementName}: ${error.message}`); + } + // If MODULE_NOT_FOUND, it means no specific transform exists, which might be okay. + } }); if (hasMacros) { diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts new file mode 100644 index 00000000000..40ac1408103 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts @@ -0,0 +1,190 @@ +import {addComponentImport} from '../../shared/utils'; +import {commentOutProp} from '../../shared/transforms'; +import {getComponents} from '../../../getComponents'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +let availableComponents = getComponents(); + +/** + * Transforms ActionGroup props and structure: + * - Use ActionButtonGroup if no selection. + * - Use ToggleButtonGroup if selection is used. + * - Comment out overflowMode (it has not been implemented yet). + * - Comment out buttonLabelBehavior (it has not been implemented yet). + * - Comment out summaryIcon (it has not been implemented yet). + * - Update root level onAction to onPress on each ActionButton. + * - Apply isDisabled directly on each ActionButton/ToggleButton instead of disabledKeys. + * - Update key to id (keep key for map). + * - Convert dynamic collections render function to items.map. + */ +export default function transformActionGroup(path: NodePath) { + // Comment out overflowMode + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'overflowMode'}); + + // Comment out buttonLabelBehavior + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'buttonLabelBehavior'}); + + // Comment out summaryIcon + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'summaryIcon'}); + + let selectionModePath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'selectionMode') as NodePath | undefined; + let selectionMode = t.isStringLiteral(selectionModePath?.node.value) ? selectionModePath.node.value.value : 'none'; + let newComponent, childComponent; + if (selectionMode === 'none') { + newComponent = 'ActionButtonGroup'; + childComponent = 'ActionButton'; + selectionModePath?.remove(); + } else { + newComponent = 'ToggleButtonGroup'; + childComponent = 'ToggleButton'; + } + + let localName = newComponent; + if (availableComponents.has(newComponent)) { + let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; + localName = addComponentImport(program, newComponent); + } + + let localChildName = childComponent; + if (availableComponents.has(childComponent)) { + let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; + localChildName = addComponentImport(program, childComponent); + } + + + // Convert dynamic collection to an array.map. + let items = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'items') as NodePath | undefined; + let itemArg: t.Identifier | undefined; + if (items && t.isJSXExpressionContainer(items.node.value) && t.isExpression(items.node.value.expression)) { + let child = path.get('children').find(c => c.isJSXExpressionContainer()); + if (child && child.isJSXExpressionContainer() && t.isFunction(child.node.expression)) { + let arg = child.node.expression.params[0]; + if (t.isIdentifier(arg)) { + itemArg = arg; + } + + child.replaceWith( + t.jsxExpressionContainer( + t.callExpression( + t.memberExpression( + items.node.value.expression, + t.identifier('map') + ), + [child.node.expression] + ) + ) + ); + } + } + items?.remove(); + + let onAction = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'onAction') as NodePath | undefined; + + // Pull disabledKeys prop out into a variable, converted to a Set. + // Then we can check it in the isDisabled prop of each item. + let disabledKeysPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'disabledKeys') as NodePath | undefined; + let disabledKeys: t.Identifier | undefined; + if (disabledKeysPath && t.isJSXExpressionContainer(disabledKeysPath.node.value) && t.isExpression(disabledKeysPath.node.value.expression)) { + disabledKeys = path.scope.generateUidIdentifier('disabledKeys'); + path.scope.push({ + id: disabledKeys, + init: t.newExpression(t.identifier('Set'), [disabledKeysPath.node.value.expression]), + kind: 'let' + }); + disabledKeysPath.remove(); + } + + path.traverse({ + JSXElement(child) { + if (t.isJSXIdentifier(child.node.openingElement.name) && child.node.openingElement.name.name === 'Item') { + // Replace Item with ActionButton or ToggleButton. + let childNode = t.cloneNode(child.node); + childNode.openingElement.name = t.jsxIdentifier(localChildName); + if (childNode.closingElement) { + childNode.closingElement.name = t.jsxIdentifier(localChildName); + } + + // If there is no key prop and we are using dynamic collections, add a default computed from item.key ?? item.id. + let key = childNode.openingElement.attributes.find(attr => t.isJSXAttribute(attr) && attr.name.name === 'key') as t.JSXAttribute | undefined; + if (!key && itemArg) { + let id = t.jsxExpressionContainer( + t.logicalExpression( + '??', + t.memberExpression(itemArg, t.identifier('key')), + t.memberExpression(itemArg, t.identifier('id')) + ) + ); + + key = t.jsxAttribute( + t.jsxIdentifier('key'), + id + ); + + childNode.openingElement.attributes.push(key); + } + + // If this is a ToggleButtonGroup, add an id prop in addition to key when needed. + if (key && newComponent === 'ToggleButtonGroup') { + // If we are in an array.map we need both key and id. Otherwise, we only need id. + if (itemArg) { + childNode.openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('id'), key.value)); + } else { + key.name.name = 'id'; + } + } + + let keyValue: t.Expression | undefined = undefined; + if (key && t.isJSXExpressionContainer(key.value) && t.isExpression(key.value.expression)) { + keyValue = key.value.expression; + } else if (key && t.isStringLiteral(key.value)) { + keyValue = key.value; + } + + // Add an onPress to each item that calls the previous onAction, passing in the key. + if (onAction && t.isJSXExpressionContainer(onAction.node.value) && t.isExpression(onAction.node.value.expression)) { + childNode.openingElement.attributes.push( + t.jsxAttribute( + t.jsxIdentifier('onPress'), + t.jsxExpressionContainer( + keyValue + ? t.arrowFunctionExpression([], t.callExpression(onAction.node.value.expression, [keyValue])) + : onAction.node.value.expression + ) + ) + ); + } + + // Add an isDisabled prop to each item, testing whether it is in disabledKeys. + if (disabledKeys && keyValue) { + childNode.openingElement.attributes.push( + t.jsxAttribute( + t.jsxIdentifier('isDisabled'), + t.jsxExpressionContainer( + t.callExpression( + t.memberExpression( + disabledKeys, + t.identifier('has') + ), + [keyValue] + ) + ) + ) + ); + } + + child.replaceWith(childNode); + } + } + }); + + onAction?.remove(); + + path.node.openingElement.name = t.jsxIdentifier(localName); + if (path.node.closingElement) { + path.node.closingElement.name = t.jsxIdentifier(localName); + } +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts new file mode 100644 index 00000000000..813932f9ed7 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts @@ -0,0 +1,19 @@ +import {commentOutProp, updateComponentWithinCollection} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms ActionMenu props: + * - Comment out closeOnSelect (it has not been implemented yet). + * - Comment out trigger (it has not been implemented yet). + * - Update Item to be a MenuItem. + */ +export default function transformActionMenu(path: NodePath) { + // Comment out closeOnSelect + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'closeOnSelect'}); + + // Comment out trigger + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'trigger'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts new file mode 100644 index 00000000000..1ce020bd270 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts @@ -0,0 +1,50 @@ +import {commentOutProp} from '../../shared/transforms'; +import {getName} from '../../shared/utils'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +function updateAvatarSize( + path: NodePath +) { + if ( + t.isJSXElement(path.node) && + t.isJSXIdentifier(path.node.openingElement.name) && + getName(path, path.node.openingElement.name) === 'Avatar' + ) { + let sizeAttrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'size') as NodePath; + if (sizeAttrPath) { + let value = sizeAttrPath.node.value; + if (value?.type === 'StringLiteral') { + const avatarDimensions = { + 'avatar-size-50': 16, + 'avatar-size-75': 18, + 'avatar-size-100': 20, + 'avatar-size-200': 22, + 'avatar-size-300': 26, + 'avatar-size-400': 28, + 'avatar-size-500': 32, + 'avatar-size-600': 36, + 'avatar-size-700': 40 + }; + let val = avatarDimensions[value.value as keyof typeof avatarDimensions]; + if (val != null) { + sizeAttrPath.node.value = t.jsxExpressionContainer(t.numericLiteral(val)); + } + } + } + } +} + +/** + * Transforms Avatar props: + * - Comment out isDisabled (it has not been implemented yet). + * - Update size to be a pixel value if it currently matches 'avatar-size-*'. + */ +export default function transformAvatar(path: NodePath) { + // Comment out isDisabled + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'isDisabled'}); + + // Update size to be a pixel value if it currently matches 'avatar-size-*' + updateAvatarSize(path); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts new file mode 100644 index 00000000000..7b97b3ee2af --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts @@ -0,0 +1,18 @@ +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; +import {updatePropNameAndValue} from '../../shared/transforms'; + +/** + * Transforms Badge props: + * - Change variant="info" to variant="informative". + */ +export default function transformBadge(path: NodePath) { + // Change variant="info" to variant="informative" + // Reason: Updated naming convention + updatePropNameAndValue(path, { + oldProp: 'variant', + oldValue: 'info', + newProp: 'variant', + newValue: 'informative' + }); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts new file mode 100644 index 00000000000..0ec360177b0 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts @@ -0,0 +1,41 @@ +import { + addCommentToElement, + commentOutProp, + removeProp, + updateComponentWithinCollection +} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms Breadcrumbs props: + * - Comment out showRoot (it has not been implemented yet). + * - Comment out isMultiline (it has not been implemented yet). + * - Comment out autoFocusCurrent (it has not been implemented yet). + * - Remove size="S" (Small is no longer a supported size in Spectrum 2). + * - Update Item to be a Breadcrumb. + * - Add comment to wrap in nav element if needed. + */ +export default function transformBreadcrumbs(path: NodePath) { + // Comment out showRoot + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'showRoot'}); + + // Comment out isMultiline + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'isMultiline'}); + + // Comment out autoFocusCurrent + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'autoFocusCurrent'}); + + // Remove size="S" + // Reason: Small is no longer a supported size in Spectrum 2 + removeProp(path, {propToRemove: 'size', propValue: 'S'}); + + // Add comment to wrap in nav element if needed + // Reason: A nav element is no longer included inside Breadcrumbs by default. + addCommentToElement(path, { + comment: 'S2 Breadcrumbs no longer includes a nav element by default. You can wrap the Breadcrumbs component in a nav element if needed.' + }); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts new file mode 100644 index 00000000000..49e0f366594 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts @@ -0,0 +1,61 @@ +import { + commentOutProp, + removeProp, + updateComponentIfPropPresent, + updatePropName, + updatePropNameAndValue, + updatePropValueAndAddNewProp +} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms Button props: + * - Change variant="cta" to variant="accent" + * - Change variant="overBackground" to variant="primary" staticColor="white" + * - Change style to fillStyle + * - Comment out isPending (it has not been implemented yet) + * - Remove isQuiet (it is no longer supported in Spectrum 2) + * - If href is present, the Button should be converted to a LinkButton + * - Remove elementType (it is no longer supported in Spectrum 2). + */ +export default function transformButton(path: NodePath) { + // Change variant="cta" to variant="accent" + updatePropNameAndValue(path, { + oldProp: 'variant', + oldValue: 'cta', + newProp: 'variant', + newValue: 'accent' + }); + + // Change variant="overBackground" to variant="primary" staticColor="white" + updatePropValueAndAddNewProp(path, { + oldProp: 'variant', + oldValue: 'overBackground', + newProp: 'variant', + newValue: 'primary', + additionalProp: 'staticColor', + additionalValue: 'white' + }); + + // Change style to fillStyle + updatePropName(path, { + oldProp: 'style', + newProp: 'fillStyle' + }); + + // Comment out isPending + commentOutProp(path, {propToComment: 'isPending'}); + + // Remove isQuiet + removeProp(path, {propToRemove: 'isQuiet'}); + + // If href is present, the Button should be converted to a LinkButton + updateComponentIfPropPresent(path, { + propToCheck: 'href', + newComponent: 'LinkButton' + }); + + // Remove elementType + removeProp(path, {propToRemove: 'elementType'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts new file mode 100644 index 00000000000..1a732ca4f73 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts @@ -0,0 +1,13 @@ +import {NodePath} from '@babel/traverse'; +import {removeProp} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms CheckboxGroup props: + * - Remove showErrorIcon (it has been removed due to accessibility issues). + */ +export default function transformCheckboxGroup(path: NodePath) { + // Remove showErrorIcon + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'showErrorIcon'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts new file mode 100644 index 00000000000..2447331bb82 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts @@ -0,0 +1,32 @@ +import {NodePath} from '@babel/traverse'; +import {removeProp, updatePropNameAndValue} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms ColorField props: + * - Remove isQuiet (it is no longer supported in Spectrum 2). + * - Remove placeholder (it has been removed due to accessibility issues). + * - Change validationState="invalid" to isInvalid. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + */ +export default function transformColorField(path: NodePath) { + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); + + // Remove placeholder + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'placeholder'}); + + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts new file mode 100644 index 00000000000..039d38e45b2 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts @@ -0,0 +1,13 @@ +import {NodePath} from '@babel/traverse'; +import {removeProp} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms ColorSlider props: + * - Remove showValueLabel (it has been removed due to accessibility issues). + */ +export default function transformColorSlider(path: NodePath) { + // Remove showValueLabel + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'showValueLabel'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Column/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Column/transform.ts new file mode 100644 index 00000000000..4ecd60888dd --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Column/transform.ts @@ -0,0 +1,13 @@ +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; +import {updateKeyToId} from '../../shared/transforms'; + +/** + * Transforms Column: + * - Update key to id. + */ +export default function transformColumn(path: NodePath) { + // Update key to id + // Reason: Standardizing collection item identifiers. + updateKeyToId(path); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts new file mode 100644 index 00000000000..3ee7a463553 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts @@ -0,0 +1,53 @@ +import { + commentOutProp, + convertDimensionValueToPx, + removeProp, + updatePropNameAndValue +} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms ComboBox props: + * - Change menuWidth value from a DimensionValue to a pixel value. + * - Remove isQuiet (it is no longer supported in Spectrum 2). + * - Comment out loadingState (it has not been implemented yet). + * - Remove placeholder (it is no longer supported in Spectrum 2). + * - Change validationState="invalid" to isInvalid. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + * - Comment out onLoadMore (it has not been implemented yet). + * - Update Item to be a ComboBoxItem. + */ +export default function transformComboBox(path: NodePath) { + // Change menuWidth value from a DimensionValue to a pixel value + // Reason: API change + convertDimensionValueToPx(path, {propToConvertValue: 'menuWidth'}); + + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); + + // Comment out loadingState + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'loadingState'}); + + // Remove placeholder + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'placeholder'}); + + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + + // Comment out onLoadMore + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'onLoadMore'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts new file mode 100644 index 00000000000..b8ad3e03057 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts @@ -0,0 +1,17 @@ +import {commentOutProp, updatePlacementToSingleValue} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms ContextualHelp props: + * - Comment out variant="info" (informative variant is the only one supported). + * - Update placement prop to have only one value (e.g., "bottom left" becomes "bottom"). + */ +export default function transformContextualHelp(path: NodePath) { + // Comment out variant="info" + // Reason: Informative variant is the only one supported + commentOutProp(path, {propToComment: 'variant', propValue: 'info'}); + + // Update placement prop to have only one value + updatePlacementToSingleValue(path, {propToUpdate: 'placement'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts new file mode 100644 index 00000000000..93149624e0b --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts @@ -0,0 +1,18 @@ +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; +import {updateDialogChild} from '../DialogTrigger/transform'; +import {updatePropName} from '../../shared/transforms'; + +/** + * Transforms DialogContainer props: + * - Remove type (dependent on the dialog level child used, e.g., Dialog, FullscreenDialog, Popover). + * - Move isDismissable (as isDismissible) to the dialog level component. + * - Move isKeyboardDismissDisabled to the dialog level component. + */ +export default function transformDialogContainer(path: NodePath) { + // Move isDismissable (as isDismissible) to the dialog level component + // Reason: Prop now exists on the dialog level component + updatePropName(path, {oldProp: 'isDismissable', newProp: 'isDismissible'}); + + updateDialogChild(path); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts new file mode 100644 index 00000000000..3c2da332996 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts @@ -0,0 +1,77 @@ +import {addComponentImport, getName} from '../../shared/utils'; +import { + commentOutProp, + moveRenderPropsToChild, + removeProp, + updatePropName +} from '../../shared/transforms'; +import {getComponents} from '../../../getComponents'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +let availableComponents = getComponents(); + +/** + * Updates DialogTrigger and DialogContainer to the new API. + * + * Example: + * - When `type="popover"`, replaces Dialog with ``. + * - When `type="fullscreen"`, replaces Dialog with ``. + * - When `type="fullscreenTakeover"`, replaces Dialog with ``. + */ +export function updateDialogChild( + path: NodePath +) { + let typePath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'type') as NodePath | undefined; + let type = typePath?.node.value?.type === 'StringLiteral' ? typePath.node.value?.value : 'modal'; + let newComponent = 'Dialog'; + let props: t.JSXAttribute[] = []; + if (type === 'popover') { + newComponent = 'Popover'; + } else if (type === 'fullscreen' || type === 'fullscreenTakeover') { + newComponent = 'FullscreenDialog'; + if (type === 'fullscreenTakeover') { + props.push(t.jsxAttribute(t.jsxIdentifier('variant'), t.stringLiteral(type))); + } + } + + for (let prop of ['isDismissible', 'mobileType', 'hideArrow', 'placement', 'shouldFlip', 'isKeyboardDismissDisabled', 'containerPadding', 'offset', 'crossOffset']) { + let attr = path.get('openingElement').get('attributes').find(attr => attr.isJSXAttribute() && attr.node.name.name === prop) as NodePath | undefined; + if (attr) { + props.push(attr.node); + attr.remove(); + } + } + + typePath?.remove(); + + let localName = newComponent; + if (newComponent !== 'Dialog' && availableComponents.has(newComponent)) { + let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; + localName = addComponentImport(program, newComponent); + } + + path.traverse({ + JSXElement(dialog) { + if (!t.isJSXIdentifier(dialog.node.openingElement.name) || getName(dialog, dialog.node.openingElement.name) !== 'Dialog') { + return; + } + + dialog.node.openingElement.name = t.jsxIdentifier(localName); + if (dialog.node.closingElement) { + dialog.node.closingElement.name = t.jsxIdentifier(localName); + } + + dialog.node.openingElement.attributes.push(...props); + } + }); +} + +export default function transformDialogTrigger(path: NodePath) { + commentOutProp(path, {propToComment: 'type', propValue: 'tray'}); + commentOutProp(path, {propToComment: 'mobileType'}); + removeProp(path, {propToRemove: 'targetRef'}); + moveRenderPropsToChild(path, {newChildComponent: 'Dialog'}); + updatePropName(path, {oldProp: 'isDismissable', newProp: 'isDismissible'}); + updateDialogChild(path); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts new file mode 100644 index 00000000000..784ae9c9ab1 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts @@ -0,0 +1,13 @@ +import {NodePath} from '@babel/traverse'; +import {removeComponentIfWithinParent} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms Divider: + * - Remove Divider component if within a Dialog (Updated design for Dialog in Spectrum 2). + */ +export default function transformDivider(path: NodePath) { + // Remove Divider component if within a Dialog + // Reason: Updated design for Dialog in Spectrum 2 + removeComponentIfWithinParent(path, {parentComponent: 'Dialog'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts new file mode 100644 index 00000000000..6df3bc35e72 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts @@ -0,0 +1,28 @@ +import {NodePath} from '@babel/traverse'; +import {removeProp} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms Form props: + * - Remove isQuiet (it is no longer supported in Spectrum 2). + * - Remove isReadOnly (it is no longer supported in Spectrum 2). + * - Remove validationState (it is no longer supported in Spectrum 2). + * - Remove validationBehavior (it is no longer supported in Spectrum 2). + */ +export default function transformForm(path: NodePath) { + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); + + // Remove isReadOnly + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isReadOnly'}); + + // Remove validationState + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState'}); + + // Remove validationBehavior + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationBehavior'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts new file mode 100644 index 00000000000..354ac37028f --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts @@ -0,0 +1,18 @@ +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; +import {updatePropNameAndValue} from '../../shared/transforms'; + +/** + * Transforms InlineAlert props: + * - Change variant="info" to variant="informative". + */ +export default function transformInlineAlert(path: NodePath) { + // Change variant="info" to variant="informative" + // Reason: Updated naming convention + updatePropNameAndValue(path, { + oldProp: 'variant', + oldValue: 'info', + newProp: 'variant', + newValue: 'informative' + }); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts new file mode 100644 index 00000000000..272e00c7c32 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts @@ -0,0 +1,34 @@ +import {commentIfParentCollectionNotDetected, updateComponentWithinCollection, updateKeyToId} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms Item: + * - If within Menu: Update Item to be a MenuItem. + * - If within ActionMenu: Update Item to be a MenuItem. + * - If within TagGroup: Update Item to be a Tag. + * - If within Breadcrumbs: Update Item to be a Breadcrumb. + * - If within Picker: Update Item to be a PickerItem. + * - If within ComboBox: Update Item to be a ComboBoxItem. + * - If within ListBox: Update Item to be a ListBoxItem. + * - If within TabList: Update Item to be a Tab. + * - If within TabPanels: Update Item to be a TabPanel and remove surrounding TabPanels. + * - Update key to id (and keep key if rendered inside array.map). + */ +export default function transformItem(path: NodePath) { + // Comment if parent collection not detected + // Reason: Item needs to be updated based on its parent collection component. + commentIfParentCollectionNotDetected(path); + + // Update key to id + // Reason: Standardizing collection item identifiers. + updateKeyToId(path); + + // Update Items based on parent collection component + updateComponentWithinCollection(path, {parentComponent: 'Menu', newComponent: 'MenuItem'}); + updateComponentWithinCollection(path, {parentComponent: 'ActionMenu', newComponent: 'MenuItem'}); + updateComponentWithinCollection(path, {parentComponent: 'TagGroup', newComponent: 'Tag'}); + updateComponentWithinCollection(path, {parentComponent: 'Breadcrumbs', newComponent: 'Breadcrumb'}); + updateComponentWithinCollection(path, {parentComponent: 'Picker', newComponent: 'PickerItem'}); + updateComponentWithinCollection(path, {parentComponent: 'ComboBox', newComponent: 'ComboBoxItem'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts new file mode 100644 index 00000000000..072a1703157 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts @@ -0,0 +1,40 @@ +import {addComment} from '../../shared/utils'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; +import {updatePropNameAndValue} from '../../shared/transforms'; + +/** + * Transforms Link props: + * - Change variant="overBackground" to staticColor="white". + * - If was used inside Link (legacy API), remove the and apply props directly to Link. + */ +export default function transformLink(path: NodePath) { + // Change variant="overBackground" to staticColor="white" + // Reason: Updated naming convention + updatePropNameAndValue(path, { + oldProp: 'variant', + oldValue: 'overBackground', + newProp: 'staticColor', + newValue: 'white' + }); + + let missingOuterHref = t.isJSXElement(path.node) && !path.node.openingElement.attributes.some((attr) => t.isJSXAttribute(attr) && attr.name.name === 'href'); + if (missingOuterHref) { + let innerLink = path.node.children.find((child) => t.isJSXElement(child) && t.isJSXIdentifier(child.openingElement.name)); + if (innerLink && t.isJSXElement(innerLink)) { + let innerAttributes = innerLink.openingElement.attributes; + let outerAttributes = path.node.openingElement.attributes; + innerAttributes.forEach((attr) => { + outerAttributes.push(attr); + }); + + if ( + t.isJSXIdentifier(innerLink.openingElement.name) && + innerLink.openingElement.name.name !== 'a' + ) { + addComment(path.node, ' TODO(S2-upgrade): You may have been using a custom link component here. You\'ll need to update this manually.'); + } + path.node.children = innerLink.children; + } + } +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts new file mode 100644 index 00000000000..9a90443f15d --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts @@ -0,0 +1,27 @@ +import {NodePath} from '@babel/traverse'; +import {removeProp, updatePropNameAndValue} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms NumberField props: + * - Remove isQuiet (it is no longer supported in Spectrum 2). + * - Change validationState="invalid" to isInvalid. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + */ +export default function transformNumberField(path: NodePath) { + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); + + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts new file mode 100644 index 00000000000..bd836399685 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts @@ -0,0 +1,48 @@ +import { + commentOutProp, + convertDimensionValueToPx, + removeProp, + updatePropNameAndValue +} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms Picker props: + * - Change menuWidth value from a DimensionValue to a pixel value. + * - Remove isQuiet (it is no longer supported in Spectrum 2). + * - Change validationState="invalid" to isInvalid. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + * - Comment out isLoading (it has not been implemented yet). + * - Comment out onLoadMore (it has not been implemented yet). + * - Update Item to be a PickerItem. + */ +export default function transformPicker(path: NodePath) { + // Change menuWidth value from a DimensionValue to a pixel value + // Reason: API change + convertDimensionValueToPx(path, {propToConvertValue: 'menuWidth'}); + + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); + + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + + // Comment out isLoading + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'isLoading'}); + + // Comment out onLoadMore + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'onLoadMore'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts new file mode 100644 index 00000000000..6b63344f7cc --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts @@ -0,0 +1,27 @@ +import {commentOutProp, removeProp, updatePropNameAndValue} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms ProgressBar props: + * - Change variant="overBackground" to staticColor="white". + * - Comment out labelPosition (it has not been implemented yet). + * - Comment out showValueLabel (it has not been implemented yet). + */ +export default function transformProgressBar(path: NodePath) { + // Change variant="overBackground" to staticColor="white" + updatePropNameAndValue(path, { + oldProp: 'variant', + oldValue: 'overBackground', + newProp: 'staticColor', + newValue: 'white' + }); + + // Comment out labelPosition + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'labelPosition'}); + + // Comment out showValueLabel + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'showValueLabel'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts new file mode 100644 index 00000000000..419ccbca165 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts @@ -0,0 +1,17 @@ +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; +import {updatePropNameAndValue} from '../../shared/transforms'; + +/** + * Transforms ProgressCircle props: + * - Change variant="overBackground" to staticColor="white". + */ +export default function transformProgressCircle(path: NodePath) { + // Change variant="overBackground" to staticColor="white" + updatePropNameAndValue(path, { + oldProp: 'variant', + oldValue: 'overBackground', + newProp: 'staticColor', + newValue: 'white' + }); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts new file mode 100644 index 00000000000..e105619731c --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts @@ -0,0 +1,27 @@ +import {NodePath} from '@babel/traverse'; +import {removeProp, updatePropNameAndValue} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms RadioGroup props: + * - Change validationState="invalid" to isInvalid. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + * - Remove showErrorIcon (it has been removed due to accessibility issues). + */ +export default function transformRadioGroup(path: NodePath) { + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + + // Remove showErrorIcon + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'showErrorIcon'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts new file mode 100644 index 00000000000..f2802e1e391 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts @@ -0,0 +1,23 @@ +import {commentOutProp, removeProp} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms RangeSlider props: + * - Remove showValueLabel (it has been removed due to accessibility issues). + * - Comment out getValueLabel (it has not been implemented yet). + * - Comment out orientation (it has not been implemented yet). + */ +export default function transformRangeSlider(path: NodePath) { + // Remove showValueLabel + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'showValueLabel'}); + + // Comment out getValueLabel + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'getValueLabel'}); + + // Comment out orientation + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'orientation'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Row/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Row/transform.ts new file mode 100644 index 00000000000..c64e024cb9e --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Row/transform.ts @@ -0,0 +1,107 @@ +import {getName} from '../../shared/utils'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; +import {updateKeyToId} from '../../shared/transforms'; + +/** + * Updates the export function signature of the Row component. + */ +function updateRowFunctionArg( + path: NodePath +) { + // Find the export function passed as a child + let functionChild = path.get('children').find(childPath => + childPath.isJSXExpressionContainer() && + childPath.get('expression').isArrowFunctionExpression() + ); + + let tablePath = path.findParent((p) => + t.isJSXElement(p.node) && + t.isJSXIdentifier(p.node.openingElement.name) && + getName(path, p.node.openingElement.name) === 'TableView' + ); + + let tableHeaderPath = tablePath?.get('children').find((child) => + t.isJSXElement(child.node) && + t.isJSXIdentifier(child.node.openingElement.name) && + getName(child as NodePath, child.node.openingElement.name) === 'TableHeader' + ) as NodePath | undefined; + + function findColumnKeyProp(path: NodePath) { + let columnKeyProp = 'id'; + path.traverse({ + JSXElement(columnPath) { + if ( + t.isArrowFunctionExpression(columnPath.parentPath.node) && + t.isJSXElement(columnPath.node) && + t.isJSXIdentifier(columnPath.node.openingElement.name) && + getName(columnPath as NodePath, columnPath.node.openingElement.name) === 'Column' + ) { + let openingElement = columnPath.get('openingElement'); + let keyPropPath = openingElement.get('attributes').find(attr => + t.isJSXAttribute(attr.node) && + (attr.node.name.name === 'key' || attr.node.name.name === 'id') + ); + keyPropPath?.traverse({ + Identifier(innerPath) { + if ( + innerPath.node.name === 'column' && + innerPath.parentPath.node.type === 'MemberExpression' && + t.isIdentifier(innerPath.parentPath.node.property) + ) { + columnKeyProp = innerPath.parentPath.node.property.name; + } + } + }); + } + } + }); + return columnKeyProp || 'id'; + } + + let columnKey = findColumnKeyProp(tableHeaderPath as NodePath); + + if (functionChild && functionChild.isJSXExpressionContainer()) { + let arrowFuncPath = functionChild.get('expression'); + if (arrowFuncPath.isArrowFunctionExpression()) { + let params = arrowFuncPath.node.params; + if (params.length === 1 && t.isIdentifier(params[0])) { + let paramName = params[0].name; + + // Rename parameter to 'column' + params[0].name = 'column'; + + // Replace references to the old parameter name inside the export function body + arrowFuncPath.get('body').traverse({ + Identifier(innerPath) { + if ( + innerPath.node.name === paramName && + // Ensure we're not replacing the parameter declaration + innerPath.node !== params[0] + ) { + // Replace with column key + innerPath.replaceWith( + t.memberExpression( + t.identifier('column'), + t.identifier(columnKey ?? 'id') + ) + ); + } + } + }); + } + } + } +} + +/** + * Transforms Row: + * - Update key to id. + */ +export default function transformRow(path: NodePath) { + // Update key to id + // Reason: Standardizing collection item identifiers. + updateKeyToId(path); + + updateRowFunctionArg(path); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts new file mode 100644 index 00000000000..bd5285abcfd --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts @@ -0,0 +1,37 @@ +import {commentOutProp, removeProp, updatePropNameAndValue} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms SearchField props: + * - Remove placeholder (it has been removed due to accessibility issues). + * - Comment out icon (it has not been implemented yet). + * - Remove isQuiet (it is no longer supported in Spectrum 2). + * - Change validationState="invalid" to isInvalid. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + */ +export default function transformSearchField(path: NodePath) { + // Remove placeholder + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'placeholder'}); + + // Comment out icon + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'icon'}); + + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); + + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts new file mode 100644 index 00000000000..dc2152620e6 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts @@ -0,0 +1,39 @@ +import {commentIfParentCollectionNotDetected, movePropToNewChildComponent, updateComponentWithinCollection} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms Section: + * - If within Menu: Update Section to be a MenuSection. + * - If within Picker: Update Section to be a PickerSection. + * - If within ComboBox: Update Section to be a ComboBoxSection. + */ +export default function transformSection(path: NodePath) { + // Update Sections based on parent collection component + updateComponentWithinCollection(path, {parentComponent: 'Menu', newComponent: 'MenuSection'}); + updateComponentWithinCollection(path, {parentComponent: 'Picker', newComponent: 'PickerSection'}); + updateComponentWithinCollection(path, {parentComponent: 'ComboBox', newComponent: 'ComboBoxSection'}); + + movePropToNewChildComponent(path, { + parentComponent: 'Menu', + childComponent: 'MenuSection', + propToMove: 'title', + newChildComponent: 'Header' + }); + movePropToNewChildComponent(path, { + parentComponent: 'Picker', + childComponent: 'PickerSection', + propToMove: 'title', + newChildComponent: 'Header' + }); + movePropToNewChildComponent(path, { + parentComponent: 'ComboBox', + childComponent: 'ComboBoxSection', + propToMove: 'title', + newChildComponent: 'Header' + }); + + // Comment if parent collection not detected + // Reason: Section needs to be updated based on its parent collection component. + commentIfParentCollectionNotDetected(path); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts new file mode 100644 index 00000000000..03bf10d6f3f --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts @@ -0,0 +1,33 @@ +import {commentOutProp, removeProp} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms Slider props: + * - Remove isFilled (Slider is always filled in Spectrum 2). + * - Remove trackGradient (Not supported in S2 design). + * - Remove showValueLabel (it has been removed due to accessibility issues). + * - Comment out getValueLabel (it has not been implemented yet). + * - Comment out orientation (it has not been implemented yet). + */ +export default function transformSlider(path: NodePath) { + // Remove isFilled + // Reason: Slider is always filled in Spectrum 2 + removeProp(path, {propToRemove: 'isFilled'}); + + // Remove trackGradient + // Reason: Not supported in S2 design + removeProp(path, {propToRemove: 'trackGradient'}); + + // Remove showValueLabel + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'showValueLabel'}); + + // Comment out getValueLabel + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'getValueLabel'}); + + // Comment out orientation + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'orientation'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts new file mode 100644 index 00000000000..dadd63576e6 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts @@ -0,0 +1,23 @@ +import {NodePath} from '@babel/traverse'; +import {removeProp, updatePropNameAndValue} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms StatusLight props: + * - Remove isDisabled (it is no longer supported in Spectrum 2). + * - Change variant="info" to variant="informative". + */ +export default function transformStatusLight(path: NodePath) { + // Remove isDisabled + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isDisabled'}); + + // Change variant="info" to variant="informative" + // Reason: Updated naming convention + updatePropNameAndValue(path, { + oldProp: 'variant', + oldValue: 'info', + newProp: 'variant', + newValue: 'informative' + }); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Table/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Table/transform.ts new file mode 100644 index 00000000000..68b11ffa58a --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Table/transform.ts @@ -0,0 +1,176 @@ +import {addComment, getName} from '../../shared/utils'; +import {commentOutProp, updateKeyToId} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Copies the columns prop from the TableHeader to the Row component. + */ +function addColumnsPropToRow( + path: NodePath +) { + const tableHeaderPath = path.get('children').find((child) => + t.isJSXElement(child.node) && + t.isJSXIdentifier(child.node.openingElement.name) && + getName(child as NodePath, child.node.openingElement.name) === 'TableHeader' + ) as NodePath | undefined; + + if (!tableHeaderPath) { + addComment(path.node, ' TODO(S2-upgrade): Could not find TableHeader within Table to retrieve columns prop.'); + return; + } + + const columnsProp = tableHeaderPath + .get('openingElement') + .get('attributes') + .find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'columns') as NodePath | undefined; + + if (columnsProp) { + path.traverse({ + JSXElement(innerPath) { + if ( + t.isJSXElement(innerPath.node) && + t.isJSXIdentifier(innerPath.node.openingElement.name) && + getName(innerPath as NodePath, innerPath.node.openingElement.name) === 'Row' + ) { + let rowPath = innerPath as NodePath; + rowPath.node.openingElement.attributes.push(columnsProp.node); + + // If Row doesn't contain id prop, leave a comment for the user to check manually + let idProp = rowPath.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'id'); + if (!idProp) { + addComment(rowPath.node, ' TODO(S2-upgrade): If the items do not have id properties, you\'ll need to add an id prop to the Row.'); + } + } + } + }); + } +} + +function commentIfNestedColumns( + path: NodePath +) { + const headerPath = path.get('children').find((child) => + t.isJSXElement(child.node) && + t.isJSXIdentifier(child.node.openingElement.name) && + getName(child as NodePath, child.node.openingElement.name) === 'TableHeader' + ) as NodePath | undefined; + const columns = headerPath?.get('children') || []; + + let hasNestedColumns = false; + + columns.forEach(column => { + let columnChildren = column.get('children'); + if ( + columnChildren.find(child => + t.isJSXElement(child.node) && + t.isJSXIdentifier(child.node.openingElement.name) && + getName(child as NodePath, child.node.openingElement.name) === 'Column' + ) + ) { + hasNestedColumns = true; + } + }); + + if (hasNestedColumns) { + addComment(path.node, ' TODO(S2-upgrade): Nested Column components are not supported yet.'); + } +} + +/** + * Adds isRowHeader to the first Column in a table if there isn't already a row header. + * @param path + */ +function addRowHeader( + path: NodePath +) { + let tableHeaderPath = path.get('children').find((child) => + t.isJSXElement(child.node) && + t.isJSXIdentifier(child.node.openingElement.name) && + getName(child as NodePath, child.node.openingElement.name) === 'TableHeader' + ) as NodePath | undefined; + + + // Check if isRowHeader is already set on a Column + let hasRowHeader = false; + tableHeaderPath?.get('children').forEach((child) => { + if ( + t.isJSXElement(child.node) && + t.isJSXIdentifier(child.node.openingElement.name) && + getName(child as NodePath, child.node.openingElement.name) === 'Column' + ) { + let isRowHeaderProp = (child.get('openingElement') as NodePath).get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'isRowHeader') as NodePath | undefined; + if (isRowHeaderProp) { + hasRowHeader = true; + } + } + }); + + // If there isn't already a row header, add one to the first Column if possible + if (!hasRowHeader) { + tableHeaderPath?.get('children').forEach((child) => { + // Add to first Column if static + if ( + !hasRowHeader && + t.isJSXElement(child.node) && + t.isJSXIdentifier(child.node.openingElement.name) && + getName(child as NodePath, child.node.openingElement.name) === 'Column' + ) { + child.node.openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('isRowHeader'), t.jsxExpressionContainer(t.booleanLiteral(true)))); + hasRowHeader = true; + } + + // If render function is used, leave a comment to update manually + if ( + t.isJSXExpressionContainer(child.node) && + t.isArrowFunctionExpression(child.node.expression) + ) { + addComment(child.node, ' TODO(S2-upgrade): You\'ll need to add isRowHeader to one of the columns manually.'); + } + + // If array.map is used, leave a comment to update manually + if ( + t.isJSXExpressionContainer(child.node) && + t.isCallExpression(child.node.expression) && + t.isMemberExpression(child.node.expression.callee) && + t.isIdentifier(child.node.expression.callee.property) && + child.node.expression.callee.property.name === 'map' + ) { + addComment(child.node, ' TODO(S2-upgrade): You\'ll need to add isRowHeader to one of the columns manually.'); + } + }); + } +} + +/** + * Transforms TableView props: + * - For Column and Row: Update key to be id (and keep key if rendered inside array.map). + * - For dynamic tables, pass a columns prop into Row. + * - For Row: Update dynamic render function to pass in column instead of columnKey. + * - Comment out UNSTABLE_allowsExpandableRows (it has not been implemented yet). + * - Comment out UNSTABLE_onExpandedChange (it has not been implemented yet). + * - Comment out UNSTABLE_expandedKeys (it has not been implemented yet). + * - Comment out UNSTABLE_defaultExpandedKeys (it has not been implemented yet). + */ +export default function transformTable(path: NodePath) { + // Add columns prop to Row for dynamic tables + addColumnsPropToRow(path); + + // Comment out nested columns + // Reason: Nested columns are not supported yet + commentIfNestedColumns(path); + + // Comment out dragAndDropHooks + commentOutProp(path, {propToComment: 'dragAndDropHooks'}); + + // Comment out selectionStyle="highlight" + commentOutProp(path, {propToComment: 'selectionStyle', propValue: 'highlight'}); + + // Comment out unstable expandable rows props + commentOutProp(path, {propToComment: 'UNSTABLE_allowsExpandableRows'}); + commentOutProp(path, {propToComment: 'UNSTABLE_onExpandedChange'}); + commentOutProp(path, {propToComment: 'UNSTABLE_expandedKeys'}); + commentOutProp(path, {propToComment: 'UNSTABLE_defaultExpandedKeys'}); + + addRowHeader(path); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts new file mode 100644 index 00000000000..aa9f5f2865c --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts @@ -0,0 +1,101 @@ +import {getName, removeComponentImport} from '../../shared/utils'; +import {NodePath} from '@babel/traverse'; +import {removeProp, updateComponentWithinCollection, updateToNewComponent} from '../../shared/transforms'; +import * as t from '@babel/types'; + +function transformTabList(tabListPath: NodePath): t.JSXElement { + tabListPath.get('children').forEach(itemPath => { + if ( + t.isJSXElement(itemPath.node) && + t.isJSXIdentifier(itemPath.node.openingElement.name) && + getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' + ) { + updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'TabList', newComponent: 'Tab'}); + } + }); + return tabListPath.node; +} + +function transformTabPanels(tabPanelsPath: NodePath, itemsProp: t.JSXAttribute | null): t.JSXElement[] { + // Dynamic case + let dynamicRender = tabPanelsPath.get('children').find(path => t.isJSXExpressionContainer(path.node)); + if (dynamicRender) { + updateToNewComponent(tabPanelsPath, {newComponent: 'Collection'}); + let itemPath = (dynamicRender.get('expression') as NodePath).get('body'); + updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'Collection', newComponent: 'TabPanel'}); + if (itemsProp) { + tabPanelsPath.node.openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('items'), itemsProp.value)); + } + return [tabPanelsPath.node]; + } + + // Static case + return tabPanelsPath.get('children').map(itemPath => { + if ( + t.isJSXElement(itemPath.node) && + t.isJSXIdentifier(itemPath.node.openingElement.name) && + getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' + ) { + updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'TabPanels', newComponent: 'TabPanel'}); + return itemPath.node; + } + return null; + }).filter(Boolean) as t.JSXElement[]; +} + +/** + * Transforms Tabs props and structure: + * - Inside TabList: Update Item to be Tab. + * - Update items on Tabs to be on TabList. + * - Inside TabPanels: Update Item to be a TabPanel and remove the surrounding TabPanels. + * - Remove isEmphasized (it is no longer supported in Spectrum 2). + * - Remove isQuiet (it is no longer supported in Spectrum 2). + */ +export default function transformTabs(path: NodePath) { + + let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; + removeComponentImport(program, 'TabPanels'); + + let tabListNode: t.JSXElement | null = null; + let tabPanelsNodes: t.JSXElement[] = []; + let itemsProp: t.JSXAttribute | null = null; + + path.node.openingElement.attributes = path.node.openingElement.attributes.filter(attr => { + if (t.isJSXAttribute(attr) && attr.name.name === 'items') { + itemsProp = attr; + return false; + } + return true; + }); + + path.get('children').forEach(childPath => { + if (t.isJSXElement(childPath.node)) { + if ( + t.isJSXIdentifier(childPath.node.openingElement.name) && + getName(childPath as NodePath, childPath.node.openingElement.name) === 'TabList' + ) { + tabListNode = transformTabList(childPath as NodePath); + if (itemsProp) { + tabListNode.openingElement.attributes.push(itemsProp); + } + } else if ( + t.isJSXIdentifier(childPath.node.openingElement.name) && + getName(childPath as NodePath, childPath.node.openingElement.name) === 'TabPanels' + ) { + tabPanelsNodes = transformTabPanels(childPath as NodePath, itemsProp); + } + } + }); + + if (tabListNode) { + path.node.children = [tabListNode, ...tabPanelsNodes]; + } + + // Remove isEmphasized + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isEmphasized'}); + + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts new file mode 100644 index 00000000000..51d2af90886 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts @@ -0,0 +1,31 @@ +import {NodePath} from '@babel/traverse'; +import {removeProp, updatePropName, updatePropNameAndValue} from '../../shared/transforms'; +import * as t from '@babel/types'; + +/** + * Transforms TagGroup props: + * - Rename actionLabel to groupActionLabel. + * - Rename onAction to onGroupAction. + * - Change validationState="invalid" to isInvalid. + * - Update Item to be Tag. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + */ +export default function transformTagGroup(path: NodePath) { + // Rename actionLabel to groupActionLabel + updatePropName(path, {oldProp: 'actionLabel', newProp: 'groupActionLabel'}); + + // Rename onAction to onGroupAction + updatePropName(path, {oldProp: 'onAction', newProp: 'onGroupAction'}); + + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts new file mode 100644 index 00000000000..62fb9b21a45 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts @@ -0,0 +1,37 @@ +import {commentOutProp, removeProp, updatePropNameAndValue} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms TextArea props: + * - Comment out icon (it has not been implemented yet). + * - Remove isQuiet (it is no longer supported in Spectrum 2). + * - Remove placeholder (it has been removed due to accessibility issues). + * - Change validationState="invalid" to isInvalid. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + */ +export default function transformTextArea(path: NodePath) { + // Comment out icon + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'icon'}); + + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); + + // Remove placeholder + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'placeholder'}); + + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts new file mode 100644 index 00000000000..68adfea475a --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts @@ -0,0 +1,37 @@ +import {commentOutProp, removeProp, updatePropNameAndValue} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms TextField props: + * - Comment out icon (it has not been implemented yet). + * - Remove isQuiet (it is no longer supported in Spectrum 2). + * - Remove placeholder (it has been removed due to accessibility issues). + * - Change validationState="invalid" to isInvalid. + * - Remove validationState="valid" (it is no longer supported in Spectrum 2). + */ +export default function transformTextField(path: NodePath) { + // Comment out icon + // Reason: It has not been implemented yet + commentOutProp(path, {propToComment: 'icon'}); + + // Remove isQuiet + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'isQuiet'}); + + // Remove placeholder + // Reason: It has been removed due to accessibility issues + removeProp(path, {propToRemove: 'placeholder'}); + + // Change validationState="invalid" to isInvalid + updatePropNameAndValue(path, { + oldProp: 'validationState', + oldValue: 'invalid', + newProp: 'isInvalid', + newValue: true + }); + + // Remove validationState="valid" + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts new file mode 100644 index 00000000000..6d0ca99d95b --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts @@ -0,0 +1,34 @@ +import {movePropToParentComponent, removeProp} from '../../shared/transforms'; +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; + +/** + * Transforms Tooltip props: + * - Remove variant (it is no longer supported in Spectrum 2). + * - Move placement prop to the parent TooltipTrigger. + * - Remove showIcon (it is no longer supported in Spectrum 2). + * - Move isOpen prop to the parent TooltipTrigger. + */ +export default function transformTooltip(path: NodePath) { + // Remove variant + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'variant'}); + + // Move placement prop to the parent TooltipTrigger + movePropToParentComponent(path, { + parentComponent: 'TooltipTrigger', + childComponent: 'Tooltip', + propToMove: 'placement' + }); + + // Remove showIcon + // Reason: It is no longer supported in Spectrum 2 + removeProp(path, {propToRemove: 'showIcon'}); + + // Move isOpen prop to the parent TooltipTrigger + movePropToParentComponent(path, { + parentComponent: 'TooltipTrigger', + childComponent: 'Tooltip', + propToMove: 'isOpen' + }); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts new file mode 100644 index 00000000000..d451b193ee8 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts @@ -0,0 +1,14 @@ +import {NodePath} from '@babel/traverse'; +import * as t from '@babel/types'; +import {updatePlacementToSingleValue} from '../../shared/transforms'; + +/** + * Transforms TooltipTrigger props: + * - Updates placement prop to single value. + */ +export default function transformTooltipTrigger(path: NodePath) { + updatePlacementToSingleValue(path, { + propToUpdate: 'placement', + childComponent: 'Tooltip' + }); +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/dialog.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/dialog.ts deleted file mode 100644 index 8b6fedb2443..00000000000 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/dialog.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {addComment} from './utils'; -import {NodePath} from '@babel/traverse'; -import * as t from '@babel/types'; - -export function transformDialog(path: NodePath) { - path.get('children').forEach(path => { - // S2 dialogs don't have a divider anymore. - if (path.isJSXElement()) { - let name = path.get('openingElement').get('name'); - if (name.referencesImport('@adobe/react-spectrum', 'Divider') || name.referencesImport('@react-spectrum/divider', 'Divider')) { - path.remove(); - } - } - }); -} - -export function transformDialogTrigger(path: NodePath) { - path.get('children').forEach(path => { - // Move close function inside dialog. - // TODO: handle other types of functions too? - if (!path.isJSXExpressionContainer()) { - return; - } - - let expression = path.get('expression'); - if (!expression.isArrowFunctionExpression()) { - return; - } - - let body = expression.get('body'); - if (body.isJSXElement()) { - let name = body.get('openingElement').get('name'); - if ((name.referencesImport('@adobe/react-spectrum', 'Dialog') || name.referencesImport('@react-spectrum/dialog', 'Dialog'))) { - body.node.children = [t.jsxExpressionContainer( - t.arrowFunctionExpression( - expression.node.params, - t.jsxFragment(t.jsxOpeningFragment(), t.jsxClosingFragment(), body.node.children) - ) - )]; - path.replaceWith(body.node); - return; - } - } - - addComment(body.node, ' TODO(S2-upgrade): update this dialog to move the close function inside'); - }); -} diff --git a/packages/dev/codemods/src/s1-to-s2/src/iconMap.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/icons/iconMap.ts similarity index 100% rename from packages/dev/codemods/src/s1-to-s2/src/iconMap.ts rename to packages/dev/codemods/src/s1-to-s2/src/codemods/icons/iconMap.ts diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/colors.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/colors.ts similarity index 100% rename from packages/dev/codemods/src/s1-to-s2/src/codemods/colors.ts rename to packages/dev/codemods/src/s1-to-s2/src/codemods/shared/colors.ts diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/dimensions.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/dimensions.ts similarity index 100% rename from packages/dev/codemods/src/s1-to-s2/src/codemods/dimensions.ts rename to packages/dev/codemods/src/s1-to-s2/src/codemods/shared/dimensions.ts diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/styleProps.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/styleProps.ts similarity index 99% rename from packages/dev/codemods/src/s1-to-s2/src/codemods/styleProps.ts rename to packages/dev/codemods/src/s1-to-s2/src/codemods/shared/styleProps.ts index 0752350b768..cbfaa937139 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/styleProps.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/styleProps.ts @@ -3,7 +3,7 @@ import {convertColor} from './colors'; import {convertDimension, convertGridTrack} from './dimensions'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; -import {transformUnsafeStyle} from './unsafeStyle'; +import transformUnsafeStyle from './unsafeStyle'; export const borderWidths = { none: 0, @@ -591,7 +591,7 @@ function expandSpaceShorthand( } } -export function transformStyleProps(path: NodePath, element: string) { +export default function transformStyleProps(path: NodePath, element: string) { let macroValues = new Map; let dynamicValues = new Map; let conditions = new Map; diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts new file mode 100644 index 00000000000..414319ebfa9 --- /dev/null +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts @@ -0,0 +1,794 @@ +import {addComment, addComponentImport, getName} from './utils'; +import {convertDimension} from './dimensions'; +import {getComponents} from '../../getComponents'; +import {NodePath} from '@babel/traverse'; +import type {ReactNode} from 'react'; +import * as t from '@babel/types'; + +let availableComponents = getComponents(); + +/** + * Update prop name and value to new prop name and value. + * + * Example: + * - Button: Change variant="cta" to variant="accent". + * - Link: Change `variant="overBackground"` to `staticColor="white"`. + */ +export function updatePropNameAndValue( + path: NodePath, + options: { + /** Prop name to replace. */ + oldProp: string, + /** Prop value to replace. */ + oldValue: ReactNode, + /** Updated prop name. */ + newProp: string, + /** Updated prop value. */ + newValue: ReactNode + } +) { + const {oldProp, oldValue, newProp, newValue} = options; + + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === oldProp) { + if ( + t.isStringLiteral(attrPath.node.value) && + attrPath.node.value.value === oldValue + ) { + // Update old prop name to new prop name + attrPath.node.name.name = newProp; + + // If prop value is a string and matches the old value, replace it with the new value + if (typeof newValue === 'string') { + attrPath.node.value = t.stringLiteral(newValue); + } else if (typeof newValue === 'boolean') { + if (!newValue) { + attrPath.node.value = t.jsxExpressionContainer(t.booleanLiteral(newValue)); + } else { + attrPath.node.value = null; + } + } + } else if (t.isJSXExpressionContainer(attrPath.node.value)) { + if (t.isIdentifier(attrPath.node.value.expression)) { + // @ts-ignore + if (attrPath.node.comments && [...attrPath.node.comments].some((comment) => comment.value.includes('could not be automatically'))) { + return; + } + addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${oldProp} could not be automatically updated because ${attrPath.node.value.expression.name} could not be followed.`); + } else { + // If prop value is an expression, traverse to find a string literal that matches the old and replace it with the new value + attrPath.traverse({ + StringLiteral(stringPath) { + if ( + t.isStringLiteral(stringPath.node) && + stringPath.node.value === oldValue + ) { + // Update old prop name to new prop name + attrPath.node.name.name = newProp; + + if (typeof newValue === 'string') { + stringPath.replaceWith(t.stringLiteral(newValue)); + } else if (typeof newValue === 'boolean') { + if (!newValue) { + stringPath.replaceWith(t.booleanLiteral(newValue)); + } else { + attrPath.node.value = null; + } + } + } + } + }); + } + } + } +} + +/** + * Updates a prop name and value to new prop name and value, and adds an additional prop. + * + * Example: + * - Button: Change `variant="overBackground"` to `variant="primary" staticColor="white"`. + */ +export function updatePropValueAndAddNewProp( + path: NodePath, + options: { + /** Prop name to replace. */ + oldProp: string, + /** Prop value to replace. */ + oldValue: ReactNode, + /** Updated prop name. */ + newProp: string, + /** Updated prop value. */ + newValue: ReactNode, + /** Additional new prop name to add. */ + additionalProp: string, + /** Additional new prop value to use. */ + additionalValue: string + } +) { + const { + oldProp, + oldValue, + newProp, + newValue, + additionalProp, + additionalValue + } = options; + + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; + if (attrPath && t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === oldValue) { + // Update old prop name to new prop name + attrPath.node.name.name = newProp; + + // If prop value is a string and matches the old value, replace it with the new value + if (typeof newValue === 'string') { + attrPath.node.value = t.stringLiteral(newValue); + } else if (typeof newValue === 'boolean') { + attrPath.node.value = t.jsxExpressionContainer(t.booleanLiteral(newValue)); + } + + if (additionalProp && additionalValue) { + attrPath.insertAfter( + t.jsxAttribute(t.jsxIdentifier(additionalProp), t.stringLiteral(additionalValue as string)) + ); + } + } else if (attrPath && t.isJSXExpressionContainer(attrPath.node.value)) { + // If prop value is an expression, traverse to find a string literal that matches the old and replace it with the new value + attrPath.traverse({ + StringLiteral(stringPath) { + if ( + t.isStringLiteral(stringPath.node) && + stringPath.node.value === oldValue + ) { + // Update old prop name to new prop name + attrPath.node.name.name = newProp; + + if (typeof newValue === 'string') { + stringPath.replaceWith(t.stringLiteral(newValue)); + } else if (typeof newValue === 'boolean') { + stringPath.replaceWith(t.booleanLiteral(newValue)); + } + + if (additionalProp && additionalValue) { + attrPath.insertAfter( + t.jsxAttribute(t.jsxIdentifier(additionalProp), t.stringLiteral(additionalValue as string)) + ); + } + } + } + }); + } +} + +/** + * Updates a prop name to new prop name. + * + * Example: + * - Button: Change style to fillStyle. + */ +export function updatePropName( + path: NodePath, + options: { + /** Prop name to replace. */ + oldProp: string, + /** Updated prop name. */ + newProp: string + } +) { + const {oldProp, newProp} = options; + + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === oldProp) { + attrPath.node.name.name = newProp; + } +} + +/** + * Removes a prop. + * + * Example: + * - Button: Remove isQuiet (it is no longer supported). + */ +export function removeProp( + path: NodePath, + options: { + /** Prop name to remove. */ + propToRemove: string, + /** If provided, prop will only be removed if set to this value. */ + propValue?: string + } +) { + const {propToRemove, propValue} = options; + + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToRemove) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToRemove) { + if (propValue) { + // If prop value is provided, remove prop only if it matches the value + if (t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === propValue) { + attrPath.remove(); + } else if ( + t.isJSXExpressionContainer(attrPath.node.value) + ) { + if (t.isIdentifier(attrPath.node.value.expression)) { + // @ts-ignore + // eslint-disable-next-line max-depth + if (attrPath.node.comments && [...attrPath.node.comments].some((comment) => comment.value.includes('could not be automatically'))) { + return; + } + addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToRemove} could not be automatically removed because ${attrPath.node.value.expression.name} could not be followed.`); + } else { + attrPath.traverse({ + StringLiteral(stringPath) { + if ( + t.isStringLiteral(stringPath.node) && + stringPath.node.value === propValue + ) { + // Invalid prop value was found inside expression. + addComment(attrPath.node, ` TODO(S2-upgrade): ${propToRemove}="${propValue}" is no longer supported. You'll need to update this manually.`); + } + } + }); + } + } + } else { + // No prop value provided, so remove prop regardless of value + attrPath.remove(); + } + } +} + +/** + * Comments out a prop. + * + * Example: + * - Button: Comment out isPending (it has not been implemented yet). + */ +export function commentOutProp( + path: NodePath, + options: { + /** Prop to comment out. */ + propToComment: string, + /** If provided, prop will only be commented out if set to this value. */ + propValue?: string + } +) { + const {propToComment, propValue} = options; + + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToComment) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToComment) { + if (propValue) { + // If prop value is provided, comment out prop only if it matches the value + if (t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === propValue) { + addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment}="${propValue}" has not been implemented yet.`); + attrPath.remove(); + } else { + attrPath.traverse({ + StringLiteral(stringPath) { + if ( + t.isStringLiteral(stringPath.node) && + stringPath.node.value === propValue + ) { + addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment}="${propValue}" has not been implemented yet.`); + attrPath.remove(); + } + } + }); + } + } else { + addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment} has not been implemented yet.`); + attrPath.remove(); + } + } +} + +/** + * Add a comment above an element. + * + * Example: + * - Breadcrumbs: Check if nav needs to be added around Bre. + */ +export function addCommentToElement( + path: NodePath, + options: { + /** Comment to leave. */ + comment: string + } +) { + const {comment} = options; + addComment(path.node, ` TODO(S2-upgrade): ${comment}`); +} + +/** + * If a prop is present, updates to use a new component. + * + * Example: + * - Button: If `href` is present, Button should be converted to `LinkButton`. + */ +export function updateComponentIfPropPresent( + path: NodePath, + options: { + /** Updated component to use. */ + newComponent: string, + /** Will update component if this prop is present. */ + propToCheck: string + } +) { + const {newComponent, propToCheck} = options; + + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToCheck) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToCheck) { + let node = attrPath.findParent((p) => t.isJSXElement(p.node))?.node; + if (node && t.isJSXElement(node)) { + let localName = newComponent; + if (availableComponents.has(newComponent)) { + let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; + localName = addComponentImport(program, newComponent); + } + node.openingElement.name = t.jsxIdentifier(localName); + if (node.closingElement) { + node.closingElement.name = t.jsxIdentifier(localName); + } + } + } +} + + +/** + * Remove render props, and move usage to a child component. + * + * Example: + * - DialogTrigger: Update children to remove render props usage, and note that `close` export function moved from `DialogTrigger` to `Dialog`. + */ +export function moveRenderPropsToChild( + path: NodePath, + options: { + newChildComponent: string + } +) { + const {newChildComponent} = options; + + const renderFunctionIndex = path.node.children.findIndex( + (child) => + t.isJSXExpressionContainer(child) && + t.isArrowFunctionExpression(child.expression) && + t.isJSXElement(child.expression.body) && + t.isJSXIdentifier(child.expression.body.openingElement.name)); + + const renderFunction = path.node.children[renderFunctionIndex] as t.JSXExpressionContainer; + + if ( + t.isJSXExpressionContainer(renderFunction) && + t.isArrowFunctionExpression(renderFunction.expression) && + t.isJSXElement(renderFunction.expression.body) && + t.isJSXIdentifier(renderFunction.expression.body.openingElement.name) && + getName(path, renderFunction.expression.body.openingElement.name) !== newChildComponent + ) { + addComment(renderFunction, ' TODO(S2-upgrade): update this dialog to move the close function inside'); + return; + } + + if ( + renderFunction && + t.isArrowFunctionExpression(renderFunction.expression) && + t.isJSXElement(renderFunction.expression.body) + ) { + const dialogElement = renderFunction.expression.body; + + const originalParam = renderFunction.expression.params[0]; + if (!t.isIdentifier(originalParam)) { + addComment(path.node.children[renderFunctionIndex], ' TODO(S2-upgrade): Could not automatically move the render props. You\'ll need to update this manually.'); + return; + } + const paramName = originalParam.name; + const objectPattern = t.objectPattern([ + t.objectProperty(t.identifier(paramName), + t.identifier(paramName), + false, + true + ) + ]); + + const newRenderFunction = t.jsxExpressionContainer( + t.arrowFunctionExpression( + [objectPattern], + t.jsxFragment( + t.jsxOpeningFragment(), + t.jsxClosingFragment(), + dialogElement.children + ) + ) + ); + + let removedOnDismiss = false; + const attributes = dialogElement.openingElement.attributes.filter((attr) => { + if (t.isJSXAttribute(attr) && attr.name.name === 'onDismiss') { + removedOnDismiss = true; + return false; + } + return true; + }); + + path.node.children[renderFunctionIndex] = t.jsxElement( + t.jsxOpeningElement(t.jsxIdentifier(newChildComponent), attributes), + t.jsxClosingElement(t.jsxIdentifier(newChildComponent)), + [newRenderFunction] + ); + + if (removedOnDismiss) { + addComment(path.node.children[renderFunctionIndex], ' onDismiss was removed from Dialog. Use onOpenChange on the DialogTrigger, or onDismiss on the DialogContainer instead'); + } + } +} + + +/** + * If within a collection component, updates to use a new component. + * + * Example: + * - Item: If within `Menu`, update name from `Item` to `MenuItem`. + */ +export function updateComponentWithinCollection( + path: NodePath, + options: { + parentComponent: string, + newComponent: string + } +) { + const {parentComponent, newComponent} = options; + + // Collections currently implemented + // TODO: Add 'ActionGroup', 'ListBox', 'ListView' once implemented + const collectionItemParents = new Set(['Menu', 'ActionMenu', 'TagGroup', 'Breadcrumbs', 'Picker', 'ComboBox', 'ListBox', 'TabList', 'TabPanels', 'Collection']); + + if ( + t.isJSXElement(path.node) && + t.isJSXIdentifier(path.node.openingElement.name) + ) { + // Find closest parent collection component + let closestParentCollection = path.findParent((p) => + t.isJSXElement(p.node) && + t.isJSXIdentifier(p.node.openingElement.name) && + collectionItemParents.has(getName(path, p.node.openingElement.name)) + ); + if ( + closestParentCollection && + t.isJSXElement(closestParentCollection.node) && + t.isJSXIdentifier(closestParentCollection.node.openingElement.name) && + getName(path, closestParentCollection.node.openingElement.name) === parentComponent + ) { + // If closest parent collection component matches parentComponent, replace with newComponent + + updateKeyToId(path); + + let localName = newComponent; + if (availableComponents.has(newComponent)) { + let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; + localName = addComponentImport(program, newComponent); + } + + let newNode = t.jsxElement( + t.jsxOpeningElement(t.jsxIdentifier(localName), path.node.openingElement.attributes), + t.jsxClosingElement(t.jsxIdentifier(localName)), + path.node.children + ); + path.replaceWith(newNode); + } + } +} + +/** + * If no parent collection detected, leave a comment for the user to check manually. + * + * Example: If they're declaring declaring Items somewhere above the collection. + */ +export function commentIfParentCollectionNotDetected( + path: NodePath +) { + const collectionItemParents = new Set(['Menu', 'ActionMenu', 'TagGroup', 'Breadcrumbs', 'Picker', 'ComboBox', 'ListBox', 'TabList', 'TabPanels', 'ActionGroup', 'ActionButtonGroup', 'ToggleButtonGroup', 'ListBox', 'ListView', 'Collection', 'SearchAutocomplete', 'Accordion', 'ActionBar', 'StepList']); + if ( + t.isJSXElement(path.node) + ) { + // Find closest parent collection component + let closestParentCollection = path.findParent((p) => + t.isJSXElement(p.node) && + t.isJSXIdentifier(p.node.openingElement.name) && + collectionItemParents.has(getName(path, p.node.openingElement.name)) + ); + if (!closestParentCollection) { + // If we couldn't find a parent collection parent, leave a comment for them to check manually + addComment(path.node, ' TODO(S2-upgrade): Couldn\'t automatically detect what type of collection component this is rendered in. You\'ll need to update this manually.'); + } + } +} + +/** + * If within a component, moves prop to new child component. + * + * Example: + * - Section: If within `Menu`, move `title` prop string to be a child of new `Heading` within a `Header`. + */ +export function movePropToNewChildComponent( + path: NodePath, + options: { + parentComponent: string, + childComponent: string, + propToMove: string, + newChildComponent: string + } +) { + const {parentComponent, childComponent, propToMove, newChildComponent} = + options; + + if ( + t.isJSXElement(path.node) && + t.isJSXElement(path.parentPath.node) && + t.isJSXIdentifier(path.node.openingElement.name) && + t.isJSXIdentifier(path.parentPath.node.openingElement.name) && + getName(path, path.node.openingElement.name) === childComponent && + getName(path, path.parentPath.node.openingElement.name) === parentComponent + ) { + let propValue: t.JSXAttribute['value'] | void; + path.node.openingElement.attributes = + path.node.openingElement.attributes.filter((attr) => { + if (t.isJSXAttribute(attr) && attr.name.name === propToMove) { + propValue = attr.value; + return false; + } + return true; + }); + + if (propValue) { + path.node.children.unshift( + t.jsxElement( + t.jsxOpeningElement(t.jsxIdentifier(newChildComponent), []), + t.jsxClosingElement(t.jsxIdentifier(newChildComponent)), + [t.isStringLiteral(propValue) ? t.jsxText(propValue.value) : propValue] + ) + ); + // TODO: handle dynamic collections. Need to wrap export function child in and move `items` prop down. + } + } +} + +/** + * Updates prop to be on parent component. + * + * Example: + * - Tooltip: Remove `placement` and add to the parent `TooltipTrigger` instead. + */ +export function movePropToParentComponent( + path: NodePath, + options: { + parentComponent: string, + childComponent: string, + propToMove: string + } +) { + const {parentComponent, childComponent, propToMove} = options; + + path.traverse({ + JSXAttribute(attributePath) { + if ( + t.isJSXElement(path.parentPath.node) && + t.isJSXIdentifier(path.node.openingElement.name) && + t.isJSXIdentifier(path.parentPath.node.openingElement.name) && + attributePath.node.name.name === propToMove && + getName(path, path.node.openingElement.name) === childComponent && + getName(path, path.parentPath.node.openingElement.name) === parentComponent + ) { + path.parentPath.node.openingElement.attributes.push( + t.jsxAttribute(t.jsxIdentifier(propToMove), attributePath.node.value) + ); + attributePath.remove(); + } + } + }); +} + +/** + * Update to use a new component. + * + * Example: + * - Flex: Update `Flex` to be a `div` and apply flex styles using the style macro. + */ +export function updateToNewComponent( + path: NodePath, + options: { + newComponent: string + } +) { + const {newComponent} = options; + + let localName = newComponent; + if (availableComponents.has(newComponent)) { + let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; + localName = addComponentImport(program, newComponent); + } + + path.node.openingElement.name = t.jsxIdentifier(localName); + if (path.node.closingElement) { + path.node.closingElement.name = t.jsxIdentifier(localName); + } +} + +const conversions = { + 'cm': 37.8, + 'mm': 3.78, + 'in': 96, + 'pt': 1.33, + 'pc': 16 +}; + +/** + * Updates prop to use pixel value instead. + * + * Example: + * - ComboBox: Update `menuWidth` to a pixel value. + */ +export function convertDimensionValueToPx( + path: NodePath, + options: { + propToConvertValue: string + } +) { + const {propToConvertValue} = options; + + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToConvertValue) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToConvertValue) { + if (t.isStringLiteral(attrPath.node.value)) { + try { + let value = convertDimension(attrPath.node.value.value, 'size'); + if (value && typeof value === 'number') { + attrPath.node.value = t.jsxExpressionContainer(t.numericLiteral(value)); + } else if (value && typeof value === 'string') { + // eslint-disable-next-line max-depth + if ((/%|vw|vh|auto|ex|ch|rem|vmin|vmax/).test(value)) { + addComment(attrPath.node, ' TODO(S2-upgrade): Unable to convert CSS unit to a pixel value'); + } else if ((/cm|mm|in|pt|pc/).test(value)) { + let unit = value.replace(/\[|\]|\d+/g, ''); + let conversion = conversions[unit as keyof typeof conversions]; + value = Number(value.replace(/\[|\]|cm|mm|in|pt|pc/g, '')); + // eslint-disable-next-line max-depth + if (!isNaN(value)) { + let pixelValue = Math.round(conversion * value); + attrPath.node.value = t.jsxExpressionContainer(t.numericLiteral(pixelValue)); + } + } else if ((/px/).test(value)) { + let pixelValue = Number(value.replace(/\[|\]|px/g, '')); + // eslint-disable-next-line max-depth + if (!isNaN(pixelValue)) { + attrPath.node.value = t.jsxExpressionContainer(t.numericLiteral(pixelValue)); + } + } + } + } catch (error) { + addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToConvertValue} could not be automatically updated due to error: ${error}`); + } + } else if (t.isJSXExpressionContainer(attrPath.node.value)) { + if (t.isIdentifier(attrPath.node.value.expression)) { + addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToConvertValue} could not be automatically updated because ${attrPath.node.value.expression.name} could not be followed.`); + } + } + } +} + +/** + * Updates double placement values to a single value. + * + * Example: + * - TooltipTrigger: Update `placement="bottom left"` to `placement="bottom"`. + */ +export function updatePlacementToSingleValue( + path: NodePath, + options: { + propToUpdate: string, + /* If provided, updates the prop on the specified child component */ + childComponent?: string + } +) { + const {propToUpdate, childComponent} = options; + + const doublePlacementValues = new Set([ + 'bottom left', + 'bottom right', + 'bottom start', + 'bottom end', + 'top left', + 'top right', + 'top start', + 'top end', + 'left top', + 'left bottom', + 'start top', + 'start bottom', + 'right top', + 'right bottom', + 'end top', + 'end bottom' + ]); + + let elementPath = childComponent ? + path.get('children').find( + (child) => t.isJSXElement(child.node) && + t.isJSXIdentifier(child.node.openingElement.name) && + getName(path, child.node.openingElement.name) === childComponent + ) as NodePath : path; + let attrPath = elementPath.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToUpdate) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToUpdate) { + if (t.isStringLiteral(attrPath.node.value) && doublePlacementValues.has(attrPath.node.value.value)) { + attrPath.replaceWith(t.jsxAttribute(t.jsxIdentifier(propToUpdate), t.stringLiteral(attrPath.node.value.value.split(' ')[0]))); + return; + } else if (t.isJSXExpressionContainer(attrPath.node.value)) { + attrPath.traverse({ + StringLiteral(stringPath) { + if ( + t.isStringLiteral(stringPath.node) && + doublePlacementValues.has(stringPath.node.value) + ) { + stringPath.replaceWith(t.stringLiteral(stringPath.node.value.split(' ')[0])); + return; + } + } + }); + } + } +} + +/** + * Remove component if within a parent component. + * + * Example: + * - Divider: Remove `Divider` if used within a `Dialog`. + */ +export function removeComponentIfWithinParent( + path: NodePath, + options: { + parentComponent: string + } +) { + const {parentComponent} = options; + if ( + t.isJSXElement(path.node) && + t.isJSXElement(path.parentPath.node) && + t.isJSXIdentifier(path.node.openingElement.name) && + t.isJSXIdentifier(path.parentPath.node.openingElement.name) && + getName(path, path.parentPath.node.openingElement.name) === parentComponent + ) { + path.remove(); + } +} + +/** + * Updates the key prop to id. Keeps the key prop if it's used in an array.map function. + */ +export function updateKeyToId( + path: NodePath +) { + let attributes = path.node.openingElement.attributes; + let keyProp = attributes.find((attr) => t.isJSXAttribute(attr) && attr.name.name === 'key'); + if ( + keyProp && + t.isJSXAttribute(keyProp) + ) { + // Update key prop to be id + keyProp.name = t.jsxIdentifier('id'); + } + + if ( + t.isArrowFunctionExpression(path.parentPath.node) && + path.parentPath.parentPath && + t.isCallExpression(path.parentPath.parentPath.node) && + path.parentPath.parentPath.node.callee.type === 'MemberExpression' && + path.parentPath.parentPath.node.callee.property.type === 'Identifier' && + path.parentPath.parentPath.node.callee.property.name === 'map' + ) { + // If Array.map is used, keep the key prop + if ( + keyProp && + t.isJSXAttribute(keyProp) + ) { + let newKeyProp = t.jsxAttribute(t.jsxIdentifier('key'), keyProp.value); + attributes.push(newKeyProp); + } + } +} diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/unsafeStyle.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/unsafeStyle.ts similarity index 99% rename from packages/dev/codemods/src/s1-to-s2/src/codemods/unsafeStyle.ts rename to packages/dev/codemods/src/s1-to-s2/src/codemods/shared/unsafeStyle.ts index 83202fa37a2..8be657d42b0 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/unsafeStyle.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/unsafeStyle.ts @@ -4,7 +4,7 @@ import {convertUnsafeDimension} from './dimensions'; import {convertUnsafeStyleColor} from './colors'; import * as t from '@babel/types'; -export function transformUnsafeStyle( +export default function transformUnsafeStyle( value: t.ObjectExpression, element: string ): StylePropValue | null { diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/utils.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts similarity index 100% rename from packages/dev/codemods/src/s1-to-s2/src/codemods/utils.ts rename to packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/transforms.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/transforms.ts deleted file mode 100644 index a8cd465bb0a..00000000000 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/transforms.ts +++ /dev/null @@ -1,1446 +0,0 @@ -import {addComment, addComponentImport, getName, removeComponentImport} from './utils'; -import {convertDimension} from './dimensions'; -import {getComponents} from '../getComponents'; -import {NodePath} from '@babel/traverse'; -import type {ReactNode} from 'react'; -import * as t from '@babel/types'; - -let availableComponents = getComponents(); - -export interface UpdatePropNameAndValueOptions { - /** Prop name to replace. */ - oldProp: string, - /** Prop value to replace. */ - oldValue: ReactNode, - /** Updated prop name. */ - newProp: string, - /** Updated prop value. */ - newValue: ReactNode -} - -/** - * Update prop name and value to new prop name and value. - * - * Example: - * - Button: Change variant="cta" to variant="accent". - * - Link: Change `variant="overBackground"` to `staticColor="white"`. - */ -function updatePropNameAndValue( - path: NodePath, - options: UpdatePropNameAndValueOptions -) { - const {oldProp, oldValue, newProp, newValue} = options; - - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === oldProp) { - if ( - t.isStringLiteral(attrPath.node.value) && - attrPath.node.value.value === oldValue - ) { - // Update old prop name to new prop name - attrPath.node.name.name = newProp; - - // If prop value is a string and matches the old value, replace it with the new value - if (typeof newValue === 'string') { - attrPath.node.value = t.stringLiteral(newValue); - } else if (typeof newValue === 'boolean') { - if (!newValue) { - attrPath.node.value = t.jsxExpressionContainer(t.booleanLiteral(newValue)); - } else { - attrPath.node.value = null; - } - } - } else if (t.isJSXExpressionContainer(attrPath.node.value)) { - if (t.isIdentifier(attrPath.node.value.expression)) { - // @ts-ignore - if (attrPath.node.comments && [...attrPath.node.comments].some((comment) => comment.value.includes('could not be automatically'))) { - return; - } - addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${oldProp} could not be automatically updated because ${attrPath.node.value.expression.name} could not be followed.`); - } else { - // If prop value is an expression, traverse to find a string literal that matches the old and replace it with the new value - attrPath.traverse({ - StringLiteral(stringPath) { - if ( - t.isStringLiteral(stringPath.node) && - stringPath.node.value === oldValue - ) { - // Update old prop name to new prop name - attrPath.node.name.name = newProp; - - if (typeof newValue === 'string') { - stringPath.replaceWith(t.stringLiteral(newValue)); - } else if (typeof newValue === 'boolean') { - if (!newValue) { - stringPath.replaceWith(t.booleanLiteral(newValue)); - } else { - attrPath.node.value = null; - } - } - } - } - }); - } - } - } -} - -export interface UpdatePropValueAndAddNewPropOptions { - /** Prop name to replace. */ - oldProp: string, - /** Prop value to replace. */ - oldValue: ReactNode, - /** Updated prop name. */ - newProp: string, - /** Updated prop value. */ - newValue: ReactNode, - /** Additional new prop name to add. */ - additionalProp: string, - /** Additional new prop value to use. */ - additionalValue: string -} - -/** - * Updates a prop name and value to new prop name and value, and adds an additional prop. - * - * Example: - * - Button: Change `variant="overBackground"` to `variant="primary" staticColor="white"`. - */ -function updatePropValueAndAddNewProp( - path: NodePath, - options: UpdatePropValueAndAddNewPropOptions -) { - const { - oldProp, - oldValue, - newProp, - newValue, - additionalProp, - additionalValue - } = options; - - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; - if (attrPath && t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === oldValue) { - // Update old prop name to new prop name - attrPath.node.name.name = newProp; - - // If prop value is a string and matches the old value, replace it with the new value - if (typeof newValue === 'string') { - attrPath.node.value = t.stringLiteral(newValue); - } else if (typeof newValue === 'boolean') { - attrPath.node.value = t.jsxExpressionContainer(t.booleanLiteral(newValue)); - } - - if (additionalProp && additionalValue) { - attrPath.insertAfter( - t.jsxAttribute(t.jsxIdentifier(additionalProp), t.stringLiteral(additionalValue as string)) - ); - } - } else if (attrPath && t.isJSXExpressionContainer(attrPath.node.value)) { - // If prop value is an expression, traverse to find a string literal that matches the old and replace it with the new value - attrPath.traverse({ - StringLiteral(stringPath) { - if ( - t.isStringLiteral(stringPath.node) && - stringPath.node.value === oldValue - ) { - // Update old prop name to new prop name - attrPath.node.name.name = newProp; - - if (typeof newValue === 'string') { - stringPath.replaceWith(t.stringLiteral(newValue)); - } else if (typeof newValue === 'boolean') { - stringPath.replaceWith(t.booleanLiteral(newValue)); - } - - if (additionalProp && additionalValue) { - attrPath.insertAfter( - t.jsxAttribute(t.jsxIdentifier(additionalProp), t.stringLiteral(additionalValue as string)) - ); - } - } - } - }); - } -} - -export interface UpdatePropNameOptions { - /** Prop name to replace. */ - oldProp: string, - /** Updated prop name. */ - newProp: string -} - -/** - * Updates a prop name to new prop name. - * - * Example: - * - Button: Change style to fillStyle. - */ -function updatePropName( - path: NodePath, - options: UpdatePropNameOptions -) { - const {oldProp, newProp} = options; - - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === oldProp) { - attrPath.node.name.name = newProp; - } -} - -export interface RemovePropOptions { - /** Prop name to remove. */ - propToRemove: string, - /** If provided, prop will only be removed if set to this value. */ - propValue?: string -} - -/** - * Removes a prop. - * - * Example: - * - Button: Remove isQuiet (it is no longer supported). - */ -function removeProp(path: NodePath, options: RemovePropOptions) { - const {propToRemove, propValue} = options; - - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToRemove) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToRemove) { - if (propValue) { - // If prop value is provided, remove prop only if it matches the value - if (t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === propValue) { - attrPath.remove(); - } else if ( - t.isJSXExpressionContainer(attrPath.node.value) - ) { - if (t.isIdentifier(attrPath.node.value.expression)) { - // @ts-ignore - // eslint-disable-next-line max-depth - if (attrPath.node.comments && [...attrPath.node.comments].some((comment) => comment.value.includes('could not be automatically'))) { - return; - } - addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToRemove} could not be automatically removed because ${attrPath.node.value.expression.name} could not be followed.`); - } else { - attrPath.traverse({ - StringLiteral(stringPath) { - if ( - t.isStringLiteral(stringPath.node) && - stringPath.node.value === propValue - ) { - // Invalid prop value was found inside expression. - addComment(attrPath.node, ` TODO(S2-upgrade): ${propToRemove}="${propValue}" is no longer supported. You'll need to update this manually.`); - } - } - }); - } - } - } else { - // No prop value provided, so remove prop regardless of value - attrPath.remove(); - } - } -} - -export interface CommentOutPropOptions { - /** Prop to comment out. */ - propToComment: string, - /** If provided, prop will only be commented out if set to this value. */ - propValue?: string -} - -/** - * Comments out a prop. - * - * Example: - * - Button: Comment out isPending (it has not been implemented yet). - */ -function commentOutProp( - path: NodePath, - options: CommentOutPropOptions -) { - const {propToComment, propValue} = options; - - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToComment) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToComment) { - if (propValue) { - // If prop value is provided, comment out prop only if it matches the value - if (t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === propValue) { - addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment}="${propValue}" has not been implemented yet.`); - attrPath.remove(); - } else { - attrPath.traverse({ - StringLiteral(stringPath) { - if ( - t.isStringLiteral(stringPath.node) && - stringPath.node.value === propValue - ) { - addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment}="${propValue}" has not been implemented yet.`); - attrPath.remove(); - } - } - }); - } - } else { - addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment} has not been implemented yet.`); - attrPath.remove(); - } - } -} - -export interface AddCommentToElementOptions { - /** Comment to leave. */ - comment: string -} - -/** - * Add a comment above an element. - * - * Example: - * - Breadcrumbs: Check if nav needs to be added around Bre. - */ -function addCommentToElement( - path: NodePath, - options: AddCommentToElementOptions -) { - const {comment} = options; - addComment(path.node, ` TODO(S2-upgrade): ${comment}`); -} - -export interface UpdateComponentIfPropPresentOptions { - /** Updated component to use. */ - newComponent: string, - /** Will update component if this prop is present. */ - propToCheck: string -} - -/** - * If a prop is present, updates to use a new component. - * - * Example: - * - Button: If `href` is present, Button should be converted to `LinkButton`. - */ -function updateComponentIfPropPresent( - path: NodePath, - options: UpdateComponentIfPropPresentOptions -) { - const {newComponent, propToCheck} = options; - - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToCheck) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToCheck) { - let node = attrPath.findParent((p) => t.isJSXElement(p.node))?.node; - if (node && t.isJSXElement(node)) { - let localName = newComponent; - if (availableComponents.has(newComponent)) { - let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); - } - node.openingElement.name = t.jsxIdentifier(localName); - if (node.closingElement) { - node.closingElement.name = t.jsxIdentifier(localName); - } - } - } -} - -export interface MoveRenderPropsOptions { - newChildComponent: string -} - -/** - * Remove render props, and move usage to a child component. - * - * Example: - * - DialogTrigger: Update children to remove render props usage, and note that `close` function moved from `DialogTrigger` to `Dialog`. - */ -function moveRenderPropsToChild( - path: NodePath, - options: MoveRenderPropsOptions -) { - const {newChildComponent} = options; - - const renderFunctionIndex = path.node.children.findIndex( - (child) => - t.isJSXExpressionContainer(child) && - t.isArrowFunctionExpression(child.expression) && - t.isJSXElement(child.expression.body) && - t.isJSXIdentifier(child.expression.body.openingElement.name)); - - const renderFunction = path.node.children[renderFunctionIndex] as t.JSXExpressionContainer; - - if ( - t.isJSXExpressionContainer(renderFunction) && - t.isArrowFunctionExpression(renderFunction.expression) && - t.isJSXElement(renderFunction.expression.body) && - t.isJSXIdentifier(renderFunction.expression.body.openingElement.name) && - getName(path, renderFunction.expression.body.openingElement.name) !== newChildComponent - ) { - addComment(renderFunction, ' TODO(S2-upgrade): update this dialog to move the close function inside'); - return; - } - - if ( - renderFunction && - t.isArrowFunctionExpression(renderFunction.expression) && - t.isJSXElement(renderFunction.expression.body) - ) { - const dialogElement = renderFunction.expression.body; - - const originalParam = renderFunction.expression.params[0]; - if (!t.isIdentifier(originalParam)) { - addComment(path.node.children[renderFunctionIndex], ' TODO(S2-upgrade): Could not automatically move the render props. You\'ll need to update this manually.'); - return; - } - const paramName = originalParam.name; - const objectPattern = t.objectPattern([ - t.objectProperty(t.identifier(paramName), - t.identifier(paramName), - false, - true - ) - ]); - - const newRenderFunction = t.jsxExpressionContainer( - t.arrowFunctionExpression( - [objectPattern], - t.jsxFragment( - t.jsxOpeningFragment(), - t.jsxClosingFragment(), - dialogElement.children - ) - ) - ); - - let removedOnDismiss = false; - const attributes = dialogElement.openingElement.attributes.filter((attr) => { - if (t.isJSXAttribute(attr) && attr.name.name === 'onDismiss') { - removedOnDismiss = true; - return false; - } - return true; - }); - - path.node.children[renderFunctionIndex] = t.jsxElement( - t.jsxOpeningElement(t.jsxIdentifier(newChildComponent), attributes), - t.jsxClosingElement(t.jsxIdentifier(newChildComponent)), - [newRenderFunction] - ); - - if (removedOnDismiss) { - addComment(path.node.children[renderFunctionIndex], ' onDismiss was removed from Dialog. Use onOpenChange on the DialogTrigger, or onDismiss on the DialogContainer instead'); - } - } -} - -export interface UpdateComponentWithinCollectionOptions { - parentComponent: string, - newComponent: string -} - -/** - * If within a collection component, updates to use a new component. - * - * Example: - * - Item: If within `Menu`, update name from `Item` to `MenuItem`. - */ -function updateComponentWithinCollection( - path: NodePath, - options: UpdateComponentWithinCollectionOptions -) { - const {parentComponent, newComponent} = options; - - // Collections currently implemented - // TODO: Add 'ActionGroup', 'ListBox', 'ListView' once implemented - const collectionItemParents = new Set(['Menu', 'ActionMenu', 'TagGroup', 'Breadcrumbs', 'Picker', 'ComboBox', 'ListBox', 'TabList', 'TabPanels', 'Collection']); - - if ( - t.isJSXElement(path.node) && - t.isJSXIdentifier(path.node.openingElement.name) - ) { - // Find closest parent collection component - let closestParentCollection = path.findParent((p) => - t.isJSXElement(p.node) && - t.isJSXIdentifier(p.node.openingElement.name) && - collectionItemParents.has(getName(path, p.node.openingElement.name)) - ); - if ( - closestParentCollection && - t.isJSXElement(closestParentCollection.node) && - t.isJSXIdentifier(closestParentCollection.node.openingElement.name) && - getName(path, closestParentCollection.node.openingElement.name) === parentComponent - ) { - // If closest parent collection component matches parentComponent, replace with newComponent - - updateKeyToId(path); - - let localName = newComponent; - if (availableComponents.has(newComponent)) { - let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); - } - - let newNode = t.jsxElement( - t.jsxOpeningElement(t.jsxIdentifier(localName), path.node.openingElement.attributes), - t.jsxClosingElement(t.jsxIdentifier(localName)), - path.node.children - ); - path.replaceWith(newNode); - } - } -} - -/** - * If no parent collection detected, leave a comment for the user to check manually. - * - * Example: If they're declaring declaring Items somewhere above the collection. - */ -function commentIfParentCollectionNotDetected( - path: NodePath -) { - const collectionItemParents = new Set(['Menu', 'ActionMenu', 'TagGroup', 'Breadcrumbs', 'Picker', 'ComboBox', 'ListBox', 'TabList', 'TabPanels', 'ActionGroup', 'ActionButtonGroup', 'ToggleButtonGroup', 'ListBox', 'ListView', 'Collection', 'SearchAutocomplete', 'Accordion', 'ActionBar', 'StepList']); - if ( - t.isJSXElement(path.node) - ) { - // Find closest parent collection component - let closestParentCollection = path.findParent((p) => - t.isJSXElement(p.node) && - t.isJSXIdentifier(p.node.openingElement.name) && - collectionItemParents.has(getName(path, p.node.openingElement.name)) - ); - if (!closestParentCollection) { - // If we couldn't find a parent collection parent, leave a comment for them to check manually - addComment(path.node, ' TODO(S2-upgrade): Couldn\'t automatically detect what type of collection component this is rendered in. You\'ll need to update this manually.'); - } - } -} - -/** - * Updates Tabs to the new API. - * - * Example: - * - Tabs: Remove TabPanels components and keep individual TabPanel components inside. - */ -function updateTabs( - path: NodePath -) { - function transformTabs(path: NodePath) { - let tabListNode: t.JSXElement | null = null; - let tabPanelsNodes: t.JSXElement[] = []; - let itemsProp: t.JSXAttribute | null = null; - - path.node.openingElement.attributes = path.node.openingElement.attributes.filter(attr => { - if (t.isJSXAttribute(attr) && attr.name.name === 'items') { - itemsProp = attr; - return false; - } - return true; - }); - - path.get('children').forEach(childPath => { - if (t.isJSXElement(childPath.node)) { - if ( - t.isJSXIdentifier(childPath.node.openingElement.name) && - getName(childPath as NodePath, childPath.node.openingElement.name) === 'TabList' - ) { - tabListNode = transformTabList(childPath as NodePath); - if (itemsProp) { - tabListNode.openingElement.attributes.push(itemsProp); - } - } else if ( - t.isJSXIdentifier(childPath.node.openingElement.name) && - getName(childPath as NodePath, childPath.node.openingElement.name) === 'TabPanels' - ) { - tabPanelsNodes = transformTabPanels(childPath as NodePath, itemsProp); - } - } - }); - - if (tabListNode) { - path.node.children = [tabListNode, ...tabPanelsNodes]; - } - } - - function transformTabList(tabListPath: NodePath): t.JSXElement { - tabListPath.get('children').forEach(itemPath => { - if ( - t.isJSXElement(itemPath.node) && - t.isJSXIdentifier(itemPath.node.openingElement.name) && - getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' - ) { - updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'TabList', newComponent: 'Tab'}); - } - }); - return tabListPath.node; - } - - function transformTabPanels(tabPanelsPath: NodePath, itemsProp: t.JSXAttribute | null): t.JSXElement[] { - // Dynamic case - let dynamicRender = tabPanelsPath.get('children').find(path => t.isJSXExpressionContainer(path.node)); - if (dynamicRender) { - updateToNewComponent(tabPanelsPath, {newComponent: 'Collection'}); - let itemPath = (dynamicRender.get('expression') as NodePath).get('body'); - updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'Collection', newComponent: 'TabPanel'}); - if (itemsProp) { - tabPanelsPath.node.openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('items'), itemsProp.value)); - } - return [tabPanelsPath.node]; - } - - // Static case - return tabPanelsPath.get('children').map(itemPath => { - if ( - t.isJSXElement(itemPath.node) && - t.isJSXIdentifier(itemPath.node.openingElement.name) && - getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' - ) { - updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'TabPanels', newComponent: 'TabPanel'}); - return itemPath.node; - } - return null; - }).filter(Boolean) as t.JSXElement[]; - } - - let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - removeComponentImport(program, 'TabPanels'); - transformTabs(path); -} - -export interface MovePropToNewChildComponentOptions { - parentComponent: string, - childComponent: string, - propToMove: string, - newChildComponent: string -} - -/** - * If within a component, moves prop to new child component. - * - * Example: - * - Section: If within `Menu`, move `title` prop string to be a child of new `Heading` within a `Header`. - */ -function movePropToNewChildComponent( - path: NodePath, - options: MovePropToNewChildComponentOptions -) { - const {parentComponent, childComponent, propToMove, newChildComponent} = - options; - - if ( - t.isJSXElement(path.node) && - t.isJSXElement(path.parentPath.node) && - t.isJSXIdentifier(path.node.openingElement.name) && - t.isJSXIdentifier(path.parentPath.node.openingElement.name) && - getName(path, path.node.openingElement.name) === childComponent && - getName(path, path.parentPath.node.openingElement.name) === parentComponent - ) { - let propValue: t.JSXAttribute['value'] | void; - path.node.openingElement.attributes = - path.node.openingElement.attributes.filter((attr) => { - if (t.isJSXAttribute(attr) && attr.name.name === propToMove) { - propValue = attr.value; - return false; - } - return true; - }); - - if (propValue) { - path.node.children.unshift( - t.jsxElement( - t.jsxOpeningElement(t.jsxIdentifier(newChildComponent), []), - t.jsxClosingElement(t.jsxIdentifier(newChildComponent)), - [t.isStringLiteral(propValue) ? t.jsxText(propValue.value) : propValue] - ) - ); - // TODO: handle dynamic collections. Need to wrap function child in and move `items` prop down. - } - } -} - -export interface MovePropToParentComponentOptions { - parentComponent: string, - childComponent: string, - propToMove: string -} - -/** - * Updates prop to be on parent component. - * - * Example: - * - Tooltip: Remove `placement` and add to the parent `TooltipTrigger` instead. - */ -function movePropToParentComponent( - path: NodePath, - options: MovePropToParentComponentOptions -) { - const {parentComponent, childComponent, propToMove} = options; - - path.traverse({ - JSXAttribute(attributePath) { - if ( - t.isJSXElement(path.parentPath.node) && - t.isJSXIdentifier(path.node.openingElement.name) && - t.isJSXIdentifier(path.parentPath.node.openingElement.name) && - attributePath.node.name.name === propToMove && - getName(path, path.node.openingElement.name) === childComponent && - getName(path, path.parentPath.node.openingElement.name) === parentComponent - ) { - path.parentPath.node.openingElement.attributes.push( - t.jsxAttribute(t.jsxIdentifier(propToMove), attributePath.node.value) - ); - attributePath.remove(); - } - } - }); -} - -export interface UpdateToNewComponentOptions { - newComponent: string -} - -/** - * Update to use a new component. - * - * Example: - * - Flex: Update `Flex` to be a `div` and apply flex styles using the style macro. - */ -function updateToNewComponent( - path: NodePath, - options: UpdateToNewComponentOptions -) { - const {newComponent} = options; - - let localName = newComponent; - if (availableComponents.has(newComponent)) { - let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); - } - - path.node.openingElement.name = t.jsxIdentifier(localName); - if (path.node.closingElement) { - path.node.closingElement.name = t.jsxIdentifier(localName); - } -} - -const conversions = { - 'cm': 37.8, - 'mm': 3.78, - 'in': 96, - 'pt': 1.33, - 'pc': 16 -}; - -export interface ConvertDimensionValueToPxOptions { - propToConvertValue: string -} - -/** - * Updates prop to use pixel value instead. - * - * Example: - * - ComboBox: Update `menuWidth` to a pixel value. - */ -function convertDimensionValueToPx( - path: NodePath, - options: ConvertDimensionValueToPxOptions -) { - const {propToConvertValue} = options; - - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToConvertValue) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToConvertValue) { - if (t.isStringLiteral(attrPath.node.value)) { - try { - let value = convertDimension(attrPath.node.value.value, 'size'); - if (value && typeof value === 'number') { - attrPath.node.value = t.jsxExpressionContainer(t.numericLiteral(value)); - } else if (value && typeof value === 'string') { - // eslint-disable-next-line max-depth - if ((/%|vw|vh|auto|ex|ch|rem|vmin|vmax/).test(value)) { - addComment(attrPath.node, ' TODO(S2-upgrade): Unable to convert CSS unit to a pixel value'); - } else if ((/cm|mm|in|pt|pc/).test(value)) { - let unit = value.replace(/\[|\]|\d+/g, ''); - let conversion = conversions[unit as keyof typeof conversions]; - value = Number(value.replace(/\[|\]|cm|mm|in|pt|pc/g, '')); - // eslint-disable-next-line max-depth - if (!isNaN(value)) { - let pixelValue = Math.round(conversion * value); - attrPath.node.value = t.jsxExpressionContainer(t.numericLiteral(pixelValue)); - } - } else if ((/px/).test(value)) { - let pixelValue = Number(value.replace(/\[|\]|px/g, '')); - // eslint-disable-next-line max-depth - if (!isNaN(pixelValue)) { - attrPath.node.value = t.jsxExpressionContainer(t.numericLiteral(pixelValue)); - } - } - } - } catch (error) { - addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToConvertValue} could not be automatically updated due to error: ${error}`); - } - } else if (t.isJSXExpressionContainer(attrPath.node.value)) { - if (t.isIdentifier(attrPath.node.value.expression)) { - addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToConvertValue} could not be automatically updated because ${attrPath.node.value.expression.name} could not be followed.`); - } - } - } -} - -export interface UpdatePlacementToSingleValueProps { - propToUpdate: string, - /* If provided, updates the prop on the specified child component */ - childComponent?: string -} - -/** - * Updates double placement values to a single value. - * - * Example: - * - TooltipTrigger: Update `placement="bottom left"` to `placement="bottom"`. - */ -function updatePlacementToSingleValue( - path: NodePath, - options: UpdatePlacementToSingleValueProps -) { - const {propToUpdate, childComponent} = options; - - const doublePlacementValues = new Set([ - 'bottom left', - 'bottom right', - 'bottom start', - 'bottom end', - 'top left', - 'top right', - 'top start', - 'top end', - 'left top', - 'left bottom', - 'start top', - 'start bottom', - 'right top', - 'right bottom', - 'end top', - 'end bottom' - ]); - - let elementPath = childComponent ? - path.get('children').find( - (child) => t.isJSXElement(child.node) && - t.isJSXIdentifier(child.node.openingElement.name) && - getName(path, child.node.openingElement.name) === childComponent - ) as NodePath : path; - let attrPath = elementPath.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToUpdate) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToUpdate) { - if (t.isStringLiteral(attrPath.node.value) && doublePlacementValues.has(attrPath.node.value.value)) { - attrPath.replaceWith(t.jsxAttribute(t.jsxIdentifier(propToUpdate), t.stringLiteral(attrPath.node.value.value.split(' ')[0]))); - return; - } else if (t.isJSXExpressionContainer(attrPath.node.value)) { - attrPath.traverse({ - StringLiteral(stringPath) { - if ( - t.isStringLiteral(stringPath.node) && - doublePlacementValues.has(stringPath.node.value) - ) { - stringPath.replaceWith(t.stringLiteral(stringPath.node.value.split(' ')[0])); - return; - } - } - }); - } - } -} - -export interface RemoveComponentIfWithinParentOptions { - parentComponent: string -} - -/** - * Remove component if within a parent component. - * - * Example: - * - Divider: Remove `Divider` if used within a `Dialog`. - */ -function removeComponentIfWithinParent( - path: NodePath, - options: RemoveComponentIfWithinParentOptions -) { - const {parentComponent} = options; - if ( - t.isJSXElement(path.node) && - t.isJSXElement(path.parentPath.node) && - t.isJSXIdentifier(path.node.openingElement.name) && - t.isJSXIdentifier(path.parentPath.node.openingElement.name) && - getName(path, path.parentPath.node.openingElement.name) === parentComponent - ) { - path.remove(); - } -} - -function updateAvatarSize( - path: NodePath -) { - if ( - t.isJSXElement(path.node) && - t.isJSXIdentifier(path.node.openingElement.name) && - getName(path, path.node.openingElement.name) === 'Avatar' - ) { - let sizeAttrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'size') as NodePath; - if (sizeAttrPath) { - let value = sizeAttrPath.node.value; - if (value?.type === 'StringLiteral') { - const avatarDimensions = { - 'avatar-size-50': 16, - 'avatar-size-75': 18, - 'avatar-size-100': 20, - 'avatar-size-200': 22, - 'avatar-size-300': 26, - 'avatar-size-400': 28, - 'avatar-size-500': 32, - 'avatar-size-600': 36, - 'avatar-size-700': 40 - }; - let val = avatarDimensions[value.value as keyof typeof avatarDimensions]; - if (val != null) { - sizeAttrPath.node.value = t.jsxExpressionContainer(t.numericLiteral(val)); - } - } - } - } -} - -/** - * Handles the legacy `Link` API where an `a` tag or custom router component could be used within a `Link` component. - * Removes the inner component and moves its attributes to the `Link` component. - */ -function updateLegacyLink( - path: NodePath -) { - let missingOuterHref = t.isJSXElement(path.node) && !path.node.openingElement.attributes.some((attr) => t.isJSXAttribute(attr) && attr.name.name === 'href'); - if (missingOuterHref) { - let innerLink = path.node.children.find((child) => t.isJSXElement(child) && t.isJSXIdentifier(child.openingElement.name)); - if (innerLink && t.isJSXElement(innerLink)) { - let innerAttributes = innerLink.openingElement.attributes; - let outerAttributes = path.node.openingElement.attributes; - innerAttributes.forEach((attr) => { - outerAttributes.push(attr); - }); - - if ( - t.isJSXIdentifier(innerLink.openingElement.name) && - innerLink.openingElement.name.name !== 'a' - ) { - addComment(path.node, ' TODO(S2-upgrade): You may have been using a custom link component here. You\'ll need to update this manually.'); - } - path.node.children = innerLink.children; - } - } -} - -/** - * Copies the columns prop from the TableHeader to the Row component. - */ -function addColumnsPropToRow( - path: NodePath -) { - const tableHeaderPath = path.get('children').find((child) => - t.isJSXElement(child.node) && - t.isJSXIdentifier(child.node.openingElement.name) && - getName(child as NodePath, child.node.openingElement.name) === 'TableHeader' - ) as NodePath | undefined; - - if (!tableHeaderPath) { - addComment(path.node, ' TODO(S2-upgrade): Could not find TableHeader within Table to retrieve columns prop.'); - return; - } - - const columnsProp = tableHeaderPath - .get('openingElement') - .get('attributes') - .find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'columns') as NodePath | undefined; - - if (columnsProp) { - path.traverse({ - JSXElement(innerPath) { - if ( - t.isJSXElement(innerPath.node) && - t.isJSXIdentifier(innerPath.node.openingElement.name) && - getName(innerPath as NodePath, innerPath.node.openingElement.name) === 'Row' - ) { - let rowPath = innerPath as NodePath; - rowPath.node.openingElement.attributes.push(columnsProp.node); - - // If Row doesn't contain id prop, leave a comment for the user to check manually - let idProp = rowPath.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'id'); - if (!idProp) { - addComment(rowPath.node, ' TODO(S2-upgrade): If the items do not have id properties, you\'ll need to add an id prop to the Row.'); - } - } - } - }); - } -} - -/** - * Updates the function signature of the Row component. - */ -function updateRowFunctionArg( - path: NodePath -) { - // Find the function passed as a child - let functionChild = path.get('children').find(childPath => - childPath.isJSXExpressionContainer() && - childPath.get('expression').isArrowFunctionExpression() - ); - - let tablePath = path.findParent((p) => - t.isJSXElement(p.node) && - t.isJSXIdentifier(p.node.openingElement.name) && - getName(path, p.node.openingElement.name) === 'TableView' - ); - - let tableHeaderPath = tablePath?.get('children').find((child) => - t.isJSXElement(child.node) && - t.isJSXIdentifier(child.node.openingElement.name) && - getName(child as NodePath, child.node.openingElement.name) === 'TableHeader' - ) as NodePath | undefined; - - function findColumnKeyProp(path: NodePath) { - let columnKeyProp = 'id'; - path.traverse({ - JSXElement(columnPath) { - if ( - t.isArrowFunctionExpression(columnPath.parentPath.node) && - t.isJSXElement(columnPath.node) && - t.isJSXIdentifier(columnPath.node.openingElement.name) && - getName(columnPath as NodePath, columnPath.node.openingElement.name) === 'Column' - ) { - let openingElement = columnPath.get('openingElement'); - let keyPropPath = openingElement.get('attributes').find(attr => - t.isJSXAttribute(attr.node) && - (attr.node.name.name === 'key' || attr.node.name.name === 'id') - ); - keyPropPath?.traverse({ - Identifier(innerPath) { - if ( - innerPath.node.name === 'column' && - innerPath.parentPath.node.type === 'MemberExpression' && - t.isIdentifier(innerPath.parentPath.node.property) - ) { - columnKeyProp = innerPath.parentPath.node.property.name; - } - } - }); - } - } - }); - return columnKeyProp || 'id'; - } - - let columnKey = findColumnKeyProp(tableHeaderPath as NodePath); - - if (functionChild && functionChild.isJSXExpressionContainer()) { - let arrowFuncPath = functionChild.get('expression'); - if (arrowFuncPath.isArrowFunctionExpression()) { - let params = arrowFuncPath.node.params; - if (params.length === 1 && t.isIdentifier(params[0])) { - let paramName = params[0].name; - - // Rename parameter to 'column' - params[0].name = 'column'; - - // Replace references to the old parameter name inside the function body - arrowFuncPath.get('body').traverse({ - Identifier(innerPath) { - if ( - innerPath.node.name === paramName && - // Ensure we're not replacing the parameter declaration - innerPath.node !== params[0] - ) { - // Replace with column key - innerPath.replaceWith( - t.memberExpression( - t.identifier('column'), - t.identifier(columnKey ?? 'id') - ) - ); - } - } - }); - } - } - } -} - -/** - * Updates the key prop to id. Keeps the key prop if it's used in an array.map function. - */ -function updateKeyToId( - path: NodePath -) { - let attributes = path.node.openingElement.attributes; - let keyProp = attributes.find((attr) => t.isJSXAttribute(attr) && attr.name.name === 'key'); - if ( - keyProp && - t.isJSXAttribute(keyProp) - ) { - // Update key prop to be id - keyProp.name = t.jsxIdentifier('id'); - } - - if ( - t.isArrowFunctionExpression(path.parentPath.node) && - path.parentPath.parentPath && - t.isCallExpression(path.parentPath.parentPath.node) && - path.parentPath.parentPath.node.callee.type === 'MemberExpression' && - path.parentPath.parentPath.node.callee.property.type === 'Identifier' && - path.parentPath.parentPath.node.callee.property.name === 'map' - ) { - // If Array.map is used, keep the key prop - if ( - keyProp && - t.isJSXAttribute(keyProp) - ) { - let newKeyProp = t.jsxAttribute(t.jsxIdentifier('key'), keyProp.value); - attributes.push(newKeyProp); - } - } -} - -export function commentIfNestedColumns( - path: NodePath -) { - const headerPath = path.get('children').find((child) => - t.isJSXElement(child.node) && - t.isJSXIdentifier(child.node.openingElement.name) && - getName(child as NodePath, child.node.openingElement.name) === 'TableHeader' - ) as NodePath | undefined; - const columns = headerPath?.get('children') || []; - - let hasNestedColumns = false; - - columns.forEach(column => { - let columnChildren = column.get('children'); - if ( - columnChildren.find(child => - t.isJSXElement(child.node) && - t.isJSXIdentifier(child.node.openingElement.name) && - getName(child as NodePath, child.node.openingElement.name) === 'Column' - ) - ) { - hasNestedColumns = true; - } - }); - - if (hasNestedColumns) { - addComment(path.node, ' TODO(S2-upgrade): Nested Column components are not supported yet.'); - } -} - -/** - * Updates DialogTrigger and DialogContainer to the new API. - * - * Example: - * - When `type="popover"`, replaces Dialog with ``. - * - When `type="fullscreen"`, replaces Dialog with ``. - * - When `type="fullscreenTakeover"`, replaces Dialog with ``. - */ -function updateDialogChild( - path: NodePath -) { - let typePath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'type') as NodePath | undefined; - let type = typePath?.node.value?.type === 'StringLiteral' ? typePath.node.value?.value : 'modal'; - let newComponent = 'Dialog'; - let props: t.JSXAttribute[] = []; - if (type === 'popover') { - newComponent = 'Popover'; - } else if (type === 'fullscreen' || type === 'fullscreenTakeover') { - newComponent = 'FullscreenDialog'; - if (type === 'fullscreenTakeover') { - props.push(t.jsxAttribute(t.jsxIdentifier('variant'), t.stringLiteral(type))); - } - } - - for (let prop of ['isDismissible', 'mobileType', 'hideArrow', 'placement', 'shouldFlip', 'isKeyboardDismissDisabled', 'containerPadding', 'offset', 'crossOffset']) { - let attr = path.get('openingElement').get('attributes').find(attr => attr.isJSXAttribute() && attr.node.name.name === prop) as NodePath | undefined; - if (attr) { - props.push(attr.node); - attr.remove(); - } - } - - typePath?.remove(); - - let localName = newComponent; - if (newComponent !== 'Dialog' && availableComponents.has(newComponent)) { - let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); - } - - path.traverse({ - JSXElement(dialog) { - if (!t.isJSXIdentifier(dialog.node.openingElement.name) || getName(dialog, dialog.node.openingElement.name) !== 'Dialog') { - return; - } - - dialog.node.openingElement.name = t.jsxIdentifier(localName); - if (dialog.node.closingElement) { - dialog.node.closingElement.name = t.jsxIdentifier(localName); - } - - dialog.node.openingElement.attributes.push(...props); - } - }); -} - -function updateActionGroup( - path: NodePath -) { - let selectionModePath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'selectionMode') as NodePath | undefined; - let selectionMode = t.isStringLiteral(selectionModePath?.node.value) ? selectionModePath.node.value.value : 'none'; - let newComponent, childComponent; - if (selectionMode === 'none') { - newComponent = 'ActionButtonGroup'; - childComponent = 'ActionButton'; - selectionModePath?.remove(); - } else { - newComponent = 'ToggleButtonGroup'; - childComponent = 'ToggleButton'; - } - - let localName = newComponent; - if (availableComponents.has(newComponent)) { - let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); - } - - let localChildName = childComponent; - if (availableComponents.has(childComponent)) { - let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localChildName = addComponentImport(program, childComponent); - } - - - // Convert dynamic collection to an array.map. - let items = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'items') as NodePath | undefined; - let itemArg: t.Identifier | undefined; - if (items && t.isJSXExpressionContainer(items.node.value) && t.isExpression(items.node.value.expression)) { - let child = path.get('children').find(c => c.isJSXExpressionContainer()); - if (child && child.isJSXExpressionContainer() && t.isFunction(child.node.expression)) { - let arg = child.node.expression.params[0]; - if (t.isIdentifier(arg)) { - itemArg = arg; - } - - child.replaceWith( - t.jsxExpressionContainer( - t.callExpression( - t.memberExpression( - items.node.value.expression, - t.identifier('map') - ), - [child.node.expression] - ) - ) - ); - } - } - items?.remove(); - - let onAction = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'onAction') as NodePath | undefined; - - // Pull disabledKeys prop out into a variable, converted to a Set. - // Then we can check it in the isDisabled prop of each item. - let disabledKeysPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'disabledKeys') as NodePath | undefined; - let disabledKeys: t.Identifier | undefined; - if (disabledKeysPath && t.isJSXExpressionContainer(disabledKeysPath.node.value) && t.isExpression(disabledKeysPath.node.value.expression)) { - disabledKeys = path.scope.generateUidIdentifier('disabledKeys'); - path.scope.push({ - id: disabledKeys, - init: t.newExpression(t.identifier('Set'), [disabledKeysPath.node.value.expression]), - kind: 'let' - }); - disabledKeysPath.remove(); - } - - path.traverse({ - JSXElement(child) { - if (t.isJSXIdentifier(child.node.openingElement.name) && child.node.openingElement.name.name === 'Item') { - // Replace Item with ActionButton or ToggleButton. - let childNode = t.cloneNode(child.node); - childNode.openingElement.name = t.jsxIdentifier(localChildName); - if (childNode.closingElement) { - childNode.closingElement.name = t.jsxIdentifier(localChildName); - } - - // If there is no key prop and we are using dynamic collections, add a default computed from item.key ?? item.id. - let key = childNode.openingElement.attributes.find(attr => t.isJSXAttribute(attr) && attr.name.name === 'key') as t.JSXAttribute | undefined; - if (!key && itemArg) { - let id = t.jsxExpressionContainer( - t.logicalExpression( - '??', - t.memberExpression(itemArg, t.identifier('key')), - t.memberExpression(itemArg, t.identifier('id')) - ) - ); - - key = t.jsxAttribute( - t.jsxIdentifier('key'), - id - ); - - childNode.openingElement.attributes.push(key); - } - - // If this is a ToggleButtonGroup, add an id prop in addition to key when needed. - if (key && newComponent === 'ToggleButtonGroup') { - // If we are in an array.map we need both key and id. Otherwise, we only need id. - if (itemArg) { - childNode.openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('id'), key.value)); - } else { - key.name.name = 'id'; - } - } - - let keyValue: t.Expression | undefined = undefined; - if (key && t.isJSXExpressionContainer(key.value) && t.isExpression(key.value.expression)) { - keyValue = key.value.expression; - } else if (key && t.isStringLiteral(key.value)) { - keyValue = key.value; - } - - // Add an onPress to each item that calls the previous onAction, passing in the key. - if (onAction && t.isJSXExpressionContainer(onAction.node.value) && t.isExpression(onAction.node.value.expression)) { - childNode.openingElement.attributes.push( - t.jsxAttribute( - t.jsxIdentifier('onPress'), - t.jsxExpressionContainer( - keyValue - ? t.arrowFunctionExpression([], t.callExpression(onAction.node.value.expression, [keyValue])) - : onAction.node.value.expression - ) - ) - ); - } - - // Add an isDisabled prop to each item, testing whether it is in disabledKeys. - if (disabledKeys && keyValue) { - childNode.openingElement.attributes.push( - t.jsxAttribute( - t.jsxIdentifier('isDisabled'), - t.jsxExpressionContainer( - t.callExpression( - t.memberExpression( - disabledKeys, - t.identifier('has') - ), - [keyValue] - ) - ) - ) - ); - } - - child.replaceWith(childNode); - } - } - }); - - onAction?.remove(); - - path.node.openingElement.name = t.jsxIdentifier(localName); - if (path.node.closingElement) { - path.node.closingElement.name = t.jsxIdentifier(localName); - } -} - -/** - * Adds isRowHeader to the first Column in a table if there isn't already a row header. - * @param path - */ -function addRowHeader( - path: NodePath -) { - let tableHeaderPath = path.get('children').find((child) => - t.isJSXElement(child.node) && - t.isJSXIdentifier(child.node.openingElement.name) && - getName(child as NodePath, child.node.openingElement.name) === 'TableHeader' - ) as NodePath | undefined; - - - // Check if isRowHeader is already set on a Column - let hasRowHeader = false; - tableHeaderPath?.get('children').forEach((child) => { - if ( - t.isJSXElement(child.node) && - t.isJSXIdentifier(child.node.openingElement.name) && - getName(child as NodePath, child.node.openingElement.name) === 'Column' - ) { - let isRowHeaderProp = (child.get('openingElement') as NodePath).get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'isRowHeader') as NodePath | undefined; - if (isRowHeaderProp) { - hasRowHeader = true; - } - } - }); - - // If there isn't already a row header, add one to the first Column if possible - if (!hasRowHeader) { - tableHeaderPath?.get('children').forEach((child) => { - // Add to first Column if static - if ( - !hasRowHeader && - t.isJSXElement(child.node) && - t.isJSXIdentifier(child.node.openingElement.name) && - getName(child as NodePath, child.node.openingElement.name) === 'Column' - ) { - child.node.openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('isRowHeader'), t.jsxExpressionContainer(t.booleanLiteral(true)))); - hasRowHeader = true; - } - - // If render function is used, leave a comment to update manually - if ( - t.isJSXExpressionContainer(child.node) && - t.isArrowFunctionExpression(child.node.expression) - ) { - addComment(child.node, ' TODO(S2-upgrade): You\'ll need to add isRowHeader to one of the columns manually.'); - } - - // If array.map is used, leave a comment to update manually - if ( - t.isJSXExpressionContainer(child.node) && - t.isCallExpression(child.node.expression) && - t.isMemberExpression(child.node.expression.callee) && - t.isIdentifier(child.node.expression.callee.property) && - child.node.expression.callee.property.name === 'map' - ) { - addComment(child.node, ' TODO(S2-upgrade): You\'ll need to add isRowHeader to one of the columns manually.'); - } - }); - } -} - -export const functionMap = { - updatePropNameAndValue, - updatePropValueAndAddNewProp, - updatePropName, - removeProp, - commentOutProp, - addCommentToElement, - updateComponentIfPropPresent, - moveRenderPropsToChild, - updateComponentWithinCollection, - commentIfParentCollectionNotDetected, - updateTabs, - movePropToNewChildComponent, - movePropToParentComponent, - updateToNewComponent, - convertDimensionValueToPx, - updatePlacementToSingleValue, - removeComponentIfWithinParent, - updateAvatarSize, - updateLegacyLink, - addColumnsPropToRow, - updateRowFunctionArg, - updateDialogChild, - updateActionGroup, - updateKeyToId, - commentIfNestedColumns, - addRowHeader -}; From 6bd9437cd666a347a436e4ca3400b29f811f430f Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 8 Apr 2025 10:54:08 -0500 Subject: [PATCH 02/10] fix TableView --- .../src/codemods/components/{Table => TableView}/transform.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/dev/codemods/src/s1-to-s2/src/codemods/components/{Table => TableView}/transform.ts (100%) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Table/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts similarity index 100% rename from packages/dev/codemods/src/s1-to-s2/src/codemods/components/Table/transform.ts rename to packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts From a835f6efbc0b41e9c2b3b579f92de6bad577b562 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 8 Apr 2025 10:55:13 -0500 Subject: [PATCH 03/10] fix selectionStyle --- .../src/s1-to-s2/src/codemods/components/TableView/transform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts index 68b11ffa58a..903cdce1ed4 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts @@ -164,7 +164,7 @@ export default function transformTable(path: NodePath) { commentOutProp(path, {propToComment: 'dragAndDropHooks'}); // Comment out selectionStyle="highlight" - commentOutProp(path, {propToComment: 'selectionStyle', propValue: 'highlight'}); + commentOutProp(path, {propToComment: 'selectionStyle'}); // Comment out unstable expandable rows props commentOutProp(path, {propToComment: 'UNSTABLE_allowsExpandableRows'}); From ea1bf0d26b83391e62feee71b59faea1198f6870 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 8 Apr 2025 11:15:16 -0500 Subject: [PATCH 04/10] fix key to id --- .../src/codemods/components/Item/transform.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts index 272e00c7c32..7903de5b96e 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts @@ -16,14 +16,6 @@ import * as t from '@babel/types'; * - Update key to id (and keep key if rendered inside array.map). */ export default function transformItem(path: NodePath) { - // Comment if parent collection not detected - // Reason: Item needs to be updated based on its parent collection component. - commentIfParentCollectionNotDetected(path); - - // Update key to id - // Reason: Standardizing collection item identifiers. - updateKeyToId(path); - // Update Items based on parent collection component updateComponentWithinCollection(path, {parentComponent: 'Menu', newComponent: 'MenuItem'}); updateComponentWithinCollection(path, {parentComponent: 'ActionMenu', newComponent: 'MenuItem'}); @@ -31,4 +23,8 @@ export default function transformItem(path: NodePath) { updateComponentWithinCollection(path, {parentComponent: 'Breadcrumbs', newComponent: 'Breadcrumb'}); updateComponentWithinCollection(path, {parentComponent: 'Picker', newComponent: 'PickerItem'}); updateComponentWithinCollection(path, {parentComponent: 'ComboBox', newComponent: 'ComboBoxItem'}); + + // Comment if parent collection not detected + // Reason: Item needs to be updated based on its parent collection component. + commentIfParentCollectionNotDetected(path); } From 9406467aa2ca0f0f6a64db7e5cdb3a9eed79020b Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 8 Apr 2025 12:14:15 -0500 Subject: [PATCH 05/10] clean up comments --- .../src/s1-to-s2/src/codemods/codemod.ts | 11 ++---- .../components/ActionGroup/transform.ts | 9 ++--- .../components/ActionMenu/transform.ts | 7 ++-- .../codemods/components/Avatar/transform.ts | 3 +- .../codemods/components/Badge/transform.ts | 3 +- .../components/Breadcrumbs/transform.ts | 11 ++---- .../codemods/components/Button/transform.ts | 2 +- .../components/CheckboxGroup/transform.ts | 3 +- .../components/ColorField/transform.ts | 5 +-- .../components/ColorSlider/transform.ts | 3 +- .../codemods/components/ComboBox/transform.ts | 9 +---- .../components/ContextualHelp/transform.ts | 3 +- .../components/DialogContainer/transform.ts | 3 +- .../components/DialogTrigger/transform.ts | 18 ++++++++++ .../src/codemods/components/Form/transform.ts | 6 +--- .../components/InlineAlert/transform.ts | 3 +- .../src/codemods/components/Item/transform.ts | 4 --- .../src/codemods/components/Link/transform.ts | 35 ++++++++++++------- .../components/NumberField/transform.ts | 4 +-- .../codemods/components/Picker/transform.ts | 8 +---- .../components/ProgressBar/transform.ts | 4 +-- .../components/ProgressCircle/transform.ts | 2 +- .../components/RadioGroup/transform.ts | 4 +-- .../components/RangeSlider/transform.ts | 5 +-- .../src/codemods/components/Row/transform.ts | 4 +-- .../components/SearchField/transform.ts | 6 +--- .../codemods/components/Section/transform.ts | 2 +- .../codemods/components/Slider/transform.ts | 7 +--- .../components/StatusLight/transform.ts | 4 +-- .../components/TableView/transform.ts | 5 ++- .../src/codemods/components/Tabs/transform.ts | 2 -- .../codemods/components/TagGroup/transform.ts | 3 +- .../codemods/components/TextArea/transform.ts | 6 +--- .../components/TextField/transform.ts | 6 +--- .../codemods/components/Tooltip/transform.ts | 4 +-- .../components/TooltipTrigger/transform.ts | 3 +- 36 files changed, 81 insertions(+), 136 deletions(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts index c9abf54b2cb..e84227938e1 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts @@ -199,15 +199,10 @@ export default function transformer(file: FileInfo, api: API, options: Options) if (componentTransform && typeof componentTransform.default === 'function') { componentTransform.default(path); } else { - // Optional: Add a comment if a transform is expected but not found or not a function - // addComment(path.node, ` TODO(S2-upgrade): Transform for ${elementName} not found or invalid.`); + // console.error('Error running transform at:', transformPath); } - } catch (error) { - // Handle cases where the transform file might not exist - if (error.code !== 'MODULE_NOT_FOUND') { - addComment(path.node, ` TODO(S2-upgrade): Error applying transform for ${elementName}: ${error.message}`); - } - // If MODULE_NOT_FOUND, it means no specific transform exists, which might be okay. + } catch (error: any) { + // console.error('Error running transform:', error); } }); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts index 40ac1408103..2ae5b5ccee3 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts @@ -7,12 +7,12 @@ import * as t from '@babel/types'; let availableComponents = getComponents(); /** - * Transforms ActionGroup props and structure: - * - Use ActionButtonGroup if no selection. - * - Use ToggleButtonGroup if selection is used. + * Transforms ActionGroup: * - Comment out overflowMode (it has not been implemented yet). * - Comment out buttonLabelBehavior (it has not been implemented yet). * - Comment out summaryIcon (it has not been implemented yet). + * - Use ActionButtonGroup if no selection. + * - Use ToggleButtonGroup if selection is used. * - Update root level onAction to onPress on each ActionButton. * - Apply isDisabled directly on each ActionButton/ToggleButton instead of disabledKeys. * - Update key to id (keep key for map). @@ -20,15 +20,12 @@ let availableComponents = getComponents(); */ export default function transformActionGroup(path: NodePath) { // Comment out overflowMode - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'overflowMode'}); // Comment out buttonLabelBehavior - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'buttonLabelBehavior'}); // Comment out summaryIcon - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'summaryIcon'}); let selectionModePath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'selectionMode') as NodePath | undefined; diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts index 813932f9ed7..e4a129a6669 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts @@ -1,19 +1,16 @@ -import {commentOutProp, updateComponentWithinCollection} from '../../shared/transforms'; +import {commentOutProp} from '../../shared/transforms'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms ActionMenu props: + * Transforms ActionMenu: * - Comment out closeOnSelect (it has not been implemented yet). * - Comment out trigger (it has not been implemented yet). - * - Update Item to be a MenuItem. */ export default function transformActionMenu(path: NodePath) { // Comment out closeOnSelect - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'closeOnSelect'}); // Comment out trigger - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'trigger'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts index 1ce020bd270..c253bc46d4c 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts @@ -36,13 +36,12 @@ function updateAvatarSize( } /** - * Transforms Avatar props: + * Transforms Avatar: * - Comment out isDisabled (it has not been implemented yet). * - Update size to be a pixel value if it currently matches 'avatar-size-*'. */ export default function transformAvatar(path: NodePath) { // Comment out isDisabled - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'isDisabled'}); // Update size to be a pixel value if it currently matches 'avatar-size-*' diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts index 7b97b3ee2af..15c945d4655 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts @@ -3,12 +3,11 @@ import * as t from '@babel/types'; import {updatePropNameAndValue} from '../../shared/transforms'; /** - * Transforms Badge props: + * Transforms Badge: * - Change variant="info" to variant="informative". */ export default function transformBadge(path: NodePath) { // Change variant="info" to variant="informative" - // Reason: Updated naming convention updatePropNameAndValue(path, { oldProp: 'variant', oldValue: 'info', diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts index 0ec360177b0..c902e10e10e 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts @@ -1,40 +1,33 @@ import { addCommentToElement, commentOutProp, - removeProp, - updateComponentWithinCollection + removeProp } from '../../shared/transforms'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms Breadcrumbs props: + * Transforms Breadcrumbs: * - Comment out showRoot (it has not been implemented yet). * - Comment out isMultiline (it has not been implemented yet). * - Comment out autoFocusCurrent (it has not been implemented yet). * - Remove size="S" (Small is no longer a supported size in Spectrum 2). - * - Update Item to be a Breadcrumb. * - Add comment to wrap in nav element if needed. */ export default function transformBreadcrumbs(path: NodePath) { // Comment out showRoot - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'showRoot'}); // Comment out isMultiline - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'isMultiline'}); // Comment out autoFocusCurrent - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'autoFocusCurrent'}); // Remove size="S" - // Reason: Small is no longer a supported size in Spectrum 2 removeProp(path, {propToRemove: 'size', propValue: 'S'}); // Add comment to wrap in nav element if needed - // Reason: A nav element is no longer included inside Breadcrumbs by default. addCommentToElement(path, { comment: 'S2 Breadcrumbs no longer includes a nav element by default. You can wrap the Breadcrumbs component in a nav element if needed.' }); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts index 49e0f366594..b75e3961bd6 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts @@ -10,7 +10,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms Button props: + * Transforms Button: * - Change variant="cta" to variant="accent" * - Change variant="overBackground" to variant="primary" staticColor="white" * - Change style to fillStyle diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts index 1a732ca4f73..b3b2cb26f90 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts @@ -3,11 +3,10 @@ import {removeProp} from '../../shared/transforms'; import * as t from '@babel/types'; /** - * Transforms CheckboxGroup props: + * Transforms CheckboxGroup: * - Remove showErrorIcon (it has been removed due to accessibility issues). */ export default function transformCheckboxGroup(path: NodePath) { // Remove showErrorIcon - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'showErrorIcon'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts index 2447331bb82..c56a5d3d1bf 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts @@ -3,7 +3,7 @@ import {removeProp, updatePropNameAndValue} from '../../shared/transforms'; import * as t from '@babel/types'; /** - * Transforms ColorField props: + * Transforms ColorField: * - Remove isQuiet (it is no longer supported in Spectrum 2). * - Remove placeholder (it has been removed due to accessibility issues). * - Change validationState="invalid" to isInvalid. @@ -11,11 +11,9 @@ import * as t from '@babel/types'; */ export default function transformColorField(path: NodePath) { // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); // Remove placeholder - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'placeholder'}); // Change validationState="invalid" to isInvalid @@ -27,6 +25,5 @@ export default function transformColorField(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts index 039d38e45b2..82a3632aee1 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts @@ -3,11 +3,10 @@ import {removeProp} from '../../shared/transforms'; import * as t from '@babel/types'; /** - * Transforms ColorSlider props: + * Transforms ColorSlider: * - Remove showValueLabel (it has been removed due to accessibility issues). */ export default function transformColorSlider(path: NodePath) { // Remove showValueLabel - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'showValueLabel'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts index 3ee7a463553..79495317cd0 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts @@ -8,7 +8,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms ComboBox props: + * Transforms ComboBox: * - Change menuWidth value from a DimensionValue to a pixel value. * - Remove isQuiet (it is no longer supported in Spectrum 2). * - Comment out loadingState (it has not been implemented yet). @@ -16,23 +16,18 @@ import * as t from '@babel/types'; * - Change validationState="invalid" to isInvalid. * - Remove validationState="valid" (it is no longer supported in Spectrum 2). * - Comment out onLoadMore (it has not been implemented yet). - * - Update Item to be a ComboBoxItem. */ export default function transformComboBox(path: NodePath) { // Change menuWidth value from a DimensionValue to a pixel value - // Reason: API change convertDimensionValueToPx(path, {propToConvertValue: 'menuWidth'}); // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); // Comment out loadingState - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'loadingState'}); // Remove placeholder - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'placeholder'}); // Change validationState="invalid" to isInvalid @@ -44,10 +39,8 @@ export default function transformComboBox(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); // Comment out onLoadMore - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'onLoadMore'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts index b8ad3e03057..566dfb7bf29 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts @@ -3,13 +3,12 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms ContextualHelp props: + * Transforms ContextualHelp: * - Comment out variant="info" (informative variant is the only one supported). * - Update placement prop to have only one value (e.g., "bottom left" becomes "bottom"). */ export default function transformContextualHelp(path: NodePath) { // Comment out variant="info" - // Reason: Informative variant is the only one supported commentOutProp(path, {propToComment: 'variant', propValue: 'info'}); // Update placement prop to have only one value diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts index 93149624e0b..9d7a1f92160 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts @@ -4,14 +4,13 @@ import {updateDialogChild} from '../DialogTrigger/transform'; import {updatePropName} from '../../shared/transforms'; /** - * Transforms DialogContainer props: + * Transforms DialogContainer: * - Remove type (dependent on the dialog level child used, e.g., Dialog, FullscreenDialog, Popover). * - Move isDismissable (as isDismissible) to the dialog level component. * - Move isKeyboardDismissDisabled to the dialog level component. */ export default function transformDialogContainer(path: NodePath) { // Move isDismissable (as isDismissible) to the dialog level component - // Reason: Prop now exists on the dialog level component updatePropName(path, {oldProp: 'isDismissable', newProp: 'isDismissible'}); updateDialogChild(path); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts index 3c2da332996..1aafafb14fa 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts @@ -67,11 +67,29 @@ export function updateDialogChild( }); } +/** + * Transforms DialogTrigger: + * - Comment out type="tray" (it has not been implemented yet). + * - Comment out mobileType (it has not been implemented yet). + * - Remove targetRef (it is no longer supported). + * - Move render props to the child component (updated API). + */ export default function transformDialogTrigger(path: NodePath) { + // Comment out type="tray" commentOutProp(path, {propToComment: 'type', propValue: 'tray'}); + + // Comment out mobileType commentOutProp(path, {propToComment: 'mobileType'}); + + // Remove targetRef removeProp(path, {propToRemove: 'targetRef'}); + + // Move render props to the child component moveRenderPropsToChild(path, {newChildComponent: 'Dialog'}); + + // Update isDismissable to isDismissible updatePropName(path, {oldProp: 'isDismissable', newProp: 'isDismissible'}); + + // Update DialogTrigger to the new API updateDialogChild(path); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts index 6df3bc35e72..f6513901bd9 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts @@ -3,7 +3,7 @@ import {removeProp} from '../../shared/transforms'; import * as t from '@babel/types'; /** - * Transforms Form props: + * Transforms Form: * - Remove isQuiet (it is no longer supported in Spectrum 2). * - Remove isReadOnly (it is no longer supported in Spectrum 2). * - Remove validationState (it is no longer supported in Spectrum 2). @@ -11,18 +11,14 @@ import * as t from '@babel/types'; */ export default function transformForm(path: NodePath) { // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); // Remove isReadOnly - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isReadOnly'}); // Remove validationState - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState'}); // Remove validationBehavior - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationBehavior'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts index 354ac37028f..5ffbb56ac27 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts @@ -3,12 +3,11 @@ import * as t from '@babel/types'; import {updatePropNameAndValue} from '../../shared/transforms'; /** - * Transforms InlineAlert props: + * Transforms InlineAlert: * - Change variant="info" to variant="informative". */ export default function transformInlineAlert(path: NodePath) { // Change variant="info" to variant="informative" - // Reason: Updated naming convention updatePropNameAndValue(path, { oldProp: 'variant', oldValue: 'info', diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts index 7903de5b96e..92b05009688 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts @@ -10,9 +10,6 @@ import * as t from '@babel/types'; * - If within Breadcrumbs: Update Item to be a Breadcrumb. * - If within Picker: Update Item to be a PickerItem. * - If within ComboBox: Update Item to be a ComboBoxItem. - * - If within ListBox: Update Item to be a ListBoxItem. - * - If within TabList: Update Item to be a Tab. - * - If within TabPanels: Update Item to be a TabPanel and remove surrounding TabPanels. * - Update key to id (and keep key if rendered inside array.map). */ export default function transformItem(path: NodePath) { @@ -25,6 +22,5 @@ export default function transformItem(path: NodePath) { updateComponentWithinCollection(path, {parentComponent: 'ComboBox', newComponent: 'ComboBoxItem'}); // Comment if parent collection not detected - // Reason: Item needs to be updated based on its parent collection component. commentIfParentCollectionNotDetected(path); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts index 072a1703157..727e13db6e3 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts @@ -4,20 +4,11 @@ import * as t from '@babel/types'; import {updatePropNameAndValue} from '../../shared/transforms'; /** - * Transforms Link props: - * - Change variant="overBackground" to staticColor="white". - * - If was used inside Link (legacy API), remove the and apply props directly to Link. + * If was used inside Link (legacy API), remove the and apply props directly to Link. */ -export default function transformLink(path: NodePath) { - // Change variant="overBackground" to staticColor="white" - // Reason: Updated naming convention - updatePropNameAndValue(path, { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'staticColor', - newValue: 'white' - }); - +function updateLegacyLink( + path: NodePath +) { let missingOuterHref = t.isJSXElement(path.node) && !path.node.openingElement.attributes.some((attr) => t.isJSXAttribute(attr) && attr.name.name === 'href'); if (missingOuterHref) { let innerLink = path.node.children.find((child) => t.isJSXElement(child) && t.isJSXIdentifier(child.openingElement.name)); @@ -37,4 +28,22 @@ export default function transformLink(path: NodePath) { path.node.children = innerLink.children; } } +} + +/** + * Transforms Link: + * - Change variant="overBackground" to staticColor="white". + * - If was used inside Link (legacy API), remove the and apply props directly to Link. + */ +export default function transformLink(path: NodePath) { + // Change variant="overBackground" to staticColor="white" + updatePropNameAndValue(path, { + oldProp: 'variant', + oldValue: 'overBackground', + newProp: 'staticColor', + newValue: 'white' + }); + + // Update legacy Link + updateLegacyLink(path); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts index 9a90443f15d..d1890a269cf 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts @@ -3,14 +3,13 @@ import {removeProp, updatePropNameAndValue} from '../../shared/transforms'; import * as t from '@babel/types'; /** - * Transforms NumberField props: + * Transforms NumberField: * - Remove isQuiet (it is no longer supported in Spectrum 2). * - Change validationState="invalid" to isInvalid. * - Remove validationState="valid" (it is no longer supported in Spectrum 2). */ export default function transformNumberField(path: NodePath) { // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); // Change validationState="invalid" to isInvalid @@ -22,6 +21,5 @@ export default function transformNumberField(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts index bd836399685..b911d0a0dca 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts @@ -8,22 +8,19 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms Picker props: + * Transforms Picker: * - Change menuWidth value from a DimensionValue to a pixel value. * - Remove isQuiet (it is no longer supported in Spectrum 2). * - Change validationState="invalid" to isInvalid. * - Remove validationState="valid" (it is no longer supported in Spectrum 2). * - Comment out isLoading (it has not been implemented yet). * - Comment out onLoadMore (it has not been implemented yet). - * - Update Item to be a PickerItem. */ export default function transformPicker(path: NodePath) { // Change menuWidth value from a DimensionValue to a pixel value - // Reason: API change convertDimensionValueToPx(path, {propToConvertValue: 'menuWidth'}); // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); // Change validationState="invalid" to isInvalid @@ -35,14 +32,11 @@ export default function transformPicker(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); // Comment out isLoading - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'isLoading'}); // Comment out onLoadMore - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'onLoadMore'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts index 6b63344f7cc..85bbed7013e 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts @@ -3,7 +3,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms ProgressBar props: + * Transforms ProgressBar: * - Change variant="overBackground" to staticColor="white". * - Comment out labelPosition (it has not been implemented yet). * - Comment out showValueLabel (it has not been implemented yet). @@ -18,10 +18,8 @@ export default function transformProgressBar(path: NodePath) { }); // Comment out labelPosition - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'labelPosition'}); // Comment out showValueLabel - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'showValueLabel'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts index 419ccbca165..9062fb46c41 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts @@ -3,7 +3,7 @@ import * as t from '@babel/types'; import {updatePropNameAndValue} from '../../shared/transforms'; /** - * Transforms ProgressCircle props: + * Transforms ProgressCircle: * - Change variant="overBackground" to staticColor="white". */ export default function transformProgressCircle(path: NodePath) { diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts index e105619731c..03b04b923ae 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts @@ -3,7 +3,7 @@ import {removeProp, updatePropNameAndValue} from '../../shared/transforms'; import * as t from '@babel/types'; /** - * Transforms RadioGroup props: + * Transforms RadioGroup: * - Change validationState="invalid" to isInvalid. * - Remove validationState="valid" (it is no longer supported in Spectrum 2). * - Remove showErrorIcon (it has been removed due to accessibility issues). @@ -18,10 +18,8 @@ export default function transformRadioGroup(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); // Remove showErrorIcon - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'showErrorIcon'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts index f2802e1e391..9095690715d 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts @@ -3,21 +3,18 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms RangeSlider props: + * Transforms RangeSlider: * - Remove showValueLabel (it has been removed due to accessibility issues). * - Comment out getValueLabel (it has not been implemented yet). * - Comment out orientation (it has not been implemented yet). */ export default function transformRangeSlider(path: NodePath) { // Remove showValueLabel - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'showValueLabel'}); // Comment out getValueLabel - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'getValueLabel'}); // Comment out orientation - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'orientation'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Row/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Row/transform.ts index c64e024cb9e..0626424121e 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Row/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Row/transform.ts @@ -4,7 +4,7 @@ import * as t from '@babel/types'; import {updateKeyToId} from '../../shared/transforms'; /** - * Updates the export function signature of the Row component. + * Updates the function signature of the Row component. */ function updateRowFunctionArg( path: NodePath @@ -97,10 +97,10 @@ function updateRowFunctionArg( /** * Transforms Row: * - Update key to id. + * - Update function signature. */ export default function transformRow(path: NodePath) { // Update key to id - // Reason: Standardizing collection item identifiers. updateKeyToId(path); updateRowFunctionArg(path); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts index bd5285abcfd..10e2f5fa270 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts @@ -3,7 +3,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms SearchField props: + * Transforms SearchField: * - Remove placeholder (it has been removed due to accessibility issues). * - Comment out icon (it has not been implemented yet). * - Remove isQuiet (it is no longer supported in Spectrum 2). @@ -12,15 +12,12 @@ import * as t from '@babel/types'; */ export default function transformSearchField(path: NodePath) { // Remove placeholder - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'placeholder'}); // Comment out icon - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'icon'}); // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); // Change validationState="invalid" to isInvalid @@ -32,6 +29,5 @@ export default function transformSearchField(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts index dc2152620e6..6b01580c39e 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts @@ -14,6 +14,7 @@ export default function transformSection(path: NodePath) { updateComponentWithinCollection(path, {parentComponent: 'Picker', newComponent: 'PickerSection'}); updateComponentWithinCollection(path, {parentComponent: 'ComboBox', newComponent: 'ComboBoxSection'}); + // Move title prop to Header component movePropToNewChildComponent(path, { parentComponent: 'Menu', childComponent: 'MenuSection', @@ -34,6 +35,5 @@ export default function transformSection(path: NodePath) { }); // Comment if parent collection not detected - // Reason: Section needs to be updated based on its parent collection component. commentIfParentCollectionNotDetected(path); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts index 03bf10d6f3f..f5fcfe72c84 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts @@ -3,7 +3,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms Slider props: + * Transforms Slider: * - Remove isFilled (Slider is always filled in Spectrum 2). * - Remove trackGradient (Not supported in S2 design). * - Remove showValueLabel (it has been removed due to accessibility issues). @@ -12,22 +12,17 @@ import * as t from '@babel/types'; */ export default function transformSlider(path: NodePath) { // Remove isFilled - // Reason: Slider is always filled in Spectrum 2 removeProp(path, {propToRemove: 'isFilled'}); // Remove trackGradient - // Reason: Not supported in S2 design removeProp(path, {propToRemove: 'trackGradient'}); // Remove showValueLabel - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'showValueLabel'}); // Comment out getValueLabel - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'getValueLabel'}); // Comment out orientation - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'orientation'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts index dadd63576e6..62d3a58e46c 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts @@ -3,17 +3,15 @@ import {removeProp, updatePropNameAndValue} from '../../shared/transforms'; import * as t from '@babel/types'; /** - * Transforms StatusLight props: + * Transforms StatusLight: * - Remove isDisabled (it is no longer supported in Spectrum 2). * - Change variant="info" to variant="informative". */ export default function transformStatusLight(path: NodePath) { // Remove isDisabled - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isDisabled'}); // Change variant="info" to variant="informative" - // Reason: Updated naming convention updatePropNameAndValue(path, { oldProp: 'variant', oldValue: 'info', diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts index 903cdce1ed4..fd67d03ff66 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts @@ -1,5 +1,5 @@ import {addComment, getName} from '../../shared/utils'; -import {commentOutProp, updateKeyToId} from '../../shared/transforms'; +import {commentOutProp} from '../../shared/transforms'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; @@ -143,7 +143,7 @@ function addRowHeader( } /** - * Transforms TableView props: + * Transforms TableView: * - For Column and Row: Update key to be id (and keep key if rendered inside array.map). * - For dynamic tables, pass a columns prop into Row. * - For Row: Update dynamic render function to pass in column instead of columnKey. @@ -157,7 +157,6 @@ export default function transformTable(path: NodePath) { addColumnsPropToRow(path); // Comment out nested columns - // Reason: Nested columns are not supported yet commentIfNestedColumns(path); // Comment out dragAndDropHooks diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts index aa9f5f2865c..ca8c3d7fdce 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts @@ -92,10 +92,8 @@ export default function transformTabs(path: NodePath) { } // Remove isEmphasized - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isEmphasized'}); // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts index 51d2af90886..5a26b1e4e46 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts @@ -3,7 +3,7 @@ import {removeProp, updatePropName, updatePropNameAndValue} from '../../shared/t import * as t from '@babel/types'; /** - * Transforms TagGroup props: + * Transforms TagGroup: * - Rename actionLabel to groupActionLabel. * - Rename onAction to onGroupAction. * - Change validationState="invalid" to isInvalid. @@ -26,6 +26,5 @@ export default function transformTagGroup(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts index 62fb9b21a45..8afb2ca0aab 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts @@ -3,7 +3,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms TextArea props: + * Transforms TextArea: * - Comment out icon (it has not been implemented yet). * - Remove isQuiet (it is no longer supported in Spectrum 2). * - Remove placeholder (it has been removed due to accessibility issues). @@ -12,15 +12,12 @@ import * as t from '@babel/types'; */ export default function transformTextArea(path: NodePath) { // Comment out icon - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'icon'}); // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); // Remove placeholder - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'placeholder'}); // Change validationState="invalid" to isInvalid @@ -32,6 +29,5 @@ export default function transformTextArea(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts index 68adfea475a..ffe6d04f6da 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts @@ -3,7 +3,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms TextField props: + * Transforms TextField: * - Comment out icon (it has not been implemented yet). * - Remove isQuiet (it is no longer supported in Spectrum 2). * - Remove placeholder (it has been removed due to accessibility issues). @@ -12,15 +12,12 @@ import * as t from '@babel/types'; */ export default function transformTextField(path: NodePath) { // Comment out icon - // Reason: It has not been implemented yet commentOutProp(path, {propToComment: 'icon'}); // Remove isQuiet - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'isQuiet'}); // Remove placeholder - // Reason: It has been removed due to accessibility issues removeProp(path, {propToRemove: 'placeholder'}); // Change validationState="invalid" to isInvalid @@ -32,6 +29,5 @@ export default function transformTextField(path: NodePath) { }); // Remove validationState="valid" - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts index 6d0ca99d95b..cf11aded2cc 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts @@ -3,7 +3,7 @@ import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; /** - * Transforms Tooltip props: + * Transforms Tooltip: * - Remove variant (it is no longer supported in Spectrum 2). * - Move placement prop to the parent TooltipTrigger. * - Remove showIcon (it is no longer supported in Spectrum 2). @@ -11,7 +11,6 @@ import * as t from '@babel/types'; */ export default function transformTooltip(path: NodePath) { // Remove variant - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'variant'}); // Move placement prop to the parent TooltipTrigger @@ -22,7 +21,6 @@ export default function transformTooltip(path: NodePath) { }); // Remove showIcon - // Reason: It is no longer supported in Spectrum 2 removeProp(path, {propToRemove: 'showIcon'}); // Move isOpen prop to the parent TooltipTrigger diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts index d451b193ee8..5f1fa8dc66e 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts @@ -3,10 +3,11 @@ import * as t from '@babel/types'; import {updatePlacementToSingleValue} from '../../shared/transforms'; /** - * Transforms TooltipTrigger props: + * Transforms TooltipTrigger: * - Updates placement prop to single value. */ export default function transformTooltipTrigger(path: NodePath) { + // Update placement prop to single value updatePlacementToSingleValue(path, { propToUpdate: 'placement', childComponent: 'Tooltip' From b62c25a7b846460c0918e404f28c5d2476116c18 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 8 Apr 2025 12:17:53 -0500 Subject: [PATCH 06/10] lint --- packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts | 6 ++---- .../src/s1-to-s2/src/codemods/components/Item/transform.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts index e84227938e1..157c583df41 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/codemod.ts @@ -198,11 +198,9 @@ export default function transformer(file: FileInfo, api: API, options: Options) const componentTransform = require(transformPath); if (componentTransform && typeof componentTransform.default === 'function') { componentTransform.default(path); - } else { - // console.error('Error running transform at:', transformPath); } - } catch (error: any) { - // console.error('Error running transform:', error); + } catch { + // Do nothing if the transform doesn't exist } }); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts index 92b05009688..2a9fd84eb43 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts @@ -1,4 +1,4 @@ -import {commentIfParentCollectionNotDetected, updateComponentWithinCollection, updateKeyToId} from '../../shared/transforms'; +import {commentIfParentCollectionNotDetected, updateComponentWithinCollection} from '../../shared/transforms'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; From 579943e269c559170295079005ff69d04fc4782d Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 8 Apr 2025 12:19:05 -0500 Subject: [PATCH 07/10] clean up --- .../src/s1-to-s2/src/codemods/components/Column/transform.ts | 1 - .../src/s1-to-s2/src/codemods/components/Divider/transform.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Column/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Column/transform.ts index 4ecd60888dd..c9a3d987dd2 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Column/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Column/transform.ts @@ -8,6 +8,5 @@ import {updateKeyToId} from '../../shared/transforms'; */ export default function transformColumn(path: NodePath) { // Update key to id - // Reason: Standardizing collection item identifiers. updateKeyToId(path); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts index 784ae9c9ab1..072cc1ef2bd 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts @@ -8,6 +8,5 @@ import * as t from '@babel/types'; */ export default function transformDivider(path: NodePath) { // Remove Divider component if within a Dialog - // Reason: Updated design for Dialog in Spectrum 2 removeComponentIfWithinParent(path, {parentComponent: 'Dialog'}); } From 01e6b67cc44dcd9b0976c52c5b555a79cffab187 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 8 Apr 2025 12:48:53 -0500 Subject: [PATCH 08/10] update README.md --- packages/dev/codemods/src/s1-to-s2/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/dev/codemods/src/s1-to-s2/README.md b/packages/dev/codemods/src/s1-to-s2/README.md index c2db857dd15..0751b1c1a80 100644 --- a/packages/dev/codemods/src/s1-to-s2/README.md +++ b/packages/dev/codemods/src/s1-to-s2/README.md @@ -13,3 +13,11 @@ Run `npx @react-spectrum/codemods s1-to-s2` from the directory you want to upgra ## How it works The upgrade assistant use codemods written with [jscodeshift](https://github.com/facebook/jscodeshift). + +## Adding a new codemod + +To add a new codemod for `Button`, for example, you would: + +1. Create a new transform function in `src/codemods/components/Button/transform.ts` and export it as `default` +2. Implement the transform logic +3. Add tests for the transform in `__tests__/button.test.ts` From 1221be1748e2997bcaedc0dd4cf760c3bdcefbd8 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 15 Apr 2025 15:21:49 -0500 Subject: [PATCH 09/10] improve transform function/option names --- .../components/ActionGroup/transform.ts | 30 +- .../components/ActionMenu/transform.ts | 4 +- .../codemods/components/Avatar/transform.ts | 2 +- .../codemods/components/Badge/transform.ts | 8 +- .../components/Breadcrumbs/transform.ts | 8 +- .../codemods/components/Button/transform.ts | 38 +-- .../components/CheckboxGroup/transform.ts | 2 +- .../components/ColorField/transform.ts | 14 +- .../components/ColorSlider/transform.ts | 2 +- .../codemods/components/ComboBox/transform.ts | 20 +- .../components/ContextualHelp/transform.ts | 4 +- .../components/DialogContainer/transform.ts | 2 +- .../components/DialogTrigger/transform.ts | 22 +- .../codemods/components/Divider/transform.ts | 2 +- .../src/codemods/components/Form/transform.ts | 8 +- .../components/InlineAlert/transform.ts | 8 +- .../src/codemods/components/Item/transform.ts | 12 +- .../src/codemods/components/Link/transform.ts | 8 +- .../components/NumberField/transform.ts | 12 +- .../codemods/components/Picker/transform.ts | 18 +- .../components/ProgressBar/transform.ts | 12 +- .../components/ProgressCircle/transform.ts | 8 +- .../components/RadioGroup/transform.ts | 12 +- .../components/RangeSlider/transform.ts | 6 +- .../components/SearchField/transform.ts | 16 +- .../codemods/components/Section/transform.ts | 38 +-- .../codemods/components/Slider/transform.ts | 10 +- .../components/StatusLight/transform.ts | 10 +- .../components/TableView/transform.ts | 12 +- .../src/codemods/components/Tabs/transform.ts | 14 +- .../codemods/components/TagGroup/transform.ts | 14 +- .../codemods/components/TextArea/transform.ts | 16 +- .../components/TextField/transform.ts | 16 +- .../codemods/components/Tooltip/transform.ts | 16 +- .../components/TooltipTrigger/transform.ts | 4 +- .../src/codemods/shared/transforms.ts | 268 +++++++++--------- .../src/s1-to-s2/src/codemods/shared/utils.ts | 18 +- 37 files changed, 357 insertions(+), 357 deletions(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts index 2ae5b5ccee3..9a997ba81bb 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts @@ -20,36 +20,36 @@ let availableComponents = getComponents(); */ export default function transformActionGroup(path: NodePath) { // Comment out overflowMode - commentOutProp(path, {propToComment: 'overflowMode'}); + commentOutProp(path, {propName: 'overflowMode'}); // Comment out buttonLabelBehavior - commentOutProp(path, {propToComment: 'buttonLabelBehavior'}); + commentOutProp(path, {propName: 'buttonLabelBehavior'}); // Comment out summaryIcon - commentOutProp(path, {propToComment: 'summaryIcon'}); + commentOutProp(path, {propName: 'summaryIcon'}); let selectionModePath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'selectionMode') as NodePath | undefined; let selectionMode = t.isStringLiteral(selectionModePath?.node.value) ? selectionModePath.node.value.value : 'none'; - let newComponent, childComponent; + let newComponentName, childComponentName; if (selectionMode === 'none') { - newComponent = 'ActionButtonGroup'; - childComponent = 'ActionButton'; + newComponentName = 'ActionButtonGroup'; + childComponentName = 'ActionButton'; selectionModePath?.remove(); } else { - newComponent = 'ToggleButtonGroup'; - childComponent = 'ToggleButton'; + newComponentName = 'ToggleButtonGroup'; + childComponentName = 'ToggleButton'; } - let localName = newComponent; - if (availableComponents.has(newComponent)) { + let localName = newComponentName; + if (availableComponents.has(newComponentName)) { let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); + localName = addComponentImport(program, newComponentName); } - let localChildName = childComponent; - if (availableComponents.has(childComponent)) { + let localChildName = childComponentName; + if (availableComponents.has(childComponentName)) { let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localChildName = addComponentImport(program, childComponent); + localChildName = addComponentImport(program, childComponentName); } @@ -125,7 +125,7 @@ export default function transformActionGroup(path: NodePath) { } // If this is a ToggleButtonGroup, add an id prop in addition to key when needed. - if (key && newComponent === 'ToggleButtonGroup') { + if (key && newComponentName === 'ToggleButtonGroup') { // If we are in an array.map we need both key and id. Otherwise, we only need id. if (itemArg) { childNode.openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('id'), key.value)); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts index e4a129a6669..aab0489787c 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionMenu/transform.ts @@ -9,8 +9,8 @@ import * as t from '@babel/types'; */ export default function transformActionMenu(path: NodePath) { // Comment out closeOnSelect - commentOutProp(path, {propToComment: 'closeOnSelect'}); + commentOutProp(path, {propName: 'closeOnSelect'}); // Comment out trigger - commentOutProp(path, {propToComment: 'trigger'}); + commentOutProp(path, {propName: 'trigger'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts index c253bc46d4c..4b4041517ae 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Avatar/transform.ts @@ -42,7 +42,7 @@ function updateAvatarSize( */ export default function transformAvatar(path: NodePath) { // Comment out isDisabled - commentOutProp(path, {propToComment: 'isDisabled'}); + commentOutProp(path, {propName: 'isDisabled'}); // Update size to be a pixel value if it currently matches 'avatar-size-*' updateAvatarSize(path); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts index 15c945d4655..604c857ac78 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Badge/transform.ts @@ -9,9 +9,9 @@ import {updatePropNameAndValue} from '../../shared/transforms'; export default function transformBadge(path: NodePath) { // Change variant="info" to variant="informative" updatePropNameAndValue(path, { - oldProp: 'variant', - oldValue: 'info', - newProp: 'variant', - newValue: 'informative' + oldPropName: 'variant', + oldPropValue: 'info', + newPropName: 'variant', + newPropValue: 'informative' }); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts index c902e10e10e..1766a2e093b 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Breadcrumbs/transform.ts @@ -16,16 +16,16 @@ import * as t from '@babel/types'; */ export default function transformBreadcrumbs(path: NodePath) { // Comment out showRoot - commentOutProp(path, {propToComment: 'showRoot'}); + commentOutProp(path, {propName: 'showRoot'}); // Comment out isMultiline - commentOutProp(path, {propToComment: 'isMultiline'}); + commentOutProp(path, {propName: 'isMultiline'}); // Comment out autoFocusCurrent - commentOutProp(path, {propToComment: 'autoFocusCurrent'}); + commentOutProp(path, {propName: 'autoFocusCurrent'}); // Remove size="S" - removeProp(path, {propToRemove: 'size', propValue: 'S'}); + removeProp(path, {propName: 'size', propValue: 'S'}); // Add comment to wrap in nav element if needed addCommentToElement(path, { diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts index b75e3961bd6..2b7aea5409f 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Button/transform.ts @@ -4,7 +4,7 @@ import { updateComponentIfPropPresent, updatePropName, updatePropNameAndValue, - updatePropValueAndAddNewProp + updatePropValueAndAddNewPropName } from '../../shared/transforms'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; @@ -22,40 +22,40 @@ import * as t from '@babel/types'; export default function transformButton(path: NodePath) { // Change variant="cta" to variant="accent" updatePropNameAndValue(path, { - oldProp: 'variant', - oldValue: 'cta', - newProp: 'variant', - newValue: 'accent' + oldPropName: 'variant', + oldPropValue: 'cta', + newPropName: 'variant', + newPropValue: 'accent' }); // Change variant="overBackground" to variant="primary" staticColor="white" - updatePropValueAndAddNewProp(path, { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'variant', - newValue: 'primary', - additionalProp: 'staticColor', - additionalValue: 'white' + updatePropValueAndAddNewPropName(path, { + oldPropName: 'variant', + oldPropValue: 'overBackground', + newPropName: 'variant', + newPropValue: 'primary', + additionalPropName: 'staticColor', + additionalPropValue: 'white' }); // Change style to fillStyle updatePropName(path, { - oldProp: 'style', - newProp: 'fillStyle' + oldPropName: 'style', + newPropName: 'fillStyle' }); // Comment out isPending - commentOutProp(path, {propToComment: 'isPending'}); + commentOutProp(path, {propName: 'isPending'}); // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // If href is present, the Button should be converted to a LinkButton updateComponentIfPropPresent(path, { - propToCheck: 'href', - newComponent: 'LinkButton' + propName: 'href', + newComponentName: 'LinkButton' }); // Remove elementType - removeProp(path, {propToRemove: 'elementType'}); + removeProp(path, {propName: 'elementType'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts index b3b2cb26f90..8e660423b90 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/CheckboxGroup/transform.ts @@ -8,5 +8,5 @@ import * as t from '@babel/types'; */ export default function transformCheckboxGroup(path: NodePath) { // Remove showErrorIcon - removeProp(path, {propToRemove: 'showErrorIcon'}); + removeProp(path, {propName: 'showErrorIcon'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts index c56a5d3d1bf..716f3e47625 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorField/transform.ts @@ -11,19 +11,19 @@ import * as t from '@babel/types'; */ export default function transformColorField(path: NodePath) { // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // Remove placeholder - removeProp(path, {propToRemove: 'placeholder'}); + removeProp(path, {propName: 'placeholder'}); // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts index 82a3632aee1..04de2a6a01a 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ColorSlider/transform.ts @@ -8,5 +8,5 @@ import * as t from '@babel/types'; */ export default function transformColorSlider(path: NodePath) { // Remove showValueLabel - removeProp(path, {propToRemove: 'showValueLabel'}); + removeProp(path, {propName: 'showValueLabel'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts index 79495317cd0..7dcdaa3e228 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ComboBox/transform.ts @@ -19,28 +19,28 @@ import * as t from '@babel/types'; */ export default function transformComboBox(path: NodePath) { // Change menuWidth value from a DimensionValue to a pixel value - convertDimensionValueToPx(path, {propToConvertValue: 'menuWidth'}); + convertDimensionValueToPx(path, {propName: 'menuWidth'}); // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // Comment out loadingState - commentOutProp(path, {propToComment: 'loadingState'}); + commentOutProp(path, {propName: 'loadingState'}); // Remove placeholder - removeProp(path, {propToRemove: 'placeholder'}); + removeProp(path, {propName: 'placeholder'}); // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); // Comment out onLoadMore - commentOutProp(path, {propToComment: 'onLoadMore'}); + commentOutProp(path, {propName: 'onLoadMore'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts index 566dfb7bf29..a7f4bcb2081 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ContextualHelp/transform.ts @@ -9,8 +9,8 @@ import * as t from '@babel/types'; */ export default function transformContextualHelp(path: NodePath) { // Comment out variant="info" - commentOutProp(path, {propToComment: 'variant', propValue: 'info'}); + commentOutProp(path, {propName: 'variant', propValue: 'info'}); // Update placement prop to have only one value - updatePlacementToSingleValue(path, {propToUpdate: 'placement'}); + updatePlacementToSingleValue(path, {propToUpdateName: 'placement'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts index 9d7a1f92160..de58d3ae0f9 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogContainer/transform.ts @@ -11,7 +11,7 @@ import {updatePropName} from '../../shared/transforms'; */ export default function transformDialogContainer(path: NodePath) { // Move isDismissable (as isDismissible) to the dialog level component - updatePropName(path, {oldProp: 'isDismissable', newProp: 'isDismissible'}); + updatePropName(path, {oldPropName: 'isDismissable', newPropName: 'isDismissible'}); updateDialogChild(path); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts index 1aafafb14fa..217c0ec0c5b 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/DialogTrigger/transform.ts @@ -24,12 +24,12 @@ export function updateDialogChild( ) { let typePath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === 'type') as NodePath | undefined; let type = typePath?.node.value?.type === 'StringLiteral' ? typePath.node.value?.value : 'modal'; - let newComponent = 'Dialog'; + let newComponentName = 'Dialog'; let props: t.JSXAttribute[] = []; if (type === 'popover') { - newComponent = 'Popover'; + newComponentName = 'Popover'; } else if (type === 'fullscreen' || type === 'fullscreenTakeover') { - newComponent = 'FullscreenDialog'; + newComponentName = 'FullscreenDialog'; if (type === 'fullscreenTakeover') { props.push(t.jsxAttribute(t.jsxIdentifier('variant'), t.stringLiteral(type))); } @@ -45,10 +45,10 @@ export function updateDialogChild( typePath?.remove(); - let localName = newComponent; - if (newComponent !== 'Dialog' && availableComponents.has(newComponent)) { + let localName = newComponentName; + if (newComponentName !== 'Dialog' && availableComponents.has(newComponentName)) { let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); + localName = addComponentImport(program, newComponentName); } path.traverse({ @@ -76,19 +76,19 @@ export function updateDialogChild( */ export default function transformDialogTrigger(path: NodePath) { // Comment out type="tray" - commentOutProp(path, {propToComment: 'type', propValue: 'tray'}); + commentOutProp(path, {propName: 'type', propValue: 'tray'}); // Comment out mobileType - commentOutProp(path, {propToComment: 'mobileType'}); + commentOutProp(path, {propName: 'mobileType'}); // Remove targetRef - removeProp(path, {propToRemove: 'targetRef'}); + removeProp(path, {propName: 'targetRef'}); // Move render props to the child component - moveRenderPropsToChild(path, {newChildComponent: 'Dialog'}); + moveRenderPropsToChild(path, {newChildComponentName: 'Dialog'}); // Update isDismissable to isDismissible - updatePropName(path, {oldProp: 'isDismissable', newProp: 'isDismissible'}); + updatePropName(path, {oldPropName: 'isDismissable', newPropName: 'isDismissible'}); // Update DialogTrigger to the new API updateDialogChild(path); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts index 072cc1ef2bd..62af597fad3 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Divider/transform.ts @@ -8,5 +8,5 @@ import * as t from '@babel/types'; */ export default function transformDivider(path: NodePath) { // Remove Divider component if within a Dialog - removeComponentIfWithinParent(path, {parentComponent: 'Dialog'}); + removeComponentIfWithinParent(path, {parentComponentName: 'Dialog'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts index f6513901bd9..7b45947aaa8 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Form/transform.ts @@ -11,14 +11,14 @@ import * as t from '@babel/types'; */ export default function transformForm(path: NodePath) { // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // Remove isReadOnly - removeProp(path, {propToRemove: 'isReadOnly'}); + removeProp(path, {propName: 'isReadOnly'}); // Remove validationState - removeProp(path, {propToRemove: 'validationState'}); + removeProp(path, {propName: 'validationState'}); // Remove validationBehavior - removeProp(path, {propToRemove: 'validationBehavior'}); + removeProp(path, {propName: 'validationBehavior'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts index 5ffbb56ac27..797a63379c2 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/InlineAlert/transform.ts @@ -9,9 +9,9 @@ import {updatePropNameAndValue} from '../../shared/transforms'; export default function transformInlineAlert(path: NodePath) { // Change variant="info" to variant="informative" updatePropNameAndValue(path, { - oldProp: 'variant', - oldValue: 'info', - newProp: 'variant', - newValue: 'informative' + oldPropName: 'variant', + oldPropValue: 'info', + newPropName: 'variant', + newPropValue: 'informative' }); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts index 2a9fd84eb43..52a91535563 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Item/transform.ts @@ -14,12 +14,12 @@ import * as t from '@babel/types'; */ export default function transformItem(path: NodePath) { // Update Items based on parent collection component - updateComponentWithinCollection(path, {parentComponent: 'Menu', newComponent: 'MenuItem'}); - updateComponentWithinCollection(path, {parentComponent: 'ActionMenu', newComponent: 'MenuItem'}); - updateComponentWithinCollection(path, {parentComponent: 'TagGroup', newComponent: 'Tag'}); - updateComponentWithinCollection(path, {parentComponent: 'Breadcrumbs', newComponent: 'Breadcrumb'}); - updateComponentWithinCollection(path, {parentComponent: 'Picker', newComponent: 'PickerItem'}); - updateComponentWithinCollection(path, {parentComponent: 'ComboBox', newComponent: 'ComboBoxItem'}); + updateComponentWithinCollection(path, {parentComponentName: 'Menu', newComponentName: 'MenuItem'}); + updateComponentWithinCollection(path, {parentComponentName: 'ActionMenu', newComponentName: 'MenuItem'}); + updateComponentWithinCollection(path, {parentComponentName: 'TagGroup', newComponentName: 'Tag'}); + updateComponentWithinCollection(path, {parentComponentName: 'Breadcrumbs', newComponentName: 'Breadcrumb'}); + updateComponentWithinCollection(path, {parentComponentName: 'Picker', newComponentName: 'PickerItem'}); + updateComponentWithinCollection(path, {parentComponentName: 'ComboBox', newComponentName: 'ComboBoxItem'}); // Comment if parent collection not detected commentIfParentCollectionNotDetected(path); diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts index 727e13db6e3..8912afb005b 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Link/transform.ts @@ -38,10 +38,10 @@ function updateLegacyLink( export default function transformLink(path: NodePath) { // Change variant="overBackground" to staticColor="white" updatePropNameAndValue(path, { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'staticColor', - newValue: 'white' + oldPropName: 'variant', + oldPropValue: 'overBackground', + newPropName: 'staticColor', + newPropValue: 'white' }); // Update legacy Link diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts index d1890a269cf..36dcab89a0b 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/NumberField/transform.ts @@ -10,16 +10,16 @@ import * as t from '@babel/types'; */ export default function transformNumberField(path: NodePath) { // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts index b911d0a0dca..0705f4f5011 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Picker/transform.ts @@ -18,25 +18,25 @@ import * as t from '@babel/types'; */ export default function transformPicker(path: NodePath) { // Change menuWidth value from a DimensionValue to a pixel value - convertDimensionValueToPx(path, {propToConvertValue: 'menuWidth'}); + convertDimensionValueToPx(path, {propName: 'menuWidth'}); // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); // Comment out isLoading - commentOutProp(path, {propToComment: 'isLoading'}); + commentOutProp(path, {propName: 'isLoading'}); // Comment out onLoadMore - commentOutProp(path, {propToComment: 'onLoadMore'}); + commentOutProp(path, {propName: 'onLoadMore'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts index 85bbed7013e..f9f6b1313c5 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressBar/transform.ts @@ -11,15 +11,15 @@ import * as t from '@babel/types'; export default function transformProgressBar(path: NodePath) { // Change variant="overBackground" to staticColor="white" updatePropNameAndValue(path, { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'staticColor', - newValue: 'white' + oldPropName: 'variant', + oldPropValue: 'overBackground', + newPropName: 'staticColor', + newPropValue: 'white' }); // Comment out labelPosition - commentOutProp(path, {propToComment: 'labelPosition'}); + commentOutProp(path, {propName: 'labelPosition'}); // Comment out showValueLabel - removeProp(path, {propToRemove: 'showValueLabel'}); + removeProp(path, {propName: 'showValueLabel'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts index 9062fb46c41..a0099b22b5a 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ProgressCircle/transform.ts @@ -9,9 +9,9 @@ import {updatePropNameAndValue} from '../../shared/transforms'; export default function transformProgressCircle(path: NodePath) { // Change variant="overBackground" to staticColor="white" updatePropNameAndValue(path, { - oldProp: 'variant', - oldValue: 'overBackground', - newProp: 'staticColor', - newValue: 'white' + oldPropName: 'variant', + oldPropValue: 'overBackground', + newPropName: 'staticColor', + newPropValue: 'white' }); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts index 03b04b923ae..0fedb73054c 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RadioGroup/transform.ts @@ -11,15 +11,15 @@ import * as t from '@babel/types'; export default function transformRadioGroup(path: NodePath) { // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); // Remove showErrorIcon - removeProp(path, {propToRemove: 'showErrorIcon'}); + removeProp(path, {propName: 'showErrorIcon'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts index 9095690715d..f5214765878 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/RangeSlider/transform.ts @@ -10,11 +10,11 @@ import * as t from '@babel/types'; */ export default function transformRangeSlider(path: NodePath) { // Remove showValueLabel - removeProp(path, {propToRemove: 'showValueLabel'}); + removeProp(path, {propName: 'showValueLabel'}); // Comment out getValueLabel - commentOutProp(path, {propToComment: 'getValueLabel'}); + commentOutProp(path, {propName: 'getValueLabel'}); // Comment out orientation - commentOutProp(path, {propToComment: 'orientation'}); + commentOutProp(path, {propName: 'orientation'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts index 10e2f5fa270..8dc81df674f 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/SearchField/transform.ts @@ -12,22 +12,22 @@ import * as t from '@babel/types'; */ export default function transformSearchField(path: NodePath) { // Remove placeholder - removeProp(path, {propToRemove: 'placeholder'}); + removeProp(path, {propName: 'placeholder'}); // Comment out icon - commentOutProp(path, {propToComment: 'icon'}); + commentOutProp(path, {propName: 'icon'}); // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts index 6b01580c39e..84b0695bce2 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Section/transform.ts @@ -1,4 +1,4 @@ -import {commentIfParentCollectionNotDetected, movePropToNewChildComponent, updateComponentWithinCollection} from '../../shared/transforms'; +import {commentIfParentCollectionNotDetected, movePropToNewChildComponentName, updateComponentWithinCollection} from '../../shared/transforms'; import {NodePath} from '@babel/traverse'; import * as t from '@babel/types'; @@ -10,28 +10,28 @@ import * as t from '@babel/types'; */ export default function transformSection(path: NodePath) { // Update Sections based on parent collection component - updateComponentWithinCollection(path, {parentComponent: 'Menu', newComponent: 'MenuSection'}); - updateComponentWithinCollection(path, {parentComponent: 'Picker', newComponent: 'PickerSection'}); - updateComponentWithinCollection(path, {parentComponent: 'ComboBox', newComponent: 'ComboBoxSection'}); + updateComponentWithinCollection(path, {parentComponentName: 'Menu', newComponentName: 'MenuSection'}); + updateComponentWithinCollection(path, {parentComponentName: 'Picker', newComponentName: 'PickerSection'}); + updateComponentWithinCollection(path, {parentComponentName: 'ComboBox', newComponentName: 'ComboBoxSection'}); // Move title prop to Header component - movePropToNewChildComponent(path, { - parentComponent: 'Menu', - childComponent: 'MenuSection', - propToMove: 'title', - newChildComponent: 'Header' + movePropToNewChildComponentName(path, { + parentComponentName: 'Menu', + childComponentName: 'MenuSection', + propName: 'title', + newChildComponentName: 'Header' }); - movePropToNewChildComponent(path, { - parentComponent: 'Picker', - childComponent: 'PickerSection', - propToMove: 'title', - newChildComponent: 'Header' + movePropToNewChildComponentName(path, { + parentComponentName: 'Picker', + childComponentName: 'PickerSection', + propName: 'title', + newChildComponentName: 'Header' }); - movePropToNewChildComponent(path, { - parentComponent: 'ComboBox', - childComponent: 'ComboBoxSection', - propToMove: 'title', - newChildComponent: 'Header' + movePropToNewChildComponentName(path, { + parentComponentName: 'ComboBox', + childComponentName: 'ComboBoxSection', + propName: 'title', + newChildComponentName: 'Header' }); // Comment if parent collection not detected diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts index f5fcfe72c84..3a69877db88 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Slider/transform.ts @@ -12,17 +12,17 @@ import * as t from '@babel/types'; */ export default function transformSlider(path: NodePath) { // Remove isFilled - removeProp(path, {propToRemove: 'isFilled'}); + removeProp(path, {propName: 'isFilled'}); // Remove trackGradient - removeProp(path, {propToRemove: 'trackGradient'}); + removeProp(path, {propName: 'trackGradient'}); // Remove showValueLabel - removeProp(path, {propToRemove: 'showValueLabel'}); + removeProp(path, {propName: 'showValueLabel'}); // Comment out getValueLabel - commentOutProp(path, {propToComment: 'getValueLabel'}); + commentOutProp(path, {propName: 'getValueLabel'}); // Comment out orientation - commentOutProp(path, {propToComment: 'orientation'}); + commentOutProp(path, {propName: 'orientation'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts index 62d3a58e46c..e61ccdef010 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/StatusLight/transform.ts @@ -9,13 +9,13 @@ import * as t from '@babel/types'; */ export default function transformStatusLight(path: NodePath) { // Remove isDisabled - removeProp(path, {propToRemove: 'isDisabled'}); + removeProp(path, {propName: 'isDisabled'}); // Change variant="info" to variant="informative" updatePropNameAndValue(path, { - oldProp: 'variant', - oldValue: 'info', - newProp: 'variant', - newValue: 'informative' + oldPropName: 'variant', + oldPropValue: 'info', + newPropName: 'variant', + newPropValue: 'informative' }); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts index fd67d03ff66..9984fea9fab 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TableView/transform.ts @@ -160,16 +160,16 @@ export default function transformTable(path: NodePath) { commentIfNestedColumns(path); // Comment out dragAndDropHooks - commentOutProp(path, {propToComment: 'dragAndDropHooks'}); + commentOutProp(path, {propName: 'dragAndDropHooks'}); // Comment out selectionStyle="highlight" - commentOutProp(path, {propToComment: 'selectionStyle'}); + commentOutProp(path, {propName: 'selectionStyle'}); // Comment out unstable expandable rows props - commentOutProp(path, {propToComment: 'UNSTABLE_allowsExpandableRows'}); - commentOutProp(path, {propToComment: 'UNSTABLE_onExpandedChange'}); - commentOutProp(path, {propToComment: 'UNSTABLE_expandedKeys'}); - commentOutProp(path, {propToComment: 'UNSTABLE_defaultExpandedKeys'}); + commentOutProp(path, {propName: 'UNSTABLE_allowsExpandableRows'}); + commentOutProp(path, {propName: 'UNSTABLE_onExpandedChange'}); + commentOutProp(path, {propName: 'UNSTABLE_expandedKeys'}); + commentOutProp(path, {propName: 'UNSTABLE_defaultExpandedKeys'}); addRowHeader(path); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts index ca8c3d7fdce..dceba75b327 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tabs/transform.ts @@ -1,6 +1,6 @@ import {getName, removeComponentImport} from '../../shared/utils'; import {NodePath} from '@babel/traverse'; -import {removeProp, updateComponentWithinCollection, updateToNewComponent} from '../../shared/transforms'; +import {removeProp, updateComponentWithinCollection, updateToNewComponentName} from '../../shared/transforms'; import * as t from '@babel/types'; function transformTabList(tabListPath: NodePath): t.JSXElement { @@ -10,7 +10,7 @@ function transformTabList(tabListPath: NodePath): t.JSXElement { t.isJSXIdentifier(itemPath.node.openingElement.name) && getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' ) { - updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'TabList', newComponent: 'Tab'}); + updateComponentWithinCollection(itemPath as NodePath, {parentComponentName: 'TabList', newComponentName: 'Tab'}); } }); return tabListPath.node; @@ -20,9 +20,9 @@ function transformTabPanels(tabPanelsPath: NodePath, itemsProp: t. // Dynamic case let dynamicRender = tabPanelsPath.get('children').find(path => t.isJSXExpressionContainer(path.node)); if (dynamicRender) { - updateToNewComponent(tabPanelsPath, {newComponent: 'Collection'}); + updateToNewComponentName(tabPanelsPath, {newComponentName: 'Collection'}); let itemPath = (dynamicRender.get('expression') as NodePath).get('body'); - updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'Collection', newComponent: 'TabPanel'}); + updateComponentWithinCollection(itemPath as NodePath, {parentComponentName: 'Collection', newComponentName: 'TabPanel'}); if (itemsProp) { tabPanelsPath.node.openingElement.attributes.push(t.jsxAttribute(t.jsxIdentifier('items'), itemsProp.value)); } @@ -36,7 +36,7 @@ function transformTabPanels(tabPanelsPath: NodePath, itemsProp: t. t.isJSXIdentifier(itemPath.node.openingElement.name) && getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' ) { - updateComponentWithinCollection(itemPath as NodePath, {parentComponent: 'TabPanels', newComponent: 'TabPanel'}); + updateComponentWithinCollection(itemPath as NodePath, {parentComponentName: 'TabPanels', newComponentName: 'TabPanel'}); return itemPath.node; } return null; @@ -92,8 +92,8 @@ export default function transformTabs(path: NodePath) { } // Remove isEmphasized - removeProp(path, {propToRemove: 'isEmphasized'}); + removeProp(path, {propName: 'isEmphasized'}); // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts index 5a26b1e4e46..1bbe3c13119 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TagGroup/transform.ts @@ -12,19 +12,19 @@ import * as t from '@babel/types'; */ export default function transformTagGroup(path: NodePath) { // Rename actionLabel to groupActionLabel - updatePropName(path, {oldProp: 'actionLabel', newProp: 'groupActionLabel'}); + updatePropName(path, {oldPropName: 'actionLabel', newPropName: 'groupActionLabel'}); // Rename onAction to onGroupAction - updatePropName(path, {oldProp: 'onAction', newProp: 'onGroupAction'}); + updatePropName(path, {oldPropName: 'onAction', newPropName: 'onGroupAction'}); // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts index 8afb2ca0aab..585660efefa 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextArea/transform.ts @@ -12,22 +12,22 @@ import * as t from '@babel/types'; */ export default function transformTextArea(path: NodePath) { // Comment out icon - commentOutProp(path, {propToComment: 'icon'}); + commentOutProp(path, {propName: 'icon'}); // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // Remove placeholder - removeProp(path, {propToRemove: 'placeholder'}); + removeProp(path, {propName: 'placeholder'}); // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts index ffe6d04f6da..1de2a30e21c 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TextField/transform.ts @@ -12,22 +12,22 @@ import * as t from '@babel/types'; */ export default function transformTextField(path: NodePath) { // Comment out icon - commentOutProp(path, {propToComment: 'icon'}); + commentOutProp(path, {propName: 'icon'}); // Remove isQuiet - removeProp(path, {propToRemove: 'isQuiet'}); + removeProp(path, {propName: 'isQuiet'}); // Remove placeholder - removeProp(path, {propToRemove: 'placeholder'}); + removeProp(path, {propName: 'placeholder'}); // Change validationState="invalid" to isInvalid updatePropNameAndValue(path, { - oldProp: 'validationState', - oldValue: 'invalid', - newProp: 'isInvalid', - newValue: true + oldPropName: 'validationState', + oldPropValue: 'invalid', + newPropName: 'isInvalid', + newPropValue: true }); // Remove validationState="valid" - removeProp(path, {propToRemove: 'validationState', propValue: 'valid'}); + removeProp(path, {propName: 'validationState', propValue: 'valid'}); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts index cf11aded2cc..87f61303398 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/Tooltip/transform.ts @@ -11,22 +11,22 @@ import * as t from '@babel/types'; */ export default function transformTooltip(path: NodePath) { // Remove variant - removeProp(path, {propToRemove: 'variant'}); + removeProp(path, {propName: 'variant'}); // Move placement prop to the parent TooltipTrigger movePropToParentComponent(path, { - parentComponent: 'TooltipTrigger', - childComponent: 'Tooltip', - propToMove: 'placement' + parentComponentName: 'TooltipTrigger', + childComponentName: 'Tooltip', + propName: 'placement' }); // Remove showIcon - removeProp(path, {propToRemove: 'showIcon'}); + removeProp(path, {propName: 'showIcon'}); // Move isOpen prop to the parent TooltipTrigger movePropToParentComponent(path, { - parentComponent: 'TooltipTrigger', - childComponent: 'Tooltip', - propToMove: 'isOpen' + parentComponentName: 'TooltipTrigger', + childComponentName: 'Tooltip', + propName: 'isOpen' }); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts index 5f1fa8dc66e..fd1be7f9c9d 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/TooltipTrigger/transform.ts @@ -9,7 +9,7 @@ import {updatePlacementToSingleValue} from '../../shared/transforms'; export default function transformTooltipTrigger(path: NodePath) { // Update placement prop to single value updatePlacementToSingleValue(path, { - propToUpdate: 'placement', - childComponent: 'Tooltip' + propToUpdateName: 'placement', + childComponentName: 'Tooltip' }); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts index 414319ebfa9..d9a07471eb9 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/transforms.ts @@ -18,32 +18,32 @@ export function updatePropNameAndValue( path: NodePath, options: { /** Prop name to replace. */ - oldProp: string, + oldPropName: string, /** Prop value to replace. */ - oldValue: ReactNode, + oldPropValue: ReactNode, /** Updated prop name. */ - newProp: string, + newPropName: string, /** Updated prop value. */ - newValue: ReactNode + newPropValue: ReactNode } ) { - const {oldProp, oldValue, newProp, newValue} = options; + const {oldPropName, oldPropValue, newPropName, newPropValue} = options; - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === oldProp) { + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldPropName) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === oldPropName) { if ( t.isStringLiteral(attrPath.node.value) && - attrPath.node.value.value === oldValue + attrPath.node.value.value === oldPropValue ) { // Update old prop name to new prop name - attrPath.node.name.name = newProp; + attrPath.node.name.name = newPropName; // If prop value is a string and matches the old value, replace it with the new value - if (typeof newValue === 'string') { - attrPath.node.value = t.stringLiteral(newValue); - } else if (typeof newValue === 'boolean') { - if (!newValue) { - attrPath.node.value = t.jsxExpressionContainer(t.booleanLiteral(newValue)); + if (typeof newPropValue === 'string') { + attrPath.node.value = t.stringLiteral(newPropValue); + } else if (typeof newPropValue === 'boolean') { + if (!newPropValue) { + attrPath.node.value = t.jsxExpressionContainer(t.booleanLiteral(newPropValue)); } else { attrPath.node.value = null; } @@ -54,23 +54,23 @@ export function updatePropNameAndValue( if (attrPath.node.comments && [...attrPath.node.comments].some((comment) => comment.value.includes('could not be automatically'))) { return; } - addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${oldProp} could not be automatically updated because ${attrPath.node.value.expression.name} could not be followed.`); + addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${oldPropName} could not be automatically updated because ${attrPath.node.value.expression.name} could not be followed.`); } else { // If prop value is an expression, traverse to find a string literal that matches the old and replace it with the new value attrPath.traverse({ StringLiteral(stringPath) { if ( t.isStringLiteral(stringPath.node) && - stringPath.node.value === oldValue + stringPath.node.value === oldPropValue ) { // Update old prop name to new prop name - attrPath.node.name.name = newProp; + attrPath.node.name.name = newPropName; - if (typeof newValue === 'string') { - stringPath.replaceWith(t.stringLiteral(newValue)); - } else if (typeof newValue === 'boolean') { - if (!newValue) { - stringPath.replaceWith(t.booleanLiteral(newValue)); + if (typeof newPropValue === 'string') { + stringPath.replaceWith(t.stringLiteral(newPropValue)); + } else if (typeof newPropValue === 'boolean') { + if (!newPropValue) { + stringPath.replaceWith(t.booleanLiteral(newPropValue)); } else { attrPath.node.value = null; } @@ -89,47 +89,47 @@ export function updatePropNameAndValue( * Example: * - Button: Change `variant="overBackground"` to `variant="primary" staticColor="white"`. */ -export function updatePropValueAndAddNewProp( +export function updatePropValueAndAddNewPropName( path: NodePath, options: { /** Prop name to replace. */ - oldProp: string, + oldPropName: string, /** Prop value to replace. */ - oldValue: ReactNode, + oldPropValue: ReactNode, /** Updated prop name. */ - newProp: string, + newPropName: string, /** Updated prop value. */ - newValue: ReactNode, + newPropValue: ReactNode, /** Additional new prop name to add. */ - additionalProp: string, + additionalPropName: string, /** Additional new prop value to use. */ - additionalValue: string + additionalPropValue: string } ) { const { - oldProp, - oldValue, - newProp, - newValue, - additionalProp, - additionalValue + oldPropName, + oldPropValue, + newPropName, + newPropValue, + additionalPropName, + additionalPropValue } = options; - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; - if (attrPath && t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === oldValue) { + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldPropName) as NodePath; + if (attrPath && t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === oldPropValue) { // Update old prop name to new prop name - attrPath.node.name.name = newProp; + attrPath.node.name.name = newPropName; // If prop value is a string and matches the old value, replace it with the new value - if (typeof newValue === 'string') { - attrPath.node.value = t.stringLiteral(newValue); - } else if (typeof newValue === 'boolean') { - attrPath.node.value = t.jsxExpressionContainer(t.booleanLiteral(newValue)); + if (typeof newPropValue === 'string') { + attrPath.node.value = t.stringLiteral(newPropValue); + } else if (typeof newPropValue === 'boolean') { + attrPath.node.value = t.jsxExpressionContainer(t.booleanLiteral(newPropValue)); } - if (additionalProp && additionalValue) { + if (additionalPropName && additionalPropValue) { attrPath.insertAfter( - t.jsxAttribute(t.jsxIdentifier(additionalProp), t.stringLiteral(additionalValue as string)) + t.jsxAttribute(t.jsxIdentifier(additionalPropName), t.stringLiteral(additionalPropValue as string)) ); } } else if (attrPath && t.isJSXExpressionContainer(attrPath.node.value)) { @@ -138,20 +138,20 @@ export function updatePropValueAndAddNewProp( StringLiteral(stringPath) { if ( t.isStringLiteral(stringPath.node) && - stringPath.node.value === oldValue + stringPath.node.value === oldPropValue ) { // Update old prop name to new prop name - attrPath.node.name.name = newProp; + attrPath.node.name.name = newPropName; - if (typeof newValue === 'string') { - stringPath.replaceWith(t.stringLiteral(newValue)); - } else if (typeof newValue === 'boolean') { - stringPath.replaceWith(t.booleanLiteral(newValue)); + if (typeof newPropValue === 'string') { + stringPath.replaceWith(t.stringLiteral(newPropValue)); + } else if (typeof newPropValue === 'boolean') { + stringPath.replaceWith(t.booleanLiteral(newPropValue)); } - if (additionalProp && additionalValue) { + if (additionalPropName && additionalPropValue) { attrPath.insertAfter( - t.jsxAttribute(t.jsxIdentifier(additionalProp), t.stringLiteral(additionalValue as string)) + t.jsxAttribute(t.jsxIdentifier(additionalPropName), t.stringLiteral(additionalPropValue as string)) ); } } @@ -170,16 +170,16 @@ export function updatePropName( path: NodePath, options: { /** Prop name to replace. */ - oldProp: string, + oldPropName: string, /** Updated prop name. */ - newProp: string + newPropName: string } ) { - const {oldProp, newProp} = options; + const {oldPropName, newPropName} = options; - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldProp) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === oldProp) { - attrPath.node.name.name = newProp; + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === oldPropName) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === oldPropName) { + attrPath.node.name.name = newPropName; } } @@ -193,15 +193,15 @@ export function removeProp( path: NodePath, options: { /** Prop name to remove. */ - propToRemove: string, + propName: string, /** If provided, prop will only be removed if set to this value. */ propValue?: string } ) { - const {propToRemove, propValue} = options; + const {propName, propValue} = options; - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToRemove) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToRemove) { + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propName) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propName) { if (propValue) { // If prop value is provided, remove prop only if it matches the value if (t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === propValue) { @@ -215,7 +215,7 @@ export function removeProp( if (attrPath.node.comments && [...attrPath.node.comments].some((comment) => comment.value.includes('could not be automatically'))) { return; } - addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToRemove} could not be automatically removed because ${attrPath.node.value.expression.name} could not be followed.`); + addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propName} could not be automatically removed because ${attrPath.node.value.expression.name} could not be followed.`); } else { attrPath.traverse({ StringLiteral(stringPath) { @@ -224,7 +224,7 @@ export function removeProp( stringPath.node.value === propValue ) { // Invalid prop value was found inside expression. - addComment(attrPath.node, ` TODO(S2-upgrade): ${propToRemove}="${propValue}" is no longer supported. You'll need to update this manually.`); + addComment(attrPath.node, ` TODO(S2-upgrade): ${propName}="${propValue}" is no longer supported. You'll need to update this manually.`); } } }); @@ -247,19 +247,19 @@ export function commentOutProp( path: NodePath, options: { /** Prop to comment out. */ - propToComment: string, + propName: string, /** If provided, prop will only be commented out if set to this value. */ propValue?: string } ) { - const {propToComment, propValue} = options; + const {propName, propValue} = options; - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToComment) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToComment) { + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propName) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propName) { if (propValue) { // If prop value is provided, comment out prop only if it matches the value if (t.isStringLiteral(attrPath.node.value) && attrPath.node.value.value === propValue) { - addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment}="${propValue}" has not been implemented yet.`); + addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propName}="${propValue}" has not been implemented yet.`); attrPath.remove(); } else { attrPath.traverse({ @@ -268,14 +268,14 @@ export function commentOutProp( t.isStringLiteral(stringPath.node) && stringPath.node.value === propValue ) { - addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment}="${propValue}" has not been implemented yet.`); + addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propName}="${propValue}" has not been implemented yet.`); attrPath.remove(); } } }); } } else { - addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propToComment} has not been implemented yet.`); + addComment(attrPath.parentPath.node, ` TODO(S2-upgrade): ${propName} has not been implemented yet.`); attrPath.remove(); } } @@ -308,21 +308,21 @@ export function updateComponentIfPropPresent( path: NodePath, options: { /** Updated component to use. */ - newComponent: string, + newComponentName: string, /** Will update component if this prop is present. */ - propToCheck: string + propName: string } ) { - const {newComponent, propToCheck} = options; + const {newComponentName, propName} = options; - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToCheck) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToCheck) { + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propName) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propName) { let node = attrPath.findParent((p) => t.isJSXElement(p.node))?.node; if (node && t.isJSXElement(node)) { - let localName = newComponent; - if (availableComponents.has(newComponent)) { + let localName = newComponentName; + if (availableComponents.has(newComponentName)) { let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); + localName = addComponentImport(program, newComponentName); } node.openingElement.name = t.jsxIdentifier(localName); if (node.closingElement) { @@ -342,10 +342,10 @@ export function updateComponentIfPropPresent( export function moveRenderPropsToChild( path: NodePath, options: { - newChildComponent: string + newChildComponentName: string } ) { - const {newChildComponent} = options; + const {newChildComponentName} = options; const renderFunctionIndex = path.node.children.findIndex( (child) => @@ -361,7 +361,7 @@ export function moveRenderPropsToChild( t.isArrowFunctionExpression(renderFunction.expression) && t.isJSXElement(renderFunction.expression.body) && t.isJSXIdentifier(renderFunction.expression.body.openingElement.name) && - getName(path, renderFunction.expression.body.openingElement.name) !== newChildComponent + getName(path, renderFunction.expression.body.openingElement.name) !== newChildComponentName ) { addComment(renderFunction, ' TODO(S2-upgrade): update this dialog to move the close function inside'); return; @@ -409,8 +409,8 @@ export function moveRenderPropsToChild( }); path.node.children[renderFunctionIndex] = t.jsxElement( - t.jsxOpeningElement(t.jsxIdentifier(newChildComponent), attributes), - t.jsxClosingElement(t.jsxIdentifier(newChildComponent)), + t.jsxOpeningElement(t.jsxIdentifier(newChildComponentName), attributes), + t.jsxClosingElement(t.jsxIdentifier(newChildComponentName)), [newRenderFunction] ); @@ -430,11 +430,11 @@ export function moveRenderPropsToChild( export function updateComponentWithinCollection( path: NodePath, options: { - parentComponent: string, - newComponent: string + parentComponentName: string, + newComponentName: string } ) { - const {parentComponent, newComponent} = options; + const {parentComponentName, newComponentName} = options; // Collections currently implemented // TODO: Add 'ActionGroup', 'ListBox', 'ListView' once implemented @@ -454,16 +454,16 @@ export function updateComponentWithinCollection( closestParentCollection && t.isJSXElement(closestParentCollection.node) && t.isJSXIdentifier(closestParentCollection.node.openingElement.name) && - getName(path, closestParentCollection.node.openingElement.name) === parentComponent + getName(path, closestParentCollection.node.openingElement.name) === parentComponentName ) { - // If closest parent collection component matches parentComponent, replace with newComponent + // If closest parent collection component matches parentComponentName, replace with newComponentName updateKeyToId(path); - let localName = newComponent; - if (availableComponents.has(newComponent)) { + let localName = newComponentName; + if (availableComponents.has(newComponentName)) { let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); + localName = addComponentImport(program, newComponentName); } let newNode = t.jsxElement( @@ -507,16 +507,16 @@ export function commentIfParentCollectionNotDetected( * Example: * - Section: If within `Menu`, move `title` prop string to be a child of new `Heading` within a `Header`. */ -export function movePropToNewChildComponent( +export function movePropToNewChildComponentName( path: NodePath, options: { - parentComponent: string, - childComponent: string, - propToMove: string, - newChildComponent: string + parentComponentName: string, + childComponentName: string, + propName: string, + newChildComponentName: string } ) { - const {parentComponent, childComponent, propToMove, newChildComponent} = + const {parentComponentName, childComponentName, propName, newChildComponentName} = options; if ( @@ -524,13 +524,13 @@ export function movePropToNewChildComponent( t.isJSXElement(path.parentPath.node) && t.isJSXIdentifier(path.node.openingElement.name) && t.isJSXIdentifier(path.parentPath.node.openingElement.name) && - getName(path, path.node.openingElement.name) === childComponent && - getName(path, path.parentPath.node.openingElement.name) === parentComponent + getName(path, path.node.openingElement.name) === childComponentName && + getName(path, path.parentPath.node.openingElement.name) === parentComponentName ) { let propValue: t.JSXAttribute['value'] | void; path.node.openingElement.attributes = path.node.openingElement.attributes.filter((attr) => { - if (t.isJSXAttribute(attr) && attr.name.name === propToMove) { + if (t.isJSXAttribute(attr) && attr.name.name === propName) { propValue = attr.value; return false; } @@ -540,8 +540,8 @@ export function movePropToNewChildComponent( if (propValue) { path.node.children.unshift( t.jsxElement( - t.jsxOpeningElement(t.jsxIdentifier(newChildComponent), []), - t.jsxClosingElement(t.jsxIdentifier(newChildComponent)), + t.jsxOpeningElement(t.jsxIdentifier(newChildComponentName), []), + t.jsxClosingElement(t.jsxIdentifier(newChildComponentName)), [t.isStringLiteral(propValue) ? t.jsxText(propValue.value) : propValue] ) ); @@ -559,12 +559,12 @@ export function movePropToNewChildComponent( export function movePropToParentComponent( path: NodePath, options: { - parentComponent: string, - childComponent: string, - propToMove: string + parentComponentName: string, + childComponentName: string, + propName: string } ) { - const {parentComponent, childComponent, propToMove} = options; + const {parentComponentName, childComponentName, propName} = options; path.traverse({ JSXAttribute(attributePath) { @@ -572,12 +572,12 @@ export function movePropToParentComponent( t.isJSXElement(path.parentPath.node) && t.isJSXIdentifier(path.node.openingElement.name) && t.isJSXIdentifier(path.parentPath.node.openingElement.name) && - attributePath.node.name.name === propToMove && - getName(path, path.node.openingElement.name) === childComponent && - getName(path, path.parentPath.node.openingElement.name) === parentComponent + attributePath.node.name.name === propName && + getName(path, path.node.openingElement.name) === childComponentName && + getName(path, path.parentPath.node.openingElement.name) === parentComponentName ) { path.parentPath.node.openingElement.attributes.push( - t.jsxAttribute(t.jsxIdentifier(propToMove), attributePath.node.value) + t.jsxAttribute(t.jsxIdentifier(propName), attributePath.node.value) ); attributePath.remove(); } @@ -591,18 +591,18 @@ export function movePropToParentComponent( * Example: * - Flex: Update `Flex` to be a `div` and apply flex styles using the style macro. */ -export function updateToNewComponent( +export function updateToNewComponentName( path: NodePath, options: { - newComponent: string + newComponentName: string } ) { - const {newComponent} = options; + const {newComponentName} = options; - let localName = newComponent; - if (availableComponents.has(newComponent)) { + let localName = newComponentName; + if (availableComponents.has(newComponentName)) { let program = path.findParent((p) => t.isProgram(p.node)) as NodePath; - localName = addComponentImport(program, newComponent); + localName = addComponentImport(program, newComponentName); } path.node.openingElement.name = t.jsxIdentifier(localName); @@ -628,13 +628,13 @@ const conversions = { export function convertDimensionValueToPx( path: NodePath, options: { - propToConvertValue: string + propName: string } ) { - const {propToConvertValue} = options; + const {propName} = options; - let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToConvertValue) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToConvertValue) { + let attrPath = path.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propName) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propName) { if (t.isStringLiteral(attrPath.node.value)) { try { let value = convertDimension(attrPath.node.value.value, 'size'); @@ -662,11 +662,11 @@ export function convertDimensionValueToPx( } } } catch (error) { - addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToConvertValue} could not be automatically updated due to error: ${error}`); + addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propName} could not be automatically updated due to error: ${error}`); } } else if (t.isJSXExpressionContainer(attrPath.node.value)) { if (t.isIdentifier(attrPath.node.value.expression)) { - addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propToConvertValue} could not be automatically updated because ${attrPath.node.value.expression.name} could not be followed.`); + addComment(attrPath.node, ` TODO(S2-upgrade): Prop ${propName} could not be automatically updated because ${attrPath.node.value.expression.name} could not be followed.`); } } } @@ -681,12 +681,12 @@ export function convertDimensionValueToPx( export function updatePlacementToSingleValue( path: NodePath, options: { - propToUpdate: string, + propToUpdateName: string, /* If provided, updates the prop on the specified child component */ - childComponent?: string + childComponentName?: string } ) { - const {propToUpdate, childComponent} = options; + const {propToUpdateName, childComponentName} = options; const doublePlacementValues = new Set([ 'bottom left', @@ -707,16 +707,16 @@ export function updatePlacementToSingleValue( 'end bottom' ]); - let elementPath = childComponent ? + let elementPath = childComponentName ? path.get('children').find( (child) => t.isJSXElement(child.node) && t.isJSXIdentifier(child.node.openingElement.name) && - getName(path, child.node.openingElement.name) === childComponent + getName(path, child.node.openingElement.name) === childComponentName ) as NodePath : path; - let attrPath = elementPath.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToUpdate) as NodePath; - if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToUpdate) { + let attrPath = elementPath.get('openingElement').get('attributes').find((attr) => t.isJSXAttribute(attr.node) && attr.node.name.name === propToUpdateName) as NodePath; + if (attrPath && t.isJSXAttribute(attrPath.node) && attrPath.node.name.name === propToUpdateName) { if (t.isStringLiteral(attrPath.node.value) && doublePlacementValues.has(attrPath.node.value.value)) { - attrPath.replaceWith(t.jsxAttribute(t.jsxIdentifier(propToUpdate), t.stringLiteral(attrPath.node.value.value.split(' ')[0]))); + attrPath.replaceWith(t.jsxAttribute(t.jsxIdentifier(propToUpdateName), t.stringLiteral(attrPath.node.value.value.split(' ')[0]))); return; } else if (t.isJSXExpressionContainer(attrPath.node.value)) { attrPath.traverse({ @@ -743,16 +743,16 @@ export function updatePlacementToSingleValue( export function removeComponentIfWithinParent( path: NodePath, options: { - parentComponent: string + parentComponentName: string } ) { - const {parentComponent} = options; + const {parentComponentName} = options; if ( t.isJSXElement(path.node) && t.isJSXElement(path.parentPath.node) && t.isJSXIdentifier(path.node.openingElement.name) && t.isJSXIdentifier(path.parentPath.node.openingElement.name) && - getName(path, path.parentPath.node.openingElement.name) === parentComponent + getName(path, path.parentPath.node.openingElement.name) === parentComponentName ) { path.remove(); } diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts index 88365a5eefe..f3425a7f715 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/shared/utils.ts @@ -73,15 +73,15 @@ export function addComment(node: any, comment: string) { }); } -export function addComponentImport(path: NodePath, newComponent: string) { - // If newComponent variable already exists in scope, alias new import to avoid conflict. - let existingBinding = path.scope.getBinding(newComponent); - let localName = newComponent; +export function addComponentImport(path: NodePath, newComponentName: string) { + // If newComponentName variable already exists in scope, alias new import to avoid conflict. + let existingBinding = path.scope.getBinding(newComponentName); + let localName = newComponentName; if (existingBinding) { - let newName = newComponent; + let newName = newComponentName; let i = 1; while (path.scope.hasBinding(newName)) { - newName = newComponent + i; + newName = newComponentName + i; i++; } localName = newName; @@ -93,7 +93,7 @@ export function addComponentImport(path: NodePath, newComponent: stri return ( t.isImportSpecifier(specifier) && specifier.imported.type === 'Identifier' && - specifier.imported.name === newComponent + specifier.imported.name === newComponentName ); }); if (specifier) { @@ -101,14 +101,14 @@ export function addComponentImport(path: NodePath, newComponent: stri return localName; } existingImport.specifiers.push( - t.importSpecifier(t.identifier(localName), t.identifier(newComponent)) + t.importSpecifier(t.identifier(localName), t.identifier(newComponentName)) ); } else { let importDeclaration = t.importDeclaration( [ t.importSpecifier( t.identifier(localName), - t.identifier(newComponent) + t.identifier(newComponentName) ) ], t.stringLiteral('@react-spectrum/s2') From e23cd3c72beaa46eb3e49398376c11ab32da8071 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Tue, 15 Apr 2025 15:41:36 -0500 Subject: [PATCH 10/10] update action group comments --- .../s1-to-s2/src/codemods/components/ActionGroup/transform.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts index 9a997ba81bb..91f2fd15cfa 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/components/ActionGroup/transform.ts @@ -15,7 +15,6 @@ let availableComponents = getComponents(); * - Use ToggleButtonGroup if selection is used. * - Update root level onAction to onPress on each ActionButton. * - Apply isDisabled directly on each ActionButton/ToggleButton instead of disabledKeys. - * - Update key to id (keep key for map). * - Convert dynamic collections render function to items.map. */ export default function transformActionGroup(path: NodePath) { @@ -32,10 +31,12 @@ export default function transformActionGroup(path: NodePath) { let selectionMode = t.isStringLiteral(selectionModePath?.node.value) ? selectionModePath.node.value.value : 'none'; let newComponentName, childComponentName; if (selectionMode === 'none') { + // Use ActionButtonGroup if no selection newComponentName = 'ActionButtonGroup'; childComponentName = 'ActionButton'; selectionModePath?.remove(); } else { + // Use ToggleButtonGroup if selection is used newComponentName = 'ToggleButtonGroup'; childComponentName = 'ToggleButton'; }