From 2e689d991555f90cd5854a9a6a529078e4c04ab1 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 21 Aug 2024 16:17:01 -0500 Subject: [PATCH] feat: Codemod: handle legacy Link API (#6907) * handle legacy Link API by removing a element * handle custom link components --- .storybook-s2/docs/Migrating.jsx | 1 + .../__tests__/__snapshots__/link.test.ts.snap | 18 +++++++++++ .../src/s1-to-s2/__tests__/link.test.ts | 25 +++++++++++++++ .../src/s1-to-s2/src/codemods/changes.ts | 12 +++++++ .../src/s1-to-s2/src/codemods/transforms.ts | 31 ++++++++++++++++++- 5 files changed, 86 insertions(+), 1 deletion(-) diff --git a/.storybook-s2/docs/Migrating.jsx b/.storybook-s2/docs/Migrating.jsx index 9c961ef5d72..3f77a7b4307 100644 --- a/.storybook-s2/docs/Migrating.jsx +++ b/.storybook-s2/docs/Migrating.jsx @@ -186,6 +186,7 @@ export function Migrating() {

Link

ListBox

diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/link.test.ts.snap b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/link.test.ts.snap index 9211c3ffc39..bd7775329c2 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/link.test.ts.snap +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/link.test.ts.snap @@ -21,3 +21,21 @@ let props = {variant: 'overBackground'}; " `; + +exports[`Leaves comment if inner link element is a custom router link (deprecated API) 1`] = ` +"import { Link } from "@react-spectrum/s2"; +import { Link as RouterLink } from "react-router-dom"; + +
+ // TODO(S2-upgrade): You may have been using a custom link component here. You'll need to update this manually. + The missing link. +
" +`; + +exports[`Remove inner anchor element (deprecated API) 1`] = ` +"import { Link } from "@react-spectrum/s2"; + +
+ The missing link. +
" +`; diff --git a/packages/dev/codemods/src/s1-to-s2/__tests__/link.test.ts b/packages/dev/codemods/src/s1-to-s2/__tests__/link.test.ts index cee8e616c4a..c9681aabc8c 100644 --- a/packages/dev/codemods/src/s1-to-s2/__tests__/link.test.ts +++ b/packages/dev/codemods/src/s1-to-s2/__tests__/link.test.ts @@ -25,3 +25,28 @@ let props = {variant: 'overBackground'}; `); + +test('Remove inner anchor element (deprecated API)', ` +import {Link} from '@adobe/react-spectrum'; + +
+ + + The missing link. + + +
+`); + +test('Leaves comment if inner link element is a custom router link (deprecated API)', ` +import {Link} from '@adobe/react-spectrum'; +import { Link as RouterLink } from "react-router-dom"; + +
+ + + The missing link. + + +
+`); 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 f7786e9bba4..54dd447d9da 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 @@ -71,6 +71,10 @@ type FunctionInfo = | { name: 'updateAvatarSize', args: {} + } + | { + name: 'updateLegacyLink', + args: {} }; type Change = { @@ -591,6 +595,14 @@ export const changes: ChangesJSON = { newValue: 'white' } } + }, + { + description: 'Remove inner anchor element if used (legacy API)', + reason: 'Updated API', + function: { + name: 'updateLegacyLink', + args: {} + } } ] }, 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 9822bfaff89..0e97f6c8b15 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 @@ -918,6 +918,34 @@ function updateAvatarSize( } } +/** + * 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; + } + } +} + export const functionMap = { updatePropNameAndValue, updatePropValueAndAddNewProp, @@ -936,5 +964,6 @@ export const functionMap = { convertDimensionValueToPx, updatePlacementToSingleValue, removeComponentIfWithinParent, - updateAvatarSize + updateAvatarSize, + updateLegacyLink };