@planningcenter/tapestry-migration-cli 3.1.0-rc.9 → 3.1.0
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/dist/tapestry-react-shim.cjs +7 -1
- package/package.json +3 -3
- package/src/components/input/transformableInput.ts +47 -6
- package/src/components/input/transforms/mergeFieldIntoInput.test.ts +78 -0
- package/src/components/input/transforms/mergeFieldIntoInput.ts +6 -212
- package/src/components/input/transforms/removeDuplicateKeys.test.ts +3 -3
- package/src/components/input/transforms/removeTypeInput.ts +3 -3
- package/src/components/input/transforms/removeTypeText.ts +2 -3
- package/src/components/input/transforms/unsupportedProps.test.ts +20 -20
- package/src/components/select/index.ts +58 -0
- package/src/components/select/transformableSelect.ts +7 -0
- package/src/components/select/transforms/auditSpreadProps.test.ts +103 -0
- package/src/components/select/transforms/auditSpreadProps.ts +26 -0
- package/src/components/select/transforms/childrenToOptions.test.ts +367 -0
- package/src/components/select/transforms/childrenToOptions.ts +295 -0
- package/src/components/select/transforms/convertLegacyOptions.test.ts +150 -0
- package/src/components/select/transforms/convertLegacyOptions.ts +105 -0
- package/src/components/select/transforms/convertStyleProps.test.ts +73 -0
- package/src/components/select/transforms/convertStyleProps.ts +12 -0
- package/src/components/select/transforms/emptyValueToPlaceholder.test.ts +122 -0
- package/src/components/select/transforms/emptyValueToPlaceholder.ts +22 -0
- package/src/components/select/transforms/innerRefToRef.test.ts +89 -0
- package/src/components/select/transforms/innerRefToRef.ts +18 -0
- package/src/components/select/transforms/mapChildrenToOptions.test.ts +521 -0
- package/src/components/select/transforms/mapChildrenToOptions.ts +312 -0
- package/src/components/select/transforms/mergeFieldIntoSelect.test.ts +506 -0
- package/src/components/select/transforms/mergeFieldIntoSelect.ts +7 -0
- package/src/components/select/transforms/mergeSelectLabel.test.ts +458 -0
- package/src/components/select/transforms/mergeSelectLabel.ts +225 -0
- package/src/components/select/transforms/moveSelectImport.test.ts +148 -0
- package/src/components/select/transforms/moveSelectImport.ts +14 -0
- package/src/components/select/transforms/removeDefaultProps.test.ts +249 -0
- package/src/components/select/transforms/removeDefaultProps.ts +112 -0
- package/src/components/select/transforms/sizeMapping.test.ts +188 -0
- package/src/components/select/transforms/sizeMapping.ts +17 -0
- package/src/components/select/transforms/skipMultipleSelect.test.ts +148 -0
- package/src/components/select/transforms/skipMultipleSelect.ts +23 -0
- package/src/components/select/transforms/stateToInvalid.test.ts +217 -0
- package/src/components/select/transforms/stateToInvalid.ts +59 -0
- package/src/components/select/transforms/stateToInvalidTernary.test.ts +146 -0
- package/src/components/select/transforms/stateToInvalidTernary.ts +13 -0
- package/src/components/select/transforms/unsupportedProps.test.ts +252 -0
- package/src/components/select/transforms/unsupportedProps.ts +44 -0
- package/src/components/shared/helpers/getAttributeExpression.ts +26 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +19 -2
- package/src/components/shared/transformFactories/mergeFieldFactory.ts +244 -0
- package/src/components/text-area/transforms/mergeFieldIntoTextArea.ts +4 -226
- package/src/index.ts +2 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./moveSelectImport"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
const result = transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
return result || source
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("moveSelectImport transform", () => {
|
|
19
|
+
it("should change import from tapestry-react to tapestry", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return <Select label="Test" placeholder="Pick" options={[]} />
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(input)
|
|
29
|
+
expect(result).toContain(
|
|
30
|
+
'import { Select } from "@planningcenter/tapestry"'
|
|
31
|
+
)
|
|
32
|
+
expect(result).not.toContain("@planningcenter/tapestry-react")
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("should handle multiple imports", () => {
|
|
36
|
+
const input = `
|
|
37
|
+
import { Button, Select } from "@planningcenter/tapestry-react"
|
|
38
|
+
|
|
39
|
+
function Test() {
|
|
40
|
+
return (
|
|
41
|
+
<div>
|
|
42
|
+
<Button>Click</Button>
|
|
43
|
+
<Select label="Test" placeholder="Pick" options={[]} />
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
`.trim()
|
|
48
|
+
|
|
49
|
+
const result = applyTransform(input)
|
|
50
|
+
expect(result).toContain(
|
|
51
|
+
'import { Button } from "@planningcenter/tapestry-react"'
|
|
52
|
+
)
|
|
53
|
+
expect(result).toContain(
|
|
54
|
+
'import { Select } from "@planningcenter/tapestry"'
|
|
55
|
+
)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it("should handle separate import declarations", () => {
|
|
59
|
+
const input = `
|
|
60
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
61
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
62
|
+
|
|
63
|
+
function Test() {
|
|
64
|
+
return (
|
|
65
|
+
<div>
|
|
66
|
+
<Button>Click</Button>
|
|
67
|
+
<Select label="Test" placeholder="Pick" options={[]} />
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
`.trim()
|
|
72
|
+
|
|
73
|
+
const result = applyTransform(input)
|
|
74
|
+
expect(result).toContain(
|
|
75
|
+
'import { Button } from "@planningcenter/tapestry-react"'
|
|
76
|
+
)
|
|
77
|
+
expect(result).toContain(
|
|
78
|
+
'import { Select } from "@planningcenter/tapestry"'
|
|
79
|
+
)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it("should not affect other components", () => {
|
|
83
|
+
const input = `
|
|
84
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
85
|
+
|
|
86
|
+
function Test() {
|
|
87
|
+
return <Button>Click me</Button>
|
|
88
|
+
}
|
|
89
|
+
`.trim()
|
|
90
|
+
|
|
91
|
+
const result = applyTransform(input)
|
|
92
|
+
expect(result).toContain(
|
|
93
|
+
'import { Button } from "@planningcenter/tapestry-react"'
|
|
94
|
+
)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it("should handle already migrated imports", () => {
|
|
98
|
+
const input = `
|
|
99
|
+
import { Select } from "@planningcenter/tapestry"
|
|
100
|
+
|
|
101
|
+
function Test() {
|
|
102
|
+
return <Select label="Test" placeholder="Pick" options={[]} />
|
|
103
|
+
}
|
|
104
|
+
`.trim()
|
|
105
|
+
|
|
106
|
+
const result = applyTransform(input)
|
|
107
|
+
expect(result).toContain(
|
|
108
|
+
'import { Select } from "@planningcenter/tapestry"'
|
|
109
|
+
)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it("should handle no imports", () => {
|
|
113
|
+
const input = `
|
|
114
|
+
function Test() {
|
|
115
|
+
return <div>No imports</div>
|
|
116
|
+
}
|
|
117
|
+
`.trim()
|
|
118
|
+
|
|
119
|
+
const result = applyTransform(input)
|
|
120
|
+
expect(result).toBe(input)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it("should preserve other attributes", () => {
|
|
124
|
+
const input = `
|
|
125
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
126
|
+
|
|
127
|
+
function Test() {
|
|
128
|
+
return (
|
|
129
|
+
<Select
|
|
130
|
+
label="Test"
|
|
131
|
+
placeholder="Pick"
|
|
132
|
+
options={[]}
|
|
133
|
+
disabled
|
|
134
|
+
onChange={() => {}}
|
|
135
|
+
/>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
`.trim()
|
|
139
|
+
|
|
140
|
+
const result = applyTransform(input)
|
|
141
|
+
expect(result).toContain(
|
|
142
|
+
'import { Select } from "@planningcenter/tapestry"'
|
|
143
|
+
)
|
|
144
|
+
expect(result).toContain('label="Test"')
|
|
145
|
+
expect(result).toContain("disabled")
|
|
146
|
+
expect(result).toContain("onChange={() => {}}")
|
|
147
|
+
})
|
|
148
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { componentTransformFactory } from "../../shared/transformFactories/componentTransformFactory"
|
|
4
|
+
import { transformableSelect } from "../transformableSelect"
|
|
5
|
+
|
|
6
|
+
const transform: Transform = componentTransformFactory({
|
|
7
|
+
condition: transformableSelect,
|
|
8
|
+
fromComponent: "Select",
|
|
9
|
+
fromPackage: "@planningcenter/tapestry-react",
|
|
10
|
+
toComponent: "Select",
|
|
11
|
+
toPackage: "@planningcenter/tapestry",
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default transform
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./removeDefaultProps"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
const result = transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
return result || source
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("removeDefaultProps transform", () => {
|
|
19
|
+
describe("matchWidths removal", () => {
|
|
20
|
+
it("should remove matchWidths boolean shorthand", () => {
|
|
21
|
+
const input = `
|
|
22
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
23
|
+
|
|
24
|
+
function Test() {
|
|
25
|
+
return <Select matchWidths emptyValue="Pick" />
|
|
26
|
+
}
|
|
27
|
+
`.trim()
|
|
28
|
+
|
|
29
|
+
const result = applyTransform(input)
|
|
30
|
+
expect(result).not.toContain("matchWidths")
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("should remove matchWidths={true}", () => {
|
|
34
|
+
const input = `
|
|
35
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
36
|
+
|
|
37
|
+
function Test() {
|
|
38
|
+
return <Select matchWidths={true} emptyValue="Pick" />
|
|
39
|
+
}
|
|
40
|
+
`.trim()
|
|
41
|
+
|
|
42
|
+
const result = applyTransform(input)
|
|
43
|
+
expect(result).not.toContain("matchWidths")
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should remove matchWidths="minimum"', () => {
|
|
47
|
+
const input = `
|
|
48
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
49
|
+
|
|
50
|
+
function Test() {
|
|
51
|
+
return <Select matchWidths="minimum" emptyValue="Pick" />
|
|
52
|
+
}
|
|
53
|
+
`.trim()
|
|
54
|
+
|
|
55
|
+
const result = applyTransform(input)
|
|
56
|
+
expect(result).not.toContain("matchWidths")
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('should remove matchWidths={"minimum"}', () => {
|
|
60
|
+
const input = `
|
|
61
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
62
|
+
|
|
63
|
+
function Test() {
|
|
64
|
+
return <Select matchWidths={"minimum"} emptyValue="Pick" />
|
|
65
|
+
}
|
|
66
|
+
`.trim()
|
|
67
|
+
|
|
68
|
+
const result = applyTransform(input)
|
|
69
|
+
expect(result).not.toContain("matchWidths")
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it("should NOT remove matchWidths={false}", () => {
|
|
73
|
+
const input = `
|
|
74
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
75
|
+
|
|
76
|
+
function Test() {
|
|
77
|
+
return <Select matchWidths={false} emptyValue="Pick" />
|
|
78
|
+
}
|
|
79
|
+
`.trim()
|
|
80
|
+
|
|
81
|
+
const result = applyTransform(input)
|
|
82
|
+
expect(result).toContain("matchWidths={false}")
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it("should NOT remove matchWidths with a variable value", () => {
|
|
86
|
+
const input = `
|
|
87
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
88
|
+
|
|
89
|
+
function Test() {
|
|
90
|
+
return <Select matchWidths={shouldMatch} emptyValue="Pick" />
|
|
91
|
+
}
|
|
92
|
+
`.trim()
|
|
93
|
+
|
|
94
|
+
const result = applyTransform(input)
|
|
95
|
+
expect(result).toContain("matchWidths={shouldMatch}")
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe("defaultOpen={false} removal", () => {
|
|
100
|
+
it("should remove defaultOpen={false}", () => {
|
|
101
|
+
const input = `
|
|
102
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
103
|
+
|
|
104
|
+
function Test() {
|
|
105
|
+
return <Select defaultOpen={false} emptyValue="Pick" />
|
|
106
|
+
}
|
|
107
|
+
`.trim()
|
|
108
|
+
|
|
109
|
+
const result = applyTransform(input)
|
|
110
|
+
expect(result).not.toContain("defaultOpen")
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it("should NOT remove defaultOpen={true}", () => {
|
|
114
|
+
const input = `
|
|
115
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
116
|
+
|
|
117
|
+
function Test() {
|
|
118
|
+
return <Select defaultOpen={true} emptyValue="Pick" />
|
|
119
|
+
}
|
|
120
|
+
`.trim()
|
|
121
|
+
|
|
122
|
+
const result = applyTransform(input)
|
|
123
|
+
expect(result).toContain("defaultOpen={true}")
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it("should NOT remove defaultOpen boolean shorthand (means true)", () => {
|
|
127
|
+
const input = `
|
|
128
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
129
|
+
|
|
130
|
+
function Test() {
|
|
131
|
+
return <Select defaultOpen emptyValue="Pick" />
|
|
132
|
+
}
|
|
133
|
+
`.trim()
|
|
134
|
+
|
|
135
|
+
const result = applyTransform(input)
|
|
136
|
+
expect(result).toContain("defaultOpen")
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
describe("lockScrollWhileOpen removal", () => {
|
|
141
|
+
it("should remove lockScrollWhileOpen boolean shorthand", () => {
|
|
142
|
+
const input = `
|
|
143
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
144
|
+
|
|
145
|
+
function Test() {
|
|
146
|
+
return <Select lockScrollWhileOpen emptyValue="Pick" />
|
|
147
|
+
}
|
|
148
|
+
`.trim()
|
|
149
|
+
|
|
150
|
+
const result = applyTransform(input)
|
|
151
|
+
expect(result).not.toContain("lockScrollWhileOpen")
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it("should remove lockScrollWhileOpen={true}", () => {
|
|
155
|
+
const input = `
|
|
156
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
157
|
+
|
|
158
|
+
function Test() {
|
|
159
|
+
return <Select lockScrollWhileOpen={true} emptyValue="Pick" />
|
|
160
|
+
}
|
|
161
|
+
`.trim()
|
|
162
|
+
|
|
163
|
+
const result = applyTransform(input)
|
|
164
|
+
expect(result).not.toContain("lockScrollWhileOpen")
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it("should NOT remove lockScrollWhileOpen={false}", () => {
|
|
168
|
+
const input = `
|
|
169
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
170
|
+
|
|
171
|
+
function Test() {
|
|
172
|
+
return <Select lockScrollWhileOpen={false} emptyValue="Pick" />
|
|
173
|
+
}
|
|
174
|
+
`.trim()
|
|
175
|
+
|
|
176
|
+
const result = applyTransform(input)
|
|
177
|
+
expect(result).toContain("lockScrollWhileOpen={false}")
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
describe("multiple removals", () => {
|
|
182
|
+
it("should remove multiple default props at once", () => {
|
|
183
|
+
const input = `
|
|
184
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
185
|
+
|
|
186
|
+
function Test() {
|
|
187
|
+
return (
|
|
188
|
+
<Select
|
|
189
|
+
matchWidths
|
|
190
|
+
defaultOpen={false}
|
|
191
|
+
lockScrollWhileOpen
|
|
192
|
+
emptyValue="Pick"
|
|
193
|
+
/>
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
`.trim()
|
|
197
|
+
|
|
198
|
+
const result = applyTransform(input)
|
|
199
|
+
expect(result).not.toContain("matchWidths")
|
|
200
|
+
expect(result).not.toContain("defaultOpen")
|
|
201
|
+
expect(result).not.toContain("lockScrollWhileOpen")
|
|
202
|
+
expect(result).toContain('emptyValue="Pick"')
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
describe("edge cases", () => {
|
|
207
|
+
it("should not transform Select without removable props", () => {
|
|
208
|
+
const input = `
|
|
209
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
210
|
+
|
|
211
|
+
function Test() {
|
|
212
|
+
return <Select emptyValue="Pick" disabled />
|
|
213
|
+
}
|
|
214
|
+
`.trim()
|
|
215
|
+
|
|
216
|
+
const result = applyTransform(input)
|
|
217
|
+
expect(result).toBe(input)
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it("should not transform other components", () => {
|
|
221
|
+
const input = `
|
|
222
|
+
import { Dropdown } from "other-library"
|
|
223
|
+
|
|
224
|
+
function Test() {
|
|
225
|
+
return <Dropdown matchWidths />
|
|
226
|
+
}
|
|
227
|
+
`.trim()
|
|
228
|
+
|
|
229
|
+
const result = applyTransform(input)
|
|
230
|
+
expect(result).toBe(input)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it("should preserve other props", () => {
|
|
234
|
+
const input = `
|
|
235
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
236
|
+
|
|
237
|
+
function Test() {
|
|
238
|
+
return <Select matchWidths emptyValue="Pick" disabled onChange={handler} />
|
|
239
|
+
}
|
|
240
|
+
`.trim()
|
|
241
|
+
|
|
242
|
+
const result = applyTransform(input)
|
|
243
|
+
expect(result).not.toContain("matchWidths")
|
|
244
|
+
expect(result).toContain('emptyValue="Pick"')
|
|
245
|
+
expect(result).toContain("disabled")
|
|
246
|
+
expect(result).toContain("onChange={handler}")
|
|
247
|
+
})
|
|
248
|
+
})
|
|
249
|
+
})
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { JSXAttribute, Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
4
|
+
import { transformableSelect } from "../transformableSelect"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Props that were legacy defaults and should be removed because they are
|
|
8
|
+
* either not supported or are no-ops in the new Select component.
|
|
9
|
+
*
|
|
10
|
+
* - matchWidths: legacy default was "minimum" (always on). The new Select
|
|
11
|
+
* does not use this prop (only relevant in complex/popover mode via CSS).
|
|
12
|
+
* Remove any value: matchWidths, matchWidths={true}, matchWidths="minimum".
|
|
13
|
+
* - defaultOpen={false}: false is the default in both old and new.
|
|
14
|
+
* - lockScrollWhileOpen / lockScrollWhileOpen={true}: legacy default, removed in new API.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
function isRemovableMatchWidths(attr: JSXAttribute): boolean {
|
|
18
|
+
if ((attr.name.name as string) !== "matchWidths") return false
|
|
19
|
+
|
|
20
|
+
// Shorthand: matchWidths (boolean true)
|
|
21
|
+
if (!attr.value) return true
|
|
22
|
+
|
|
23
|
+
// matchWidths={true}
|
|
24
|
+
if (
|
|
25
|
+
attr.value.type === "JSXExpressionContainer" &&
|
|
26
|
+
attr.value.expression.type === "BooleanLiteral" &&
|
|
27
|
+
attr.value.expression.value === true
|
|
28
|
+
) {
|
|
29
|
+
return true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// matchWidths="minimum"
|
|
33
|
+
if (attr.value.type === "StringLiteral" && attr.value.value === "minimum") {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// matchWidths={"minimum"}
|
|
38
|
+
if (
|
|
39
|
+
attr.value.type === "JSXExpressionContainer" &&
|
|
40
|
+
attr.value.expression.type === "StringLiteral" &&
|
|
41
|
+
attr.value.expression.value === "minimum"
|
|
42
|
+
) {
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isRemovableDefaultOpen(attr: JSXAttribute): boolean {
|
|
50
|
+
if ((attr.name.name as string) !== "defaultOpen") return false
|
|
51
|
+
|
|
52
|
+
// defaultOpen={false}
|
|
53
|
+
if (
|
|
54
|
+
attr.value &&
|
|
55
|
+
attr.value.type === "JSXExpressionContainer" &&
|
|
56
|
+
attr.value.expression.type === "BooleanLiteral" &&
|
|
57
|
+
attr.value.expression.value === false
|
|
58
|
+
) {
|
|
59
|
+
return true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isRemovableLockScroll(attr: JSXAttribute): boolean {
|
|
66
|
+
const name = attr.name.name as string
|
|
67
|
+
if (name !== "lockScrollWhileOpen") return false
|
|
68
|
+
|
|
69
|
+
// lockScrollWhileOpen (shorthand true) or lockScrollWhileOpen={true}
|
|
70
|
+
if (!attr.value) return true
|
|
71
|
+
if (
|
|
72
|
+
attr.value.type === "JSXExpressionContainer" &&
|
|
73
|
+
attr.value.expression.type === "BooleanLiteral" &&
|
|
74
|
+
attr.value.expression.value === true
|
|
75
|
+
) {
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const transform: Transform = attributeTransformFactory({
|
|
83
|
+
condition: transformableSelect,
|
|
84
|
+
targetComponent: "Select",
|
|
85
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
86
|
+
transform: (element) => {
|
|
87
|
+
const attributes = element.openingElement.attributes || []
|
|
88
|
+
const toRemove: number[] = []
|
|
89
|
+
|
|
90
|
+
attributes.forEach((attr, index) => {
|
|
91
|
+
if (attr.type !== "JSXAttribute") return
|
|
92
|
+
if (
|
|
93
|
+
isRemovableMatchWidths(attr) ||
|
|
94
|
+
isRemovableDefaultOpen(attr) ||
|
|
95
|
+
isRemovableLockScroll(attr)
|
|
96
|
+
) {
|
|
97
|
+
toRemove.push(index)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
if (toRemove.length === 0) return false
|
|
102
|
+
|
|
103
|
+
// Remove in reverse order to preserve indices
|
|
104
|
+
for (let i = toRemove.length - 1; i >= 0; i--) {
|
|
105
|
+
attributes.splice(toRemove[i], 1)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return true
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
export default transform
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./sizeMapping"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
const result = transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
return result || source
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("sizeMapping transform", () => {
|
|
19
|
+
describe("size value transformations", () => {
|
|
20
|
+
it("should transform xs to md", () => {
|
|
21
|
+
const input = `
|
|
22
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
23
|
+
|
|
24
|
+
function Test() {
|
|
25
|
+
return <Select size="xs" emptyValue="Pick" />
|
|
26
|
+
}
|
|
27
|
+
`.trim()
|
|
28
|
+
|
|
29
|
+
const result = applyTransform(input)
|
|
30
|
+
expect(result).toContain('size="md"')
|
|
31
|
+
expect(result).not.toContain('size="xs"')
|
|
32
|
+
expect(result).toContain('Size "xs" was mapped to "md"')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("should transform sm to md", () => {
|
|
36
|
+
const input = `
|
|
37
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
38
|
+
|
|
39
|
+
function Test() {
|
|
40
|
+
return <Select size="sm" emptyValue="Pick" />
|
|
41
|
+
}
|
|
42
|
+
`.trim()
|
|
43
|
+
|
|
44
|
+
const result = applyTransform(input)
|
|
45
|
+
expect(result).toContain('size="md"')
|
|
46
|
+
expect(result).not.toContain('size="sm"')
|
|
47
|
+
expect(result).toContain('Size "sm" was mapped to "md"')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it("should transform xl to lg", () => {
|
|
51
|
+
const input = `
|
|
52
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
53
|
+
|
|
54
|
+
function Test() {
|
|
55
|
+
return <Select size="xl" emptyValue="Pick" />
|
|
56
|
+
}
|
|
57
|
+
`.trim()
|
|
58
|
+
|
|
59
|
+
const result = applyTransform(input)
|
|
60
|
+
expect(result).toContain('size="lg"')
|
|
61
|
+
expect(result).not.toContain('size="xl"')
|
|
62
|
+
expect(result).toContain('Size "xl" was mapped to "lg"')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should transform size={"xs"} expression to md', () => {
|
|
66
|
+
const input = `
|
|
67
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
68
|
+
|
|
69
|
+
function Test() {
|
|
70
|
+
return <Select size={"xs"} emptyValue="Pick" />
|
|
71
|
+
}
|
|
72
|
+
`.trim()
|
|
73
|
+
|
|
74
|
+
const result = applyTransform(input)
|
|
75
|
+
expect(result).toContain('size="md"')
|
|
76
|
+
expect(result).toContain('Size "xs" was mapped to "md"')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("should not transform supported sizes (md, lg)", () => {
|
|
80
|
+
const input = `
|
|
81
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
82
|
+
|
|
83
|
+
function Test() {
|
|
84
|
+
return (
|
|
85
|
+
<div>
|
|
86
|
+
<Select size="md" emptyValue="Pick" />
|
|
87
|
+
<Select size="lg" emptyValue="Pick" />
|
|
88
|
+
</div>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
`.trim()
|
|
92
|
+
|
|
93
|
+
const result = applyTransform(input)
|
|
94
|
+
expect(result).toContain('size="md"')
|
|
95
|
+
expect(result).toContain('size="lg"')
|
|
96
|
+
expect(result).not.toContain("TODO: tapestry-migration")
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe("edge cases", () => {
|
|
101
|
+
it("should not affect Select without size prop", () => {
|
|
102
|
+
const input = `
|
|
103
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
104
|
+
|
|
105
|
+
function Test() {
|
|
106
|
+
return <Select emptyValue="Pick" />
|
|
107
|
+
}
|
|
108
|
+
`.trim()
|
|
109
|
+
|
|
110
|
+
const result = applyTransform(input)
|
|
111
|
+
expect(result).toBe(input)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("should not affect other components", () => {
|
|
115
|
+
const input = `
|
|
116
|
+
import { Button, Select } from "@planningcenter/tapestry-react"
|
|
117
|
+
|
|
118
|
+
function Test() {
|
|
119
|
+
return (
|
|
120
|
+
<div>
|
|
121
|
+
<Button size="xs">Click</Button>
|
|
122
|
+
<Select size="md" emptyValue="Pick" />
|
|
123
|
+
</div>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
`.trim()
|
|
127
|
+
|
|
128
|
+
const result = applyTransform(input)
|
|
129
|
+
expect(result).toContain('size="xs"')
|
|
130
|
+
expect(result).toContain('size="md"')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("should handle multiple Selects with different sizes", () => {
|
|
134
|
+
const input = `
|
|
135
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
136
|
+
|
|
137
|
+
function Test() {
|
|
138
|
+
return (
|
|
139
|
+
<div>
|
|
140
|
+
<Select size="xs" emptyValue="Pick" />
|
|
141
|
+
<Select size="sm" emptyValue="Pick" />
|
|
142
|
+
<Select size="lg" emptyValue="Pick" />
|
|
143
|
+
<Select size="xl" emptyValue="Pick" />
|
|
144
|
+
</div>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
`.trim()
|
|
148
|
+
|
|
149
|
+
const result = applyTransform(input)
|
|
150
|
+
expect(result).not.toContain('size="xs"')
|
|
151
|
+
expect(result).not.toContain('size="sm"')
|
|
152
|
+
expect(result).not.toContain('size="xl"')
|
|
153
|
+
const sizeMappingMatches = result.match(/Size ".*" was mapped to/g)
|
|
154
|
+
expect(sizeMappingMatches).toHaveLength(3)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it("should not transform expression values (variables)", () => {
|
|
158
|
+
const input = `
|
|
159
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
160
|
+
|
|
161
|
+
function Test() {
|
|
162
|
+
const size = "xs"
|
|
163
|
+
return <Select size={size} emptyValue="Pick" />
|
|
164
|
+
}
|
|
165
|
+
`.trim()
|
|
166
|
+
|
|
167
|
+
const result = applyTransform(input)
|
|
168
|
+
expect(result).toContain("size={size}")
|
|
169
|
+
expect(result).not.toContain("Size")
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it("should preserve other props", () => {
|
|
173
|
+
const input = `
|
|
174
|
+
import { Select } from "@planningcenter/tapestry-react"
|
|
175
|
+
|
|
176
|
+
function Test() {
|
|
177
|
+
return <Select size="xs" emptyValue="Pick" disabled onChange={() => {}} />
|
|
178
|
+
}
|
|
179
|
+
`.trim()
|
|
180
|
+
|
|
181
|
+
const result = applyTransform(input)
|
|
182
|
+
expect(result).toContain('size="md"')
|
|
183
|
+
expect(result).toContain('emptyValue="Pick"')
|
|
184
|
+
expect(result).toContain("disabled")
|
|
185
|
+
expect(result).toContain("onChange={() => {}}")
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
})
|