Skip to content

Commit 3652eb9

Browse files
sam-b-roselaurkim
andauthored
Add relative option for replace-text-component migration (#7529)
<!-- ☝️How to write a good PR title: - Prefix it with [ComponentName] (if applicable), for example: [Button] - Start with a verb, for example: Add, Delete, Improve, Fix… - Give as much context as necessary and as little as possible - Prefix it with [WIP] while it’s a work in progress --> ### WHY are these changes introduced? We'd like to run component migrations on `polaris-react` which uses relative component imports. <!-- Context about the problem that’s being addressed. --> ### WHAT is this pull request doing? This adds the ability to run the `replace-text-component` migration on relative paths: ```diff - import {DisplayText} from "../../DisplayText"; + import {Text} from "../../Text" ``` <!-- Summary of the changes committed. Before / after screenshots are appreciated for UI changes. Make sure to include alt text that describes the screenshot. If you include an animated gif showing your change, wrapping it in a details tag is recommended. Gifs usually autoplay, which can cause accessibility issues for people reviewing your PR: <details> <summary>Summary of your gif(s)</summary> <img src="..." alt="Description of what the gif shows"> </details> --> <!-- ℹ️ Delete the following for small / trivial changes --> ### How to 🎩 🖥 [Local development instructions](https://github.com/Shopify/polaris/blob/main/README.md#local-development) 🗒 [General tophatting guidelines](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting.md) 📄 [Changelog guidelines](https://github.com/Shopify/polaris/blob/main/.github/CONTRIBUTING.md#changelog) <!-- Give as much information as needed to experiment with the component in the playground. --> <details> <summary>Copy-paste this code in <code>playground/Playground.tsx</code>:</summary> ```jsx import React from 'react'; import {Page} from '../src'; export function Playground() { return ( <Page title="Playground"> {/* Add the code you want to test in here */} </Page> ); } ``` </details> ### 🎩 checklist - [ ] Tested on [mobile](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting.md#cross-browser-testing) - [ ] Tested on [multiple browsers](https://help.shopify.com/en/manual/shopify-admin/supported-browsers) - [ ] Tested for [accessibility](https://github.com/Shopify/polaris/blob/main/documentation/Accessibility%20testing.md) - [ ] Updated the component's `README.md` with documentation changes - [ ] [Tophatted documentation](https://github.com/Shopify/polaris/blob/main/documentation/Tophatting%20documentation.md) changes in the style guide Co-authored-by: Lo Kim <lo.kim@shopify.com>
1 parent 5678222 commit 3652eb9

File tree

10 files changed

+330
-52
lines changed

10 files changed

+330
-52
lines changed

.changeset/old-mayflies-clean.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/polaris-migrator': minor
3+
---
4+
5+
Add relative option for replace-text-component migration
Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
1-
import type {API, FileInfo} from 'jscodeshift';
1+
import type {API, FileInfo, Options} from 'jscodeshift';
22

33
import {hasImportDeclaration} from '../../utilities/imports';
44

55
import {replaceDisplayText} from './steps/replace-display-text';
66
import {replaceOther} from './steps/replace-other';
77
import {replaceTextStyle} from './steps/replace-text-style';
88

9+
export interface MigrationOptions extends Options {
10+
relative: boolean;
11+
}
12+
913
export default function replaceTextComponent(
1014
file: FileInfo,
1115
{jscodeshift: j}: API,
16+
options: MigrationOptions,
1217
) {
1318
const source = j(file.source);
1419

15-
if (!hasImportDeclaration(j, source, '@shopify/polaris')) {
20+
if (
21+
!options.relative &&
22+
!hasImportDeclaration(j, source, '@shopify/polaris')
23+
) {
1624
return file.source;
1725
}
1826

19-
replaceDisplayText(j, source);
20-
replaceOther(j, source);
21-
replaceTextStyle(j, source);
27+
replaceDisplayText(j, source, options);
28+
replaceOther(j, source, options);
29+
replaceTextStyle(j, source, options);
2230

2331
return source.toSource();
2432
}

polaris-migrator/src/migrations/replace-text-component/steps/replace-display-text.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ import {
77
insertJSXAttribute,
88
} from '../../../utilities/jsx';
99
import {
10-
renameImportSpecifier,
1110
getImportSpecifierName,
12-
hasImportSpecifier,
13-
removeImportSpecifier,
11+
normalizeImportSourcePaths,
12+
updateImports,
1413
} from '../../../utilities/imports';
14+
import type {MigrationOptions} from '../replace-text-component';
1515

1616
const displayTextSizeMap = {
17-
small: 'headingXl',
18-
medium: 'heading2xl',
19-
large: 'heading3xl',
17+
small: 'headingLg',
18+
medium: 'headingXl',
19+
large: 'heading2xl',
2020
extraLarge: 'heading4xl',
2121
};
2222

@@ -26,17 +26,27 @@ const displayTextSizeMap = {
2626
export function replaceDisplayText<NodeType = ASTNode>(
2727
j: JSCodeshift,
2828
source: Collection<NodeType>,
29+
options: MigrationOptions,
2930
) {
30-
if (hasImportSpecifier(j, source, 'Text', '@shopify/polaris')) {
31-
removeImportSpecifier(j, source, 'DisplayText', '@shopify/polaris');
32-
} else {
33-
renameImportSpecifier(j, source, 'DisplayText', 'Text', '@shopify/polaris');
34-
}
31+
const sourcePaths = normalizeImportSourcePaths(j, source, {
32+
relative: options.relative,
33+
from: 'DisplayText',
34+
to: 'Text',
35+
});
36+
37+
if (!sourcePaths) return;
3538

3639
const localElementName =
37-
getImportSpecifierName(j, source, 'DisplayText', '@shopify/polaris') ||
40+
getImportSpecifierName(j, source, 'DisplayText', sourcePaths.from) ||
3841
'DisplayText';
3942

43+
updateImports(j, source, {
44+
fromSpecifier: 'DisplayText',
45+
toSpecifier: 'Text',
46+
fromSourcePath: sourcePaths.from,
47+
toSourcePath: sourcePaths.to,
48+
});
49+
4050
source.findJSXElements(localElementName).forEach((element) => {
4151
replaceJSXElement(j, element, 'Text');
4252
replaceJSXAttributes(j, element, 'size', 'variant', displayTextSizeMap);

polaris-migrator/src/migrations/replace-text-component/steps/replace-other.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,19 @@ import {
77
insertJSXAttribute,
88
} from '../../../utilities/jsx';
99
import {
10-
renameImportSpecifier,
1110
getImportSpecifierName,
12-
hasImportSpecifier,
13-
removeImportSpecifier,
11+
normalizeImportSourcePaths,
12+
updateImports,
1413
} from '../../../utilities/imports';
14+
import type {MigrationOptions} from '../replace-text-component';
1515

1616
const components = {
1717
Heading: {
18-
variant: 'headingLg',
18+
variant: 'headingMd',
1919
as: 'h2',
2020
},
2121
Subheading: {
22-
variant: 'headingSm',
22+
variant: 'headingXs',
2323
as: 'h3',
2424
},
2525
Caption: {
@@ -38,24 +38,30 @@ const components = {
3838
export function replaceOther<NodeType = ASTNode>(
3939
j: JSCodeshift,
4040
source: Collection<NodeType>,
41+
options: MigrationOptions,
4142
) {
43+
const relative = options.relative;
44+
4245
Object.entries(components).forEach(([componentName, {variant, as}]) => {
43-
if (hasImportSpecifier(j, source, 'Text', '@shopify/polaris')) {
44-
removeImportSpecifier(j, source, componentName, '@shopify/polaris');
45-
} else {
46-
renameImportSpecifier(
47-
j,
48-
source,
49-
componentName,
50-
'Text',
51-
'@shopify/polaris',
52-
);
53-
}
46+
const sourcePaths = normalizeImportSourcePaths(j, source, {
47+
relative,
48+
from: componentName,
49+
to: 'Text',
50+
});
51+
52+
if (!sourcePaths) return;
5453

5554
const localElementName =
56-
getImportSpecifierName(j, source, componentName, '@shopify/polaris') ||
55+
getImportSpecifierName(j, source, componentName, sourcePaths.from) ||
5756
componentName;
5857

58+
updateImports(j, source, {
59+
fromSpecifier: componentName,
60+
toSpecifier: 'Text',
61+
fromSourcePath: sourcePaths.from,
62+
toSourcePath: sourcePaths.to,
63+
});
64+
5965
source.findJSXElements(localElementName).forEach((element) => {
6066
replaceJSXElement(j, element, 'Text');
6167
insertJSXAttribute(j, element, 'variant', variant);

polaris-migrator/src/migrations/replace-text-component/steps/replace-text-style.ts

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import {
1010
} from '../../../utilities/jsx';
1111
import {
1212
insertImportSpecifier,
13-
renameImportSpecifier,
1413
getImportSpecifierName,
1514
hasImportSpecifier,
16-
removeImportSpecifier,
15+
insertImportDeclaration,
16+
normalizeImportSourcePaths,
17+
updateImports,
1718
} from '../../../utilities/imports';
19+
import type {MigrationOptions} from '../replace-text-component';
1820

1921
const variationMap = {
2022
strong: {fontWeight: 'bold'},
@@ -31,17 +33,27 @@ const variationMap = {
3133
export function replaceTextStyle<NodeType = ASTNode>(
3234
j: JSCodeshift,
3335
source: Collection<NodeType>,
36+
options: MigrationOptions,
3437
) {
35-
if (hasImportSpecifier(j, source, 'Text', '@shopify/polaris')) {
36-
removeImportSpecifier(j, source, 'TextStyle', '@shopify/polaris');
37-
} else {
38-
renameImportSpecifier(j, source, 'TextStyle', 'Text', '@shopify/polaris');
39-
}
38+
const sourcePaths = normalizeImportSourcePaths(j, source, {
39+
relative: options.relative,
40+
from: 'TextStyle',
41+
to: 'Text',
42+
});
43+
44+
if (!sourcePaths) return;
4045

4146
const localElementName =
42-
getImportSpecifierName(j, source, 'TextStyle', '@shopify/polaris') ||
47+
getImportSpecifierName(j, source, 'TextStyle', sourcePaths.from) ||
4348
'TextStyle';
4449

50+
updateImports(j, source, {
51+
fromSpecifier: 'TextStyle',
52+
toSpecifier: 'Text',
53+
fromSourcePath: sourcePaths.from,
54+
toSourcePath: sourcePaths.to,
55+
});
56+
4557
source.findJSXElements(localElementName).forEach((element) => {
4658
replaceJSXElement(j, element, 'Text');
4759
insertJSXAttribute(j, element, 'variant', 'bodyMd');
@@ -50,10 +62,29 @@ export function replaceTextStyle<NodeType = ASTNode>(
5062
.forEach((literal) => {
5163
const currentValue = literal.node.value as keyof typeof variationMap;
5264
if (currentValue === 'code') {
65+
const inlineTextSourcePath = options.relative
66+
? sourcePaths.from.replace('TextStyle', 'InlineCode')
67+
: '@shopify/polaris';
68+
5369
if (
54-
!hasImportSpecifier(j, source, 'InlineCode', '@shopify/polaris')
70+
!hasImportSpecifier(j, source, 'InlineCode', inlineTextSourcePath)
5571
) {
56-
insertImportSpecifier(j, source, 'InlineCode', '@shopify/polaris');
72+
if (options.relative) {
73+
insertImportDeclaration(
74+
j,
75+
source,
76+
'InlineCode',
77+
inlineTextSourcePath,
78+
sourcePaths.to,
79+
);
80+
} else {
81+
insertImportSpecifier(
82+
j,
83+
source,
84+
'InlineCode',
85+
inlineTextSourcePath,
86+
);
87+
}
5788
}
5889

5990
const InlineCode = j.jsxElement(
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// @ts-nocheck
2+
import React from 'react';
3+
4+
import {DisplayText} from '../DisplayText';
5+
import {Heading} from '../Heading';
6+
import {Subheading} from '../Subheading';
7+
import {Caption} from '../Caption';
8+
import {TextStyle} from '../TextStyle';
9+
import {VisuallyHidden} from '../VisuallyHidden';
10+
11+
export function App() {
12+
return (
13+
<>
14+
<DisplayText size="extraLarge">Display text</DisplayText>
15+
<DisplayText size="large">Display text</DisplayText>
16+
<DisplayText size="medium">Display text</DisplayText>
17+
<DisplayText size="small">Display text</DisplayText>
18+
<Heading element="h1">Heading</Heading>
19+
<Heading>Heading</Heading>
20+
<Subheading element="h2">Subheading</Subheading>
21+
<Subheading>Subheading</Subheading>
22+
<Caption>Caption</Caption>
23+
<TextStyle variation="strong">Strong</TextStyle>
24+
<TextStyle variation="positive">Positive</TextStyle>
25+
<TextStyle variation="negative">Negative</TextStyle>
26+
<TextStyle variation="warning">Warning</TextStyle>
27+
<TextStyle variation="code">Code</TextStyle>
28+
<VisuallyHidden>Hidden text</VisuallyHidden>
29+
</>
30+
);
31+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// @ts-nocheck
2+
import React from 'react';
3+
4+
import {Text} from '../Text';
5+
import {InlineCode} from '../InlineCode';
6+
7+
export function App() {
8+
return (
9+
<>
10+
<Text variant="heading4xl" as="p">
11+
Display text
12+
</Text>
13+
<Text variant="heading2xl" as="p">
14+
Display text
15+
</Text>
16+
<Text variant="headingXl" as="p">
17+
Display text
18+
</Text>
19+
<Text variant="headingLg" as="p">
20+
Display text
21+
</Text>
22+
<Text as="h1" variant="headingMd">
23+
Heading
24+
</Text>
25+
<Text variant="headingMd" as="h2">
26+
Heading
27+
</Text>
28+
<Text as="h2" variant="headingXs">
29+
Subheading
30+
</Text>
31+
<Text variant="headingXs" as="h3">
32+
Subheading
33+
</Text>
34+
<Text variant="bodySm" as="p">
35+
Caption
36+
</Text>
37+
<Text variant="bodyMd" fontWeight="bold" as="span">
38+
Strong
39+
</Text>
40+
<Text variant="bodyMd" color="success" as="span">
41+
Positive
42+
</Text>
43+
<Text variant="bodyMd" color="critical" as="span">
44+
Negative
45+
</Text>
46+
<Text variant="bodyMd" color="warning" as="span">
47+
Warning
48+
</Text>
49+
<Text variant="bodyMd" as="span">
50+
<InlineCode>Code</InlineCode>
51+
</Text>
52+
<Text variant="bodySm" as="span" visuallyHidden>
53+
Hidden text
54+
</Text>
55+
</>
56+
);
57+
}

polaris-migrator/src/migrations/replace-text-component/tests/replace-components.output.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ export function App() {
77
<Text variant="heading4xl" as="p">
88
Display text
99
</Text>
10-
<Text variant="heading3xl" as="p">
11-
Display text
12-
</Text>
1310
<Text variant="heading2xl" as="p">
1411
Display text
1512
</Text>
1613
<Text variant="headingXl" as="p">
1714
Display text
1815
</Text>
16+
<Text variant="headingLg" as="p">
17+
Display text
18+
</Text>
1919
<Text as="h1" variant="headingLg">
2020
Heading
2121
</Text>
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import {check} from '../../../utilities/testUtils';
22

33
const migration = 'replace-text-component';
4-
const fixtures = ['replace-components'];
4+
const fixtures = ['replace-components-relative'];
55

66
for (const fixture of fixtures) {
77
check(__dirname, {
88
fixture,
99
migration,
10+
options: {
11+
relative: fixture.includes('relative') ? true : undefined,
12+
},
1013
});
1114
}

0 commit comments

Comments
 (0)