@planningcenter/tapestry-migration-cli 2.4.0-rc.11 → 2.4.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/components/button/index.ts +2 -0
- package/src/components/button/transforms/innerRefToRef.test.ts +170 -0
- package/src/components/button/transforms/innerRefToRef.ts +14 -0
- package/src/components/link/index.ts +4 -0
- package/src/components/link/transforms/innerRefToRef.test.ts +170 -0
- package/src/components/link/transforms/innerRefToRef.ts +14 -0
- package/src/components/link/transforms/tooltipToWrapper.test.ts +392 -0
- package/src/components/link/transforms/tooltipToWrapper.ts +35 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/tapestry-migration-cli",
|
|
3
|
-
"version": "2.4.0-rc.
|
|
3
|
+
"version": "2.4.0-rc.13",
|
|
4
4
|
"description": "CLI tool for Tapestry migrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "275d9583901bea8ef0d121ef25eff0a1fdf309b4"
|
|
55
55
|
}
|
|
@@ -8,6 +8,7 @@ import convertStyleProps from "./transforms/convertStyleProps"
|
|
|
8
8
|
import iconLeftToPrefix from "./transforms/iconLeftToPrefix"
|
|
9
9
|
import iconRightToSuffix from "./transforms/iconRightToSuffix"
|
|
10
10
|
import iconToIconButton from "./transforms/iconToIconButton"
|
|
11
|
+
import innerRefToRef from "./transforms/innerRefToRef"
|
|
11
12
|
import linkToButton from "./transforms/linkToButton"
|
|
12
13
|
import moveButtonImport from "./transforms/moveButtonImport"
|
|
13
14
|
import removeAsButton from "./transforms/removeAsButton"
|
|
@@ -55,6 +56,7 @@ const transform: Transform = async (fileInfo, api, options) => {
|
|
|
55
56
|
themeVariantToKind,
|
|
56
57
|
titleToLabel,
|
|
57
58
|
childrenToLabel,
|
|
59
|
+
innerRefToRef,
|
|
58
60
|
removeToTransform,
|
|
59
61
|
removeDuplicateKeys,
|
|
60
62
|
removeAsButton,
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./innerRefToRef"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string | null {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
return transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("innerRefToRef transform", () => {
|
|
18
|
+
describe("basic transformations", () => {
|
|
19
|
+
it("should transform Button 'innerRef' prop to 'ref' prop", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
export default function Test() {
|
|
24
|
+
return <Button innerRef={buttonRef}>Click me</Button>
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const expected = `
|
|
29
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
30
|
+
|
|
31
|
+
export default function Test() {
|
|
32
|
+
return <Button ref={buttonRef}>Click me</Button>;
|
|
33
|
+
}
|
|
34
|
+
`.trim()
|
|
35
|
+
|
|
36
|
+
const result = applyTransform(input)
|
|
37
|
+
expect(result).toBe(expected)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("should transform Button 'innerRef' prop to 'ref' prop with function", () => {
|
|
41
|
+
const input = `
|
|
42
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
43
|
+
|
|
44
|
+
export default function Test() {
|
|
45
|
+
return <Button innerRef={(el) => setButtonRef(el)}>Click me</Button>
|
|
46
|
+
}
|
|
47
|
+
`.trim()
|
|
48
|
+
|
|
49
|
+
const expected = `
|
|
50
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
51
|
+
|
|
52
|
+
export default function Test() {
|
|
53
|
+
return <Button ref={(el) => setButtonRef(el)}>Click me</Button>;
|
|
54
|
+
}
|
|
55
|
+
`.trim()
|
|
56
|
+
|
|
57
|
+
const result = applyTransform(input)
|
|
58
|
+
expect(result).toBe(expected)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("should transform Button 'innerRef' prop to 'ref' prop with useRef", () => {
|
|
62
|
+
const input = `
|
|
63
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
64
|
+
import { useRef } from "react"
|
|
65
|
+
|
|
66
|
+
export default function Test() {
|
|
67
|
+
const buttonRef = useRef(null)
|
|
68
|
+
return <Button innerRef={buttonRef}>Click me</Button>
|
|
69
|
+
}
|
|
70
|
+
`.trim()
|
|
71
|
+
|
|
72
|
+
const expected = `
|
|
73
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
74
|
+
import { useRef } from "react"
|
|
75
|
+
|
|
76
|
+
export default function Test() {
|
|
77
|
+
const buttonRef = useRef(null)
|
|
78
|
+
return <Button ref={buttonRef}>Click me</Button>;
|
|
79
|
+
}
|
|
80
|
+
`.trim()
|
|
81
|
+
|
|
82
|
+
const result = applyTransform(input)
|
|
83
|
+
expect(result).toBe(expected)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
describe("multiple components", () => {
|
|
88
|
+
it("should transform multiple Button components with innerRef", () => {
|
|
89
|
+
const input = `
|
|
90
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
91
|
+
|
|
92
|
+
export default function Test() {
|
|
93
|
+
return (
|
|
94
|
+
<div>
|
|
95
|
+
<Button innerRef={buttonRef1}>First</Button>
|
|
96
|
+
<Button innerRef={buttonRef2}>Second</Button>
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
`.trim()
|
|
101
|
+
|
|
102
|
+
const expected = `
|
|
103
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
104
|
+
|
|
105
|
+
export default function Test() {
|
|
106
|
+
return (
|
|
107
|
+
<div>
|
|
108
|
+
<Button ref={buttonRef1}>First</Button>
|
|
109
|
+
<Button ref={buttonRef2}>Second</Button>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
`.trim()
|
|
114
|
+
|
|
115
|
+
const result = applyTransform(input)
|
|
116
|
+
expect(result).toBe(expected)
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
describe("edge cases", () => {
|
|
121
|
+
it("should not transform Button without innerRef", () => {
|
|
122
|
+
const input = `
|
|
123
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
124
|
+
|
|
125
|
+
export default function Test() {
|
|
126
|
+
return <Button onClick={handleClick}>Click me</Button>
|
|
127
|
+
}
|
|
128
|
+
`.trim()
|
|
129
|
+
|
|
130
|
+
const result = applyTransform(input)
|
|
131
|
+
expect(result).toBe(null)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it("should not transform other components with innerRef", () => {
|
|
135
|
+
const input = `
|
|
136
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
137
|
+
|
|
138
|
+
export default function Test() {
|
|
139
|
+
return <Link innerRef={linkRef}>Go somewhere</Link>
|
|
140
|
+
}
|
|
141
|
+
`.trim()
|
|
142
|
+
|
|
143
|
+
const result = applyTransform(input)
|
|
144
|
+
expect(result).toBe(null)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it("should preserve other props when transforming innerRef", () => {
|
|
148
|
+
const input = `
|
|
149
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
150
|
+
|
|
151
|
+
export default function Test() {
|
|
152
|
+
const buttonRef = null
|
|
153
|
+
return <Button innerRef={buttonRef} onClick={() => {}} disabled>Click me</Button>
|
|
154
|
+
}
|
|
155
|
+
`.trim()
|
|
156
|
+
|
|
157
|
+
const expected = `
|
|
158
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
159
|
+
|
|
160
|
+
export default function Test() {
|
|
161
|
+
const buttonRef = null
|
|
162
|
+
return <Button ref={buttonRef} onClick={() => {}} disabled>Click me</Button>;
|
|
163
|
+
}
|
|
164
|
+
`.trim()
|
|
165
|
+
|
|
166
|
+
const result = applyTransform(input)
|
|
167
|
+
expect(result).toBe(expected)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { transformAttributeName } from "../../shared/actions/transformAttributeName"
|
|
2
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
3
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
4
|
+
|
|
5
|
+
const transform = attributeTransformFactory({
|
|
6
|
+
condition: hasAttribute("innerRef"),
|
|
7
|
+
targetComponent: "Button",
|
|
8
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
+
transform: (element) => {
|
|
10
|
+
return transformAttributeName("innerRef", "ref", { element })
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default transform
|
|
@@ -4,10 +4,12 @@ import childrenToLabel from "./transforms/childrenToLabel"
|
|
|
4
4
|
import convertStyleProps from "./transforms/convertStyleProps"
|
|
5
5
|
import inlineMemberToKind from "./transforms/inlineMemberToKind"
|
|
6
6
|
import inlinePropToKind from "./transforms/inlinePropToKind"
|
|
7
|
+
import innerRefToRef from "./transforms/innerRefToRef"
|
|
7
8
|
import moveLinkImport from "./transforms/moveLinkImport"
|
|
8
9
|
import removeAs from "./transforms/removeAs"
|
|
9
10
|
import reviewStyles from "./transforms/reviewStyles"
|
|
10
11
|
import targetBlankToExternal from "./transforms/targetBlankToExternal"
|
|
12
|
+
import tooltipToWrapper from "./transforms/tooltipToWrapper"
|
|
11
13
|
import toToHref from "./transforms/toToHref"
|
|
12
14
|
import unsupportedProps from "./transforms/unsupportedProps"
|
|
13
15
|
|
|
@@ -18,8 +20,10 @@ const transform: Transform = (fileInfo, api, options) => {
|
|
|
18
20
|
const transforms: Transform[] = [
|
|
19
21
|
inlineMemberToKind,
|
|
20
22
|
inlinePropToKind,
|
|
23
|
+
tooltipToWrapper,
|
|
21
24
|
toToHref,
|
|
22
25
|
targetBlankToExternal,
|
|
26
|
+
innerRefToRef,
|
|
23
27
|
removeAs,
|
|
24
28
|
childrenToLabel,
|
|
25
29
|
reviewStyles,
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./innerRefToRef"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string | null {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
return transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("innerRefToRef transform", () => {
|
|
18
|
+
describe("basic transformations", () => {
|
|
19
|
+
it("should transform Link 'innerRef' prop to 'ref' prop", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
export default function Test() {
|
|
24
|
+
return <Link innerRef={linkRef}>Go somewhere</Link>
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const expected = `
|
|
29
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
30
|
+
|
|
31
|
+
export default function Test() {
|
|
32
|
+
return <Link ref={linkRef}>Go somewhere</Link>;
|
|
33
|
+
}
|
|
34
|
+
`.trim()
|
|
35
|
+
|
|
36
|
+
const result = applyTransform(input)
|
|
37
|
+
expect(result).toBe(expected)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("should transform Link 'innerRef' prop to 'ref' prop with function", () => {
|
|
41
|
+
const input = `
|
|
42
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
43
|
+
|
|
44
|
+
export default function Test() {
|
|
45
|
+
return <Link innerRef={(el) => setLinkRef(el)}>Go somewhere</Link>
|
|
46
|
+
}
|
|
47
|
+
`.trim()
|
|
48
|
+
|
|
49
|
+
const expected = `
|
|
50
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
51
|
+
|
|
52
|
+
export default function Test() {
|
|
53
|
+
return <Link ref={(el) => setLinkRef(el)}>Go somewhere</Link>;
|
|
54
|
+
}
|
|
55
|
+
`.trim()
|
|
56
|
+
|
|
57
|
+
const result = applyTransform(input)
|
|
58
|
+
expect(result).toBe(expected)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("should transform Link 'innerRef' prop to 'ref' prop with useRef", () => {
|
|
62
|
+
const input = `
|
|
63
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
64
|
+
import { useRef } from "react"
|
|
65
|
+
|
|
66
|
+
export default function Test() {
|
|
67
|
+
const linkRef = useRef(null)
|
|
68
|
+
return <Link innerRef={linkRef}>Go somewhere</Link>
|
|
69
|
+
}
|
|
70
|
+
`.trim()
|
|
71
|
+
|
|
72
|
+
const expected = `
|
|
73
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
74
|
+
import { useRef } from "react"
|
|
75
|
+
|
|
76
|
+
export default function Test() {
|
|
77
|
+
const linkRef = useRef(null)
|
|
78
|
+
return <Link ref={linkRef}>Go somewhere</Link>;
|
|
79
|
+
}
|
|
80
|
+
`.trim()
|
|
81
|
+
|
|
82
|
+
const result = applyTransform(input)
|
|
83
|
+
expect(result).toBe(expected)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
describe("multiple components", () => {
|
|
88
|
+
it("should transform multiple Link components with innerRef", () => {
|
|
89
|
+
const input = `
|
|
90
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
91
|
+
|
|
92
|
+
export default function Test() {
|
|
93
|
+
return (
|
|
94
|
+
<div>
|
|
95
|
+
<Link innerRef={linkRef1}>First</Link>
|
|
96
|
+
<Link innerRef={linkRef2}>Second</Link>
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
`.trim()
|
|
101
|
+
|
|
102
|
+
const expected = `
|
|
103
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
104
|
+
|
|
105
|
+
export default function Test() {
|
|
106
|
+
return (
|
|
107
|
+
<div>
|
|
108
|
+
<Link ref={linkRef1}>First</Link>
|
|
109
|
+
<Link ref={linkRef2}>Second</Link>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
`.trim()
|
|
114
|
+
|
|
115
|
+
const result = applyTransform(input)
|
|
116
|
+
expect(result).toBe(expected)
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
describe("edge cases", () => {
|
|
121
|
+
it("should not transform Link without innerRef", () => {
|
|
122
|
+
const input = `
|
|
123
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
124
|
+
|
|
125
|
+
export default function Test() {
|
|
126
|
+
return <Link href="/dashboard">Go to dashboard</Link>
|
|
127
|
+
}
|
|
128
|
+
`.trim()
|
|
129
|
+
|
|
130
|
+
const result = applyTransform(input)
|
|
131
|
+
expect(result).toBe(null)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it("should not transform other components with innerRef", () => {
|
|
135
|
+
const input = `
|
|
136
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
137
|
+
|
|
138
|
+
export default function Test() {
|
|
139
|
+
return <Button innerRef={buttonRef}>Click me</Button>
|
|
140
|
+
}
|
|
141
|
+
`.trim()
|
|
142
|
+
|
|
143
|
+
const result = applyTransform(input)
|
|
144
|
+
expect(result).toBe(null)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it("should preserve other props when transforming innerRef", () => {
|
|
148
|
+
const input = `
|
|
149
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
150
|
+
|
|
151
|
+
export default function Test() {
|
|
152
|
+
const linkRef = null
|
|
153
|
+
return <Link innerRef={linkRef} href="/dashboard" onClick={() => {}}>Go to dashboard</Link>
|
|
154
|
+
}
|
|
155
|
+
`.trim()
|
|
156
|
+
|
|
157
|
+
const expected = `
|
|
158
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
159
|
+
|
|
160
|
+
export default function Test() {
|
|
161
|
+
const linkRef = null
|
|
162
|
+
return <Link ref={linkRef} href="/dashboard" onClick={() => {}}>Go to dashboard</Link>;
|
|
163
|
+
}
|
|
164
|
+
`.trim()
|
|
165
|
+
|
|
166
|
+
const result = applyTransform(input)
|
|
167
|
+
expect(result).toBe(expected)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { transformAttributeName } from "../../shared/actions/transformAttributeName"
|
|
2
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
3
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
4
|
+
|
|
5
|
+
const transform = attributeTransformFactory({
|
|
6
|
+
condition: hasAttribute("innerRef"),
|
|
7
|
+
targetComponent: "Link",
|
|
8
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
+
transform: (element) => {
|
|
10
|
+
return transformAttributeName("innerRef", "ref", { element })
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default transform
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./tooltipToWrapper"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string | null {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
return transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("tooltipToWrapper transform", () => {
|
|
18
|
+
describe("basic transformations", () => {
|
|
19
|
+
it("should wrap Link with tooltip string in Tooltip component", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
export default function Test() {
|
|
24
|
+
return <Link href="/save" tooltip={{ title: "Save this item" }}>Save</Link>
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(input)
|
|
29
|
+
|
|
30
|
+
expect(result).toContain(
|
|
31
|
+
'import { Link, Tooltip } from "@planningcenter/tapestry-react"'
|
|
32
|
+
)
|
|
33
|
+
expect(result).toContain('<Tooltip {...{ title: "Save this item" }}>')
|
|
34
|
+
expect(result).toContain('<Link href="/save">Save</Link>')
|
|
35
|
+
expect(result).toContain("</Tooltip>")
|
|
36
|
+
expect(result).not.toContain("tooltip=")
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it("should handle tooltip with JSX expression containing string literal", () => {
|
|
40
|
+
const input = `
|
|
41
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
42
|
+
|
|
43
|
+
export default function Test() {
|
|
44
|
+
return <Link href="/save" tooltip={{ title: "Save this item" }}>Save</Link>
|
|
45
|
+
}
|
|
46
|
+
`.trim()
|
|
47
|
+
|
|
48
|
+
const result = applyTransform(input)
|
|
49
|
+
|
|
50
|
+
expect(result).toContain('<Tooltip {...{ title: "Save this item" }}>')
|
|
51
|
+
expect(result).toContain('<Link href="/save">Save</Link>')
|
|
52
|
+
expect(result).not.toContain("tooltip=")
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it("should handle tooltip with object expression as spread", () => {
|
|
56
|
+
const input = `
|
|
57
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
58
|
+
|
|
59
|
+
export default function Test() {
|
|
60
|
+
return <Link href="/save" tooltip={{text: "Save", placement: "top"}}>Save</Link>
|
|
61
|
+
}
|
|
62
|
+
`.trim()
|
|
63
|
+
|
|
64
|
+
const result = applyTransform(input)
|
|
65
|
+
|
|
66
|
+
expect(result).toContain(
|
|
67
|
+
'<Tooltip {...{text: "Save", placement: "top"}}>'
|
|
68
|
+
)
|
|
69
|
+
expect(result).toContain('<Link href="/save">Save</Link>')
|
|
70
|
+
expect(result).not.toContain("tooltip=")
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it("should preserve other Link attributes", () => {
|
|
74
|
+
const input = `
|
|
75
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
76
|
+
|
|
77
|
+
export default function Test() {
|
|
78
|
+
return <Link href="/help" tooltip={{title: "Help"}} kind="primary" onClick={handler}>Help</Link>
|
|
79
|
+
}
|
|
80
|
+
`.trim()
|
|
81
|
+
|
|
82
|
+
const result = applyTransform(input)
|
|
83
|
+
|
|
84
|
+
expect(result).toContain('<Tooltip {...{title: "Help"}}>')
|
|
85
|
+
expect(result).toContain(
|
|
86
|
+
'<Link href="/help" kind="primary" onClick={handler}>Help</Link>'
|
|
87
|
+
)
|
|
88
|
+
expect(result).not.toContain("tooltip=")
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it("should handle self-closing Link with tooltip", () => {
|
|
92
|
+
const input = `
|
|
93
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
94
|
+
|
|
95
|
+
export default function Test() {
|
|
96
|
+
return <Link href="/icon" tooltip={{title: "Icon link"}} />
|
|
97
|
+
}
|
|
98
|
+
`.trim()
|
|
99
|
+
|
|
100
|
+
const result = applyTransform(input)
|
|
101
|
+
|
|
102
|
+
expect(result).toContain('<Tooltip {...{title: "Icon link"}}>')
|
|
103
|
+
expect(result).toContain('<Link href="/icon" />')
|
|
104
|
+
expect(result).toContain("</Tooltip>")
|
|
105
|
+
expect(result).not.toContain("tooltip=")
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe("import management", () => {
|
|
110
|
+
it("should add Tooltip to existing tapestry-react import", () => {
|
|
111
|
+
const input = `
|
|
112
|
+
import { Link, Button } from "@planningcenter/tapestry-react"
|
|
113
|
+
|
|
114
|
+
export default function Test() {
|
|
115
|
+
return <Link href="/save" tooltip={{title: "Save"}}>Save</Link>
|
|
116
|
+
}
|
|
117
|
+
`.trim()
|
|
118
|
+
|
|
119
|
+
const result = applyTransform(input)
|
|
120
|
+
|
|
121
|
+
expect(result).toContain(
|
|
122
|
+
'import { Link, Button, Tooltip } from "@planningcenter/tapestry-react"'
|
|
123
|
+
)
|
|
124
|
+
expect(result).toContain('<Tooltip {...{title: "Save"}}>')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it("should not add duplicate Tooltip import", () => {
|
|
128
|
+
const input = `
|
|
129
|
+
import { Link, Tooltip } from "@planningcenter/tapestry-react"
|
|
130
|
+
|
|
131
|
+
export default function Test() {
|
|
132
|
+
return <Link href="/save" tooltip={{title: "Save"}}>Save</Link>
|
|
133
|
+
}
|
|
134
|
+
`.trim()
|
|
135
|
+
|
|
136
|
+
const result = applyTransform(input)
|
|
137
|
+
|
|
138
|
+
const tooltipImports = (result?.match(/import.*Tooltip.*from/g) || [])
|
|
139
|
+
.length
|
|
140
|
+
expect(tooltipImports).toBe(1)
|
|
141
|
+
expect(result).toContain(
|
|
142
|
+
'import { Link, Tooltip } from "@planningcenter/tapestry-react"'
|
|
143
|
+
)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it("should create new tapestry-react import when none exists", () => {
|
|
147
|
+
const input = `
|
|
148
|
+
import React from "react"
|
|
149
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
150
|
+
|
|
151
|
+
export default function Test() {
|
|
152
|
+
return <Link href="/save" tooltip={{title: "Save"}}>Save</Link>
|
|
153
|
+
}
|
|
154
|
+
`.trim()
|
|
155
|
+
|
|
156
|
+
const result = applyTransform(input)
|
|
157
|
+
|
|
158
|
+
expect(result).toContain(
|
|
159
|
+
'import { Link, Tooltip } from "@planningcenter/tapestry-react"'
|
|
160
|
+
)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it("should handle empty imports scenario", () => {
|
|
164
|
+
const input = `
|
|
165
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
166
|
+
export default function Test() {
|
|
167
|
+
return <Link href="/save" tooltip={{title: "Save"}}>Save</Link>
|
|
168
|
+
}
|
|
169
|
+
`.trim()
|
|
170
|
+
|
|
171
|
+
const result = applyTransform(input)
|
|
172
|
+
|
|
173
|
+
expect(result).toContain(
|
|
174
|
+
'import { Link, Tooltip } from "@planningcenter/tapestry-react"'
|
|
175
|
+
)
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
describe("multiple elements", () => {
|
|
180
|
+
it("should handle multiple Links with tooltips", () => {
|
|
181
|
+
const input = `
|
|
182
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
183
|
+
|
|
184
|
+
export default function Test() {
|
|
185
|
+
return (
|
|
186
|
+
<div>
|
|
187
|
+
<Link href="/save" tooltip={{title: "Save the document"}}>Save</Link>
|
|
188
|
+
<Link href="/cancel" tooltip={{title: "Cancel operation"}}>Cancel</Link>
|
|
189
|
+
</div>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
`.trim()
|
|
193
|
+
|
|
194
|
+
const result = applyTransform(input)
|
|
195
|
+
|
|
196
|
+
expect(result).toContain(
|
|
197
|
+
'import { Link, Tooltip } from "@planningcenter/tapestry-react"'
|
|
198
|
+
)
|
|
199
|
+
expect(result).toContain('<Tooltip {...{title: "Save the document"}}>')
|
|
200
|
+
expect(result).toContain('<Tooltip {...{title: "Cancel operation"}}>')
|
|
201
|
+
expect(result).not.toContain("tooltip=")
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it("should only transform Links with tooltip attribute", () => {
|
|
205
|
+
const input = `
|
|
206
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
207
|
+
|
|
208
|
+
export default function Test() {
|
|
209
|
+
return (
|
|
210
|
+
<div>
|
|
211
|
+
<Link href="/with" tooltip={{title: "Has tooltip"}}>With Tooltip</Link>
|
|
212
|
+
<Link href="/without">Without Tooltip</Link>
|
|
213
|
+
</div>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
`.trim()
|
|
217
|
+
|
|
218
|
+
const result = applyTransform(input)
|
|
219
|
+
|
|
220
|
+
expect(result).toContain('<Tooltip {...{title: "Has tooltip"}}>')
|
|
221
|
+
expect(result).toContain('<Link href="/with">With Tooltip</Link>')
|
|
222
|
+
expect(result).toContain('<Link href="/without">Without Tooltip</Link>')
|
|
223
|
+
const tooltipCount = (result?.match(/<Tooltip/g) || []).length
|
|
224
|
+
expect(tooltipCount).toBe(1)
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe("edge cases", () => {
|
|
229
|
+
it("should return null when Link is not imported from tapestry-react", () => {
|
|
230
|
+
const input = `
|
|
231
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
232
|
+
import { Link } from "other-library"
|
|
233
|
+
|
|
234
|
+
export default function Test() {
|
|
235
|
+
return <Link href="/save" tooltip={{title: "Save"}}>Save</Link>
|
|
236
|
+
}
|
|
237
|
+
`.trim()
|
|
238
|
+
|
|
239
|
+
const result = applyTransform(input)
|
|
240
|
+
expect(result).toBe(null)
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it("should return null when no Link is imported", () => {
|
|
244
|
+
const input = `
|
|
245
|
+
import React from "react"
|
|
246
|
+
|
|
247
|
+
export default function Test() {
|
|
248
|
+
return <div>No links here</div>
|
|
249
|
+
}
|
|
250
|
+
`.trim()
|
|
251
|
+
|
|
252
|
+
const result = applyTransform(input)
|
|
253
|
+
expect(result).toBe(null)
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it("should return null when no tooltips are present", () => {
|
|
257
|
+
const input = `
|
|
258
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
259
|
+
|
|
260
|
+
export default function Test() {
|
|
261
|
+
return <Link href="/primary" kind="primary">Save</Link>
|
|
262
|
+
}
|
|
263
|
+
`.trim()
|
|
264
|
+
|
|
265
|
+
const result = applyTransform(input)
|
|
266
|
+
expect(result).toBe(null)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it("should handle aliased Link import", () => {
|
|
270
|
+
const input = `
|
|
271
|
+
import { Link as TapestryLink } from "@planningcenter/tapestry-react"
|
|
272
|
+
|
|
273
|
+
export default function Test() {
|
|
274
|
+
return <TapestryLink href="/save" tooltip={{title: "Save item"}}>Save</TapestryLink>
|
|
275
|
+
}
|
|
276
|
+
`.trim()
|
|
277
|
+
|
|
278
|
+
const result = applyTransform(input)
|
|
279
|
+
|
|
280
|
+
expect(result).toContain(
|
|
281
|
+
'import { Link as TapestryLink, Tooltip } from "@planningcenter/tapestry-react"'
|
|
282
|
+
)
|
|
283
|
+
expect(result).toContain('<Tooltip {...{title: "Save item"}}>')
|
|
284
|
+
expect(result).toContain('<TapestryLink href="/save">Save</TapestryLink>')
|
|
285
|
+
expect(result).not.toContain("tooltip=")
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
it("should handle complex JSX expressions", () => {
|
|
289
|
+
const input = `
|
|
290
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
291
|
+
|
|
292
|
+
export default function Test({ items }) {
|
|
293
|
+
return (
|
|
294
|
+
<div>
|
|
295
|
+
{items.map(item => (
|
|
296
|
+
<Link key={item.id} href={item.url} tooltip={\`Save \${item.name}\`} onClick={() => save(item)}>
|
|
297
|
+
{item.name}
|
|
298
|
+
</Link>
|
|
299
|
+
))}
|
|
300
|
+
</div>
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
`.trim()
|
|
304
|
+
|
|
305
|
+
const result = applyTransform(input)
|
|
306
|
+
|
|
307
|
+
expect(result).toContain(
|
|
308
|
+
"<Tooltip key={item.id} {...`Save ${item.name}`}>"
|
|
309
|
+
)
|
|
310
|
+
expect(result).toContain(
|
|
311
|
+
"<Link href={item.url} onClick={() => save(item)}>"
|
|
312
|
+
)
|
|
313
|
+
expect(result).not.toContain("tooltip=")
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it("should handle empty tooltip attribute", () => {
|
|
317
|
+
const input = `
|
|
318
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
319
|
+
|
|
320
|
+
export default function Test() {
|
|
321
|
+
return <Link href="/save" tooltip>Save</Link>
|
|
322
|
+
}
|
|
323
|
+
`.trim()
|
|
324
|
+
|
|
325
|
+
const result = applyTransform(input)
|
|
326
|
+
|
|
327
|
+
expect(result).toContain("<Tooltip>")
|
|
328
|
+
expect(result).toContain('<Link href="/save">Save</Link>')
|
|
329
|
+
expect(result).toContain("</Tooltip>")
|
|
330
|
+
expect(result).not.toContain("tooltip")
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
describe("complex scenarios", () => {
|
|
335
|
+
it("should handle nested JSX and mixed components", () => {
|
|
336
|
+
const input = `
|
|
337
|
+
import React from "react"
|
|
338
|
+
import { Link, Button } from "@planningcenter/tapestry-react"
|
|
339
|
+
|
|
340
|
+
export default function Test() {
|
|
341
|
+
return (
|
|
342
|
+
<form>
|
|
343
|
+
<div className="actions">
|
|
344
|
+
<Link href="/submit" tooltip={{title: "Submit the form"}} kind="primary">
|
|
345
|
+
Submit
|
|
346
|
+
</Link>
|
|
347
|
+
<Button onClick={cancel}>Cancel</Button>
|
|
348
|
+
<Link href="/reset" tooltip={{text: "Reset form", placement: "bottom"}}>
|
|
349
|
+
Reset
|
|
350
|
+
</Link>
|
|
351
|
+
</div>
|
|
352
|
+
</form>
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
`.trim()
|
|
356
|
+
|
|
357
|
+
const result = applyTransform(input)
|
|
358
|
+
|
|
359
|
+
expect(result).toContain(
|
|
360
|
+
'import { Link, Button, Tooltip } from "@planningcenter/tapestry-react"'
|
|
361
|
+
)
|
|
362
|
+
expect(result).toContain('<Tooltip {...{title: "Submit the form"}}>')
|
|
363
|
+
expect(result).toContain('href="/submit" kind="primary"')
|
|
364
|
+
expect(result).toContain("Submit")
|
|
365
|
+
expect(result).toContain(
|
|
366
|
+
'<Tooltip {...{text: "Reset form", placement: "bottom"}}>'
|
|
367
|
+
)
|
|
368
|
+
expect(result).toContain('href="/reset"')
|
|
369
|
+
expect(result).toContain("Reset")
|
|
370
|
+
expect(result).toContain("<Button onClick={cancel}>Cancel</Button>")
|
|
371
|
+
expect(result).not.toContain("tooltip=")
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
it("converts tooltip with string literal to title prop", () => {
|
|
376
|
+
const input = `
|
|
377
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
378
|
+
|
|
379
|
+
export default function Test() {
|
|
380
|
+
return <Link href="/click" tooltip="Click me">Click</Link>
|
|
381
|
+
}
|
|
382
|
+
`.trim()
|
|
383
|
+
|
|
384
|
+
const result = applyTransform(input)!
|
|
385
|
+
|
|
386
|
+
expect(result.replace(/(\s*)?\n(\s*)?/gm, " ")).toContain(
|
|
387
|
+
'<Tooltip {...{ title: "Click me" }}>'
|
|
388
|
+
)
|
|
389
|
+
expect(result).toContain('<Link href="/click">Click</Link>')
|
|
390
|
+
expect(result).not.toContain("tooltip=")
|
|
391
|
+
})
|
|
392
|
+
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createWrapper } from "../../shared/actions/createWrapper"
|
|
2
|
+
import { getAttributeValueAsProps } from "../../shared/actions/getAttributeValueAsProps"
|
|
3
|
+
import { removeAttribute } from "../../shared/actions/removeAttribute"
|
|
4
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
5
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
6
|
+
|
|
7
|
+
const transform = attributeTransformFactory({
|
|
8
|
+
condition: hasAttribute("tooltip"),
|
|
9
|
+
targetComponent: "Link",
|
|
10
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
11
|
+
transform: (element, { j, source }) => {
|
|
12
|
+
const wrapperProps = getAttributeValueAsProps({
|
|
13
|
+
element,
|
|
14
|
+
j,
|
|
15
|
+
name: "tooltip",
|
|
16
|
+
stringValueKey: "title",
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
removeAttribute("tooltip", { element, j, source })
|
|
20
|
+
|
|
21
|
+
createWrapper({
|
|
22
|
+
conflictAlias: "TRTooltip",
|
|
23
|
+
element,
|
|
24
|
+
j,
|
|
25
|
+
source,
|
|
26
|
+
wrapperName: "Tooltip",
|
|
27
|
+
wrapperPackage: "@planningcenter/tapestry-react",
|
|
28
|
+
wrapperProps,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return true
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export default transform
|