From 0f2c781c3c7380e06fafbd60f49a365c103bba52 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Fri, 16 Aug 2024 17:17:42 -0500 Subject: [PATCH] Tabs codemods (#6896) * codemod for new S2 Tabs API * move items from Tabs to TabList * remove transformItem * support dynamic tab panels * pass items into collection * fix ts * update index path to get components * remove private from package.json * cleanup dist * fix test env * remove TabPanels import --- .storybook-s2/docs/Migrating.jsx | 3 + packages/@react-spectrum/s2/src/index.ts | 3 +- .../s2/stories/Tabs.stories.tsx | 18 +- packages/dev/codemods/package.json | 1 - .../__tests__/__snapshots__/tabs.test.ts.snap | 106 +++++++- .../src/s1-to-s2/__tests__/tabs.test.ts | 246 +++++++++++++++++- .../src/s1-to-s2/src/codemods/changes.ts | 65 ++--- .../src/s1-to-s2/src/codemods/transforms.ts | 102 ++++++-- .../src/s1-to-s2/src/codemods/utils.ts | 19 ++ .../src/s1-to-s2/src/getComponents.ts | 2 +- packages/dev/codemods/tsconfig.json | 4 +- 11 files changed, 490 insertions(+), 79 deletions(-) diff --git a/.storybook-s2/docs/Migrating.jsx b/.storybook-s2/docs/Migrating.jsx index c31f30f76e0..9e5158c2a44 100644 --- a/.storybook-s2/docs/Migrating.jsx +++ b/.storybook-s2/docs/Migrating.jsx @@ -290,7 +290,10 @@ export function Migrating() {

Tabs

TagGroup

diff --git a/packages/@react-spectrum/s2/src/index.ts b/packages/@react-spectrum/s2/src/index.ts index a6d50bd884c..91a9ee6a3a0 100644 --- a/packages/@react-spectrum/s2/src/index.ts +++ b/packages/@react-spectrum/s2/src/index.ts @@ -60,6 +60,7 @@ export {TextArea, TextField, TextAreaContext, TextFieldContext} from './TextFiel export {ToggleButton, ToggleButtonContext} from './ToggleButton'; export {Tooltip, TooltipTrigger} from './Tooltip'; +export {Collection} from 'react-aria-components'; export {FileTrigger} from 'react-aria-components'; export type {ActionButtonProps} from './ActionButton'; @@ -102,7 +103,7 @@ export type {SliderProps} from './Slider'; export type {RangeSliderProps} from './RangeSlider'; export type {StatusLightProps} from './StatusLight'; export type {SwitchProps} from './Switch'; -export type {TabsProps, TabProps, TabListProps, TabPanelProps} from './Tabs' +export type {TabsProps, TabProps, TabListProps, TabPanelProps} from './Tabs'; export type {TagGroupProps, TagProps} from './TagGroup'; export type {TextFieldProps, TextAreaProps} from './TextField'; export type {ToggleButtonProps} from './ToggleButton'; diff --git a/packages/@react-spectrum/s2/stories/Tabs.stories.tsx b/packages/@react-spectrum/s2/stories/Tabs.stories.tsx index 54e60ce2495..900907aedab 100644 --- a/packages/@react-spectrum/s2/stories/Tabs.stories.tsx +++ b/packages/@react-spectrum/s2/stories/Tabs.stories.tsx @@ -10,8 +10,8 @@ * governing permissions and limitations under the License. */ -import Edit from '../s2wf-icons/S2_Icon_Edit_20_N.svg'; import Bell from '../s2wf-icons/S2_Icon_Bell_20_N.svg'; +import Edit from '../s2wf-icons/S2_Icon_Edit_20_N.svg'; import Heart from '../s2wf-icons/S2_Icon_Heart_20_N.svg'; import type {Meta} from '@storybook/react'; import {style} from '../style/spectrum-theme' with { type: 'macro' }; @@ -35,18 +35,18 @@ export const Example = (args: any) => (
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum non rutrum augue, a dictum est. Sed ultricies vel orci in blandit. Morbi sed tempor leo. Phasellus et sollicitudin nunc, a volutpat est. In volutpat molestie velit, nec rhoncus felis vulputate porttitor. In efficitur nibh tortor, maximus imperdiet libero sollicitudin sed. Pellentesque dictum, quam id scelerisque rutrum, lorem augue suscipit est, nec ultricies ligula lorem id dui. Cras lacus tortor, fringilla nec ligula quis, semper imperdiet ex.

-
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum non rutrum augue, a dictum est. Sed ultricies vel orci in blandit. Morbi sed tempor leo. Phasellus et sollicitudin nunc, a volutpat est. In volutpat molestie velit, nec rhoncus felis vulputate porttitor. In efficitur nibh tortor, maximus imperdiet libero sollicitudin sed. Pellentesque dictum, quam id scelerisque rutrum, lorem augue suscipit est, nec ultricies ligula lorem id dui. Cras lacus tortor, fringilla nec ligula quis, semper imperdiet ex.

+
-
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut vulputate justo. Suspendisse potenti. Nunc id fringilla leo, at luctus quam. Maecenas et ipsum nisi. Curabitur in porta purus, a pretium est. Fusce eu urna diam. Sed nunc neque, consectetur ut purus nec, consequat elementum libero. Sed ut diam in quam maximus condimentum at non erat. Vestibulum sagittis rutrum velit, vitae suscipit arcu. Nulla ac feugiat ante, vitae laoreet ligula. Maecenas sed molestie ligula. Nulla sed fringilla ex. Nulla viverra tortor at enim condimentum egestas. Nulla sed tristique sapien. Integer ligula quam, vulputate eget mollis eu, interdum sit amet justo.

-

Vivamus dignissim tortor ut sapien congue tristique. Sed ac aliquet mauris. Nulla metus dui, elementum sit amet luctus eu, condimentum id elit. Praesent id nibh sed ligula congue venenatis. Pellentesque urna turpis, eleifend id pellentesque a, auctor nec neque. Vestibulum ipsum mauris, rutrum sit amet magna et, aliquet mollis tellus. Pellentesque nec ultricies nibh, at tempus massa. Phasellus dictum turpis et interdum scelerisque. Aliquam fermentum tincidunt ipsum sit amet suscipit. Fusce non dui sed diam lacinia mattis fermentum eu urna. Cras pretium id nunc in elementum. Mauris laoreet odio vitae laoreet dictum. In non justo nec nunc vehicula posuere non non ligula. Nullam eleifend scelerisque nibh, in sollicitudin tortor ullamcorper vel. Praesent sagittis risus in erat dignissim, non lacinia elit efficitur. Quisque maximus nulla vel luctus pharetra.

-
+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ut vulputate justo. Suspendisse potenti. Nunc id fringilla leo, at luctus quam. Maecenas et ipsum nisi. Curabitur in porta purus, a pretium est. Fusce eu urna diam. Sed nunc neque, consectetur ut purus nec, consequat elementum libero. Sed ut diam in quam maximus condimentum at non erat. Vestibulum sagittis rutrum velit, vitae suscipit arcu. Nulla ac feugiat ante, vitae laoreet ligula. Maecenas sed molestie ligula. Nulla sed fringilla ex. Nulla viverra tortor at enim condimentum egestas. Nulla sed tristique sapien. Integer ligula quam, vulputate eget mollis eu, interdum sit amet justo.

+

Vivamus dignissim tortor ut sapien congue tristique. Sed ac aliquet mauris. Nulla metus dui, elementum sit amet luctus eu, condimentum id elit. Praesent id nibh sed ligula congue venenatis. Pellentesque urna turpis, eleifend id pellentesque a, auctor nec neque. Vestibulum ipsum mauris, rutrum sit amet magna et, aliquet mollis tellus. Pellentesque nec ultricies nibh, at tempus massa. Phasellus dictum turpis et interdum scelerisque. Aliquam fermentum tincidunt ipsum sit amet suscipit. Fusce non dui sed diam lacinia mattis fermentum eu urna. Cras pretium id nunc in elementum. Mauris laoreet odio vitae laoreet dictum. In non justo nec nunc vehicula posuere non non ligula. Nullam eleifend scelerisque nibh, in sollicitudin tortor ullamcorper vel. Praesent sagittis risus in erat dignissim, non lacinia elit efficitur. Quisque maximus nulla vel luctus pharetra.

+
-
-

Alea jacta est.

+
+

Alea jacta est.

diff --git a/packages/dev/codemods/package.json b/packages/dev/codemods/package.json index bbbd292e91c..36bd00779e3 100644 --- a/packages/dev/codemods/package.json +++ b/packages/dev/codemods/package.json @@ -1,7 +1,6 @@ { "name": "@react-spectrum/codemods", "version": "0.0.1", - "private": true, "main": "dist/index.js", "source": "src/index.ts", "bin": "dist/index.js", diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/tabs.test.ts.snap b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/tabs.test.ts.snap index 4a845dc25b8..fc7da05e709 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/tabs.test.ts.snap +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/tabs.test.ts.snap @@ -1,8 +1,110 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Renames variants 1`] = ` -"// import {Button} from '@adobe/react-spectrum'; +exports[`Move items from Tabs to TabList 1`] = ` +"import { Collection, TabPanel, Tabs, TabList } from "@react-spectrum/s2"; +import { Item } from '@adobe/react-spectrum'; +let items = [ + {name: 'Tab 1', children: 'Tab Body 1'}, + {name: 'Tab 2', children: 'Tab Body 2'}, + {name: 'Tab 3', children: 'Tab Body 3'}, + {name: 'Tab 4', children: 'Tab Body 4'}, + {name: 'Tab 5', children: 'Tab Body 5'}, + {name: 'Tab 6', children: 'Tab Body 6'} +]; + + + {(item) => ( + + {item.name} + + )} + + {(item) => ( + + {item.children} + + )} + " +`; + +exports[`Remove isEmphasized 1`] = ` +"import { Tab, TabPanel, Tabs, TabList } from "@react-spectrum/s2"; +import { Item } from '@adobe/react-spectrum'; +let isEmphasized = true; +let props = {isEmphasized: true}; +
+ + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. +
" +`; + +exports[`Remove isQuiet 1`] = ` +"import { Tab, TabPanel, Tabs, TabList } from "@react-spectrum/s2"; +import { Item } from '@adobe/react-spectrum'; +let isQuiet = true; +let props = {isQuiet: true};
+ + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est. + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est.
" `; + +exports[`Update to use new API 1`] = ` +"import { Tab, TabPanel, Tabs, TabList } from "@react-spectrum/s2"; +import { Item } from '@adobe/react-spectrum'; + + + Founding of Rome + Monarchy and Republic + Empire + Arma virumque cano, Troiae qui primus ab oris.Senatus Populusque Romanus.Alea jacta est." +`; diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/tabs.test.ts b/packages/dev/codemods/src/s1-to-s2/__tests__/tabs.test.ts index a7b16598b27..409a0503106 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/tabs.test.ts +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/tabs.test.ts @@ -6,9 +6,251 @@ const test = (name: string, input: string) => { defineSnapshotTest(transform, {}, input, name); }; -test('Renames variants', ` -// import {Button} from '@adobe/react-spectrum'; +test('Update to use new API', ` +import {Tabs, TabList, TabPanels, Item} from '@adobe/react-spectrum'; + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + +`); + +test('Remove isEmphasized', ` +import {Tabs, TabList, TabPanels, Item} from '@adobe/react-spectrum'; +let isEmphasized = true; +let props = {isEmphasized: true}; +
+ + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + +
+`); + +test('Remove isQuiet', ` +import {Tabs, TabList, TabPanels, Item} from '@adobe/react-spectrum'; +let isQuiet = true; +let props = {isQuiet: true};
+ + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + + + + + Founding of Rome + Monarchy and Republic + Empire + + + + Arma virumque cano, Troiae qui primus ab oris. + + + Senatus Populusque Romanus. + + + Alea jacta est. + + +
`); + +test('Move items from Tabs to TabList', ` +import {Tabs, TabList, TabPanels, Item} from '@adobe/react-spectrum'; + +let items = [ + {name: 'Tab 1', children: 'Tab Body 1'}, + {name: 'Tab 2', children: 'Tab Body 2'}, + {name: 'Tab 3', children: 'Tab Body 3'}, + {name: 'Tab 4', children: 'Tab Body 4'}, + {name: 'Tab 5', children: 'Tab Body 5'}, + {name: 'Tab 6', children: 'Tab Body 6'} +]; + + + + {(item) => ( + + {item.name} + + )} + + + {(item) => ( + + {item.children} + + )} + + +`); 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 index 59404b136e9..8c92726c5b8 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/changes.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/changes.ts @@ -6,7 +6,6 @@ import { MovePropToParentComponentOptions, MoveRenderPropsOptions, RemoveComponentIfWithinParentOptions, - RemoveParentAndKeepChildrenOptions, RemovePropOptions, UpdateComponentIfPropPresentOptions, UpdateComponentWithinCollectionOptions, @@ -38,8 +37,8 @@ type FunctionInfo = args: UpdateComponentWithinCollectionOptions } | { - name: 'removeParentAndKeepChildren', - args: RemoveParentAndKeepChildrenOptions + name: 'updateTabs', + args: {} } | { name: 'movePropToNewChildComponent', @@ -576,29 +575,6 @@ export const changes: ChangesJSON = { // } // } // }, - // TODO: Not yet implemented in S2 - // { - // description: 'If within TabList, update Item to be a Tab', - // reason: 'Updated collections API', - // function: { - // name: 'updateComponentWithinCollection', - // args: { - // parentComponent: 'TabList', - // newComponent: 'Tab' - // } - // } - // }, - // { - // description: 'If within TabPanels, update Item to be a TabPanel', - // reason: 'Updated collections API', - // function: { - // name: 'updateComponentWithinCollection', - // args: { - // parentComponent: 'TabPanels', - // newComponent: 'TabPanel' - // } - // } - // }, ] }, Link: { @@ -965,21 +941,28 @@ export const changes: ChangesJSON = { } ] }, - // TODO: Not yet implemented in S2 - // Tabs: { - // changes: [ - // { - // description: 'Remove TabPanels components and keep individual TabPanel components inside.', - // reason: 'Updated collections API', - // function: { - // name: 'removeParentAndKeepChildren', - // args: { - // parentComponent: 'TabPanels' - // } - // } - // } - // ] - // }, + 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: [ { 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 index cc3545fe1ec..1bbb5d3c284 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/transforms.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/transforms.ts @@ -1,4 +1,4 @@ -import {addComment, addComponentImport, getName} from './utils'; +import {addComment, addComponentImport, getName, removeComponentImport} from './utils'; import {convertDimension} from './dimensions'; import {getComponents} from '../getComponents'; import {NodePath} from '@babel/traverse'; @@ -437,7 +437,7 @@ function updateComponentWithinCollection( // Collections currently implemented // TODO: Add 'ActionGroup', 'ListBox', 'ListView' once implemented - const collectionItemParents = new Set(['Menu', 'ActionMenu', 'TagGroup', 'Breadcrumbs', 'Picker', 'ComboBox', 'ListBox', 'TabList', 'TabPanels']); + const collectionItemParents = new Set(['Menu', 'ActionMenu', 'TagGroup', 'Breadcrumbs', 'Picker', 'ComboBox', 'ListBox', 'TabList', 'TabPanels', 'Collection']); if ( t.isJSXElement(path.node) && @@ -509,7 +509,7 @@ function updateComponentWithinCollection( function commentIfParentCollectionNotDetected( path: NodePath ) { - const collectionItemParents = new Set(['Menu', 'ActionMenu', 'TagGroup', 'Breadcrumbs', 'Picker', 'ComboBox', 'ListBox', 'TabList', 'TabPanels', 'ActionGroup', 'ListBox', 'ListView']); + const collectionItemParents = new Set(['Menu', 'ActionMenu', 'TagGroup', 'Breadcrumbs', 'Picker', 'ComboBox', 'ListBox', 'TabList', 'TabPanels', 'ActionGroup', 'ListBox', 'ListView', 'Collection']); if ( t.isJSXElement(path.node) ) { @@ -526,33 +526,95 @@ function commentIfParentCollectionNotDetected( } } -export interface RemoveParentAndKeepChildrenOptions { - parentComponent: string -} - /** - * Remove a parent component and keep its children. + * Updates Tabs to the new API. * * Example: * - Tabs: Remove TabPanels components and keep individual TabPanel components inside. */ -function removeParentAndKeepChildren( - path: NodePath, - options: RemoveParentAndKeepChildrenOptions +function updateTabs( + path: NodePath ) { - const {parentComponent} = options; + 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.traverse({ - JSXElement(path) { + 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(path.node) && - t.isJSXIdentifier(path.node.openingElement.name) && - getName(path, path.node.openingElement.name) === parentComponent + t.isJSXElement(itemPath.node) && + t.isJSXIdentifier(itemPath.node.openingElement.name) && + getName(itemPath as NodePath, itemPath.node.openingElement.name) === 'Item' ) { - path.replaceWithMultiple(path.node.children); + 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 { @@ -866,7 +928,7 @@ export const functionMap = { moveRenderPropsToChild, updateComponentWithinCollection, commentIfParentCollectionNotDetected, - removeParentAndKeepChildren, + updateTabs, movePropToNewChildComponent, movePropToParentComponent, updateToNewComponent, diff --git a/packages/dev/codemods/src/s1-to-s2/src/codemods/utils.ts b/packages/dev/codemods/src/s1-to-s2/src/codemods/utils.ts index 5336e5832ff..88365a5eefe 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/codemods/utils.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/codemods/utils.ts @@ -118,6 +118,25 @@ export function addComponentImport(path: NodePath, newComponent: stri return localName; } +export function removeComponentImport(path: NodePath, component: string) { + let existingImport = path.node.body.find((node) => t.isImportDeclaration(node) && node.source.value === '@adobe/react-spectrum' || t.isImportDeclaration(node) && node.source.value.startsWith('@react-spectrum/')); + if (existingImport && t.isImportDeclaration(existingImport)) { + let specifier = existingImport.specifiers.find((specifier) => { + return ( + t.isImportSpecifier(specifier) && + specifier.imported.type === 'Identifier' && + specifier.imported.name === component + ); + }); + if (specifier) { + existingImport.specifiers = existingImport.specifiers.filter((s) => s !== specifier); + if (existingImport.specifiers.length === 0) { + path.node.body = path.node.body.filter((node) => node !== existingImport); + } + } + } +} + /** * Look up the name in path.scope and find the original binding. * Returns the original name even if an alias is used. diff --git a/packages/dev/codemods/src/s1-to-s2/src/getComponents.ts b/packages/dev/codemods/src/s1-to-s2/src/getComponents.ts index c6910193deb..6b677b58881 100644 --- a/packages/dev/codemods/src/s1-to-s2/src/getComponents.ts +++ b/packages/dev/codemods/src/s1-to-s2/src/getComponents.ts @@ -7,7 +7,7 @@ export function getComponents(): Set { // Determine list of available components in S2 from index.ts let availableComponents = new Set(); const packagePath = require.resolve('@react-spectrum/s2'); - const indexPath = path.join(path.dirname(packagePath), 'src/index.ts'); + const indexPath = path.join(path.dirname(packagePath), process.env.NODE_ENV === 'test' ? 'src/index.ts' : '../src/index.js'); let index = parse(readFileSync(indexPath, 'utf8'), {sourceType: 'module', plugins: ['typescript']}); traverse(index, { ExportNamedDeclaration(path) { diff --git a/packages/dev/codemods/tsconfig.json b/packages/dev/codemods/tsconfig.json index c770947841a..400010f4b37 100644 --- a/packages/dev/codemods/tsconfig.json +++ b/packages/dev/codemods/tsconfig.json @@ -8,5 +8,5 @@ "moduleResolution": "node", }, "include": ["src/**/*"], - "exclude": ["node_modules"] -} \ No newline at end of file + "exclude": ["node_modules", "**/*.test.ts", "**/__mocks__"] +}