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
Change variant="overBackground" to staticColor="white"
+
If a was used inside Link (legacy API), remove the a and apply props (i.e href) directly to 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';
+
+
+`);
+
+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
};