@planningcenter/tapestry-migration-cli 3.1.0-rc.2 → 3.1.0-rc.20
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/button/transforms/convertStyleProps.test.ts +97 -0
- package/src/components/button/transforms/removeTypeButton.test.ts +0 -1
- package/src/components/checkbox/transforms/moveCheckboxImport.test.ts +3 -0
- package/src/components/input/index.ts +66 -0
- package/src/components/input/transformableInput.ts +49 -0
- package/src/components/input/transforms/auditSpreadProps.test.ts +192 -0
- package/src/components/input/transforms/auditSpreadProps.ts +26 -0
- package/src/components/input/transforms/autoWidthTransform.test.ts +172 -0
- package/src/components/input/transforms/autoWidthTransform.ts +41 -0
- package/src/components/input/transforms/convertStyleProps.test.ts +128 -0
- package/src/components/input/transforms/convertStyleProps.ts +12 -0
- package/src/components/input/transforms/highlightOnInteractionToSelectTextOnFocus.test.ts +186 -0
- package/src/components/input/transforms/highlightOnInteractionToSelectTextOnFocus.ts +27 -0
- package/src/components/input/transforms/inputLabelToLabelProp.test.ts +319 -0
- package/src/components/input/transforms/inputLabelToLabelProp.ts +203 -0
- package/src/components/input/transforms/mergeFieldIntoInput.test.ts +469 -0
- package/src/components/input/transforms/mergeFieldIntoInput.ts +7 -0
- package/src/components/input/transforms/mergeInputLabel.test.ts +458 -0
- package/src/components/input/transforms/mergeInputLabel.ts +204 -0
- package/src/components/input/transforms/moveInputImport.test.ts +166 -0
- package/src/components/input/transforms/moveInputImport.ts +14 -0
- package/src/components/input/transforms/numberFieldAddTypeNumber.test.ts +92 -0
- package/src/components/input/transforms/numberFieldAddTypeNumber.ts +14 -0
- package/src/components/input/transforms/numberFieldRenameToInput.test.ts +126 -0
- package/src/components/input/transforms/numberFieldRenameToInput.ts +9 -0
- package/src/components/input/transforms/removeAsInput.test.ts +139 -0
- package/src/components/input/transforms/removeAsInput.ts +20 -0
- package/src/components/input/transforms/removeDuplicateKeys.test.ts +302 -0
- package/src/components/input/transforms/removeDuplicateKeys.ts +10 -0
- package/src/components/input/transforms/removeInputBox.test.ts +352 -0
- package/src/components/input/transforms/removeInputBox.ts +109 -0
- package/src/components/input/transforms/removeRedundantAriaLabel.test.ts +128 -0
- package/src/components/input/transforms/removeRedundantAriaLabel.ts +21 -0
- package/src/components/input/transforms/removeTypeInput.test.ts +212 -0
- package/src/components/input/transforms/removeTypeInput.ts +22 -0
- package/src/components/input/transforms/removeTypeText.test.ts +160 -0
- package/src/components/input/transforms/removeTypeText.ts +17 -0
- package/src/components/input/transforms/sizeMapping.test.ts +198 -0
- package/src/components/input/transforms/sizeMapping.ts +17 -0
- package/src/components/input/transforms/skipRenderSideProps.test.ts +236 -0
- package/src/components/input/transforms/skipRenderSideProps.ts +27 -0
- package/src/components/input/transforms/stateToInvalid.test.ts +208 -0
- package/src/components/input/transforms/stateToInvalid.ts +59 -0
- package/src/components/input/transforms/stateToInvalidTernary.test.ts +159 -0
- package/src/components/input/transforms/stateToInvalidTernary.ts +13 -0
- package/src/components/input/transforms/unsupportedProps.test.ts +566 -0
- package/src/components/input/transforms/unsupportedProps.ts +84 -0
- package/src/components/link/transforms/reviewStyles.test.ts +0 -1
- 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 +102 -0
- package/src/components/shared/transformFactories/helpers/manageImports.ts +14 -12
- package/src/components/shared/transformFactories/mergeFieldFactory.ts +244 -0
- package/src/components/shared/transformFactories/sizeMappingFactory.ts +9 -2
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +56 -17
- package/src/components/shared/transformFactories/ternaryConditionalToPropFactory.ts +65 -0
- package/src/components/text-area/index.ts +48 -0
- package/src/components/text-area/transforms/auditSpreadProps.test.ts +139 -0
- package/src/components/text-area/transforms/auditSpreadProps.ts +10 -0
- package/src/components/text-area/transforms/convertStyleProps.test.ts +158 -0
- package/src/components/text-area/transforms/convertStyleProps.ts +10 -0
- package/src/components/text-area/transforms/innerRefToRef.test.ts +206 -0
- package/src/components/text-area/transforms/innerRefToRef.ts +14 -0
- package/src/components/text-area/transforms/mergeFieldIntoTextArea.test.ts +477 -0
- package/src/components/text-area/transforms/mergeFieldIntoTextArea.ts +5 -0
- package/src/components/text-area/transforms/moveTextAreaImport.test.ts +168 -0
- package/src/components/text-area/transforms/moveTextAreaImport.ts +13 -0
- package/src/components/text-area/transforms/removeDuplicateKeys.test.ts +129 -0
- package/src/components/text-area/transforms/removeDuplicateKeys.ts +8 -0
- package/src/components/text-area/transforms/removeRedundantAriaLabel.test.ts +183 -0
- package/src/components/text-area/transforms/removeRedundantAriaLabel.ts +59 -0
- package/src/components/text-area/transforms/sizeMapping.test.ts +199 -0
- package/src/components/text-area/transforms/sizeMapping.ts +15 -0
- package/src/components/text-area/transforms/stateToInvalid.test.ts +204 -0
- package/src/components/text-area/transforms/stateToInvalid.ts +57 -0
- package/src/components/text-area/transforms/stateToInvalidTernary.test.ts +133 -0
- package/src/components/text-area/transforms/stateToInvalidTernary.ts +11 -0
- package/src/components/text-area/transforms/unsupportedProps.test.ts +275 -0
- package/src/components/text-area/transforms/unsupportedProps.ts +35 -0
- package/src/index.ts +2 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./removeDuplicateKeys"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string) {
|
|
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("removeDuplicateKeys transform", () => {
|
|
18
|
+
describe("Input elements with duplicate attributes", () => {
|
|
19
|
+
it("should remove duplicate attributes from Input elements", () => {
|
|
20
|
+
const source = `
|
|
21
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
export function TestComponent() {
|
|
24
|
+
return <Input kind="primary" disabled kind="secondary">Save</Input>
|
|
25
|
+
}
|
|
26
|
+
`
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(source)
|
|
29
|
+
|
|
30
|
+
expect(result).not.toBeNull()
|
|
31
|
+
expect(result).toContain('kind="primary"')
|
|
32
|
+
expect(result).not.toContain('kind="secondary"')
|
|
33
|
+
expect(result).toContain("disabled")
|
|
34
|
+
expect(result).toContain("Save")
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("should handle multiple Input elements with duplicates", () => {
|
|
38
|
+
const source = `
|
|
39
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
40
|
+
|
|
41
|
+
export function TestComponent() {
|
|
42
|
+
return (
|
|
43
|
+
<div>
|
|
44
|
+
<Input kind="primary" size="large" kind="secondary">Save</Input>
|
|
45
|
+
<Input disabled loading disabled>Cancel</Input>
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
`
|
|
50
|
+
|
|
51
|
+
const result = applyTransform(source)
|
|
52
|
+
|
|
53
|
+
expect(result).not.toBeNull()
|
|
54
|
+
expect(result).toContain('kind="primary"')
|
|
55
|
+
expect(result).not.toContain('kind="secondary"')
|
|
56
|
+
expect(result).toContain('size="large"')
|
|
57
|
+
|
|
58
|
+
expect(result).toContain("<Input disabled loading>")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("should preserve first occurrence of duplicate attributes", () => {
|
|
62
|
+
const source = `
|
|
63
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
64
|
+
|
|
65
|
+
export function TestComponent() {
|
|
66
|
+
return <Input onClick={handleFirst} aria-label="First" onClick={handleSecond} aria-label="Second">Click</Input>
|
|
67
|
+
}
|
|
68
|
+
`
|
|
69
|
+
|
|
70
|
+
const result = applyTransform(source)
|
|
71
|
+
|
|
72
|
+
expect(result).not.toBeNull()
|
|
73
|
+
expect(result).toContain("onClick={handleFirst}")
|
|
74
|
+
expect(result).not.toContain("onClick={handleSecond}")
|
|
75
|
+
expect(result).toContain('aria-label="First"')
|
|
76
|
+
expect(result).not.toContain('aria-label="Second"')
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe("Input elements without duplicate attributes", () => {
|
|
81
|
+
it("should return null when no duplicates exist", () => {
|
|
82
|
+
const source = `
|
|
83
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
84
|
+
|
|
85
|
+
export function TestComponent() {
|
|
86
|
+
return <Input kind="primary" size="large" disabled>Save</Input>
|
|
87
|
+
}
|
|
88
|
+
`
|
|
89
|
+
|
|
90
|
+
const result = applyTransform(source)
|
|
91
|
+
|
|
92
|
+
expect(result).toBeNull() // No changes needed
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it("should return null for Input with no attributes", () => {
|
|
96
|
+
const source = `
|
|
97
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
98
|
+
|
|
99
|
+
export function TestComponent() {
|
|
100
|
+
return <Input>Save</Input>
|
|
101
|
+
}
|
|
102
|
+
`
|
|
103
|
+
|
|
104
|
+
const result = applyTransform(source)
|
|
105
|
+
|
|
106
|
+
expect(result).toBeNull()
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe("import handling", () => {
|
|
111
|
+
it("should only process files that import Input from tapestry-react", () => {
|
|
112
|
+
const source = `
|
|
113
|
+
import { Input } from "some-other-library"
|
|
114
|
+
|
|
115
|
+
export function TestComponent() {
|
|
116
|
+
return <Input kind="primary" kind="secondary">Save</Input>
|
|
117
|
+
}
|
|
118
|
+
`
|
|
119
|
+
|
|
120
|
+
const result = applyTransform(source)
|
|
121
|
+
|
|
122
|
+
expect(result).toBeNull() // Should not process non-tapestry Inputs
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it("should handle aliased Input imports", () => {
|
|
126
|
+
const source = `
|
|
127
|
+
import { Input as TapestryInput } from "@planningcenter/tapestry-react"
|
|
128
|
+
|
|
129
|
+
export function TestComponent() {
|
|
130
|
+
return <TapestryInput kind="primary" disabled kind="secondary">Save</TapestryInput>
|
|
131
|
+
}
|
|
132
|
+
`
|
|
133
|
+
|
|
134
|
+
const result = applyTransform(source)
|
|
135
|
+
|
|
136
|
+
expect(result).not.toBeNull()
|
|
137
|
+
expect(result).toContain("<TapestryInput")
|
|
138
|
+
expect(result).toContain('kind="primary"')
|
|
139
|
+
expect(result).not.toContain('kind="secondary"')
|
|
140
|
+
expect(result).toContain("</TapestryInput>")
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it("should handle renamed imports", () => {
|
|
144
|
+
const source = `
|
|
145
|
+
import { Input as PrimaryInput } from "@planningcenter/tapestry-react"
|
|
146
|
+
|
|
147
|
+
export function TestComponent() {
|
|
148
|
+
return <PrimaryInput size="large" onClick={handler} size="small">Click</PrimaryInput>
|
|
149
|
+
}
|
|
150
|
+
`
|
|
151
|
+
|
|
152
|
+
const result = applyTransform(source)
|
|
153
|
+
|
|
154
|
+
expect(result).not.toBeNull()
|
|
155
|
+
expect(result).toContain('size="large"')
|
|
156
|
+
expect(result).not.toContain('size="small"')
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
describe("mixed elements", () => {
|
|
161
|
+
it("should only process Input elements, not other elements", () => {
|
|
162
|
+
const source = `
|
|
163
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
164
|
+
|
|
165
|
+
export function TestComponent() {
|
|
166
|
+
return (
|
|
167
|
+
<div>
|
|
168
|
+
<Input kind="primary" kind="secondary">Save</Input>
|
|
169
|
+
<div className="container" className="wrapper">Content</div>
|
|
170
|
+
<span role="button" role="link">Text</span>
|
|
171
|
+
</div>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
`
|
|
175
|
+
|
|
176
|
+
const result = applyTransform(source)
|
|
177
|
+
|
|
178
|
+
expect(result).not.toBeNull()
|
|
179
|
+
|
|
180
|
+
// Input duplicates should be removed
|
|
181
|
+
expect(result).toContain('kind="primary"')
|
|
182
|
+
expect(result).not.toContain('kind="secondary"')
|
|
183
|
+
|
|
184
|
+
// Other elements should remain unchanged (duplicates preserved)
|
|
185
|
+
expect(result).toContain('className="container"')
|
|
186
|
+
expect(result).toContain('className="wrapper"')
|
|
187
|
+
expect(result).toContain('role="button"')
|
|
188
|
+
expect(result).toContain('role="link"')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it("should process multiple Input elements independently", () => {
|
|
192
|
+
const source = `
|
|
193
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
194
|
+
|
|
195
|
+
export function TestComponent() {
|
|
196
|
+
return (
|
|
197
|
+
<form>
|
|
198
|
+
<Input type="email" kind="primary" type="text">Submit</Input>
|
|
199
|
+
<Input disabled loading disabled>Loading</Input>
|
|
200
|
+
<Input size="small">Small</Input>
|
|
201
|
+
</form>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
`
|
|
205
|
+
|
|
206
|
+
const result = applyTransform(source)
|
|
207
|
+
|
|
208
|
+
expect(result).not.toBeNull()
|
|
209
|
+
|
|
210
|
+
// First input: should keep first 'type'
|
|
211
|
+
expect(result).toContain('type="email"')
|
|
212
|
+
expect(result).not.toContain('type="text"')
|
|
213
|
+
|
|
214
|
+
expect(result).toContain("<Input disabled loading>")
|
|
215
|
+
|
|
216
|
+
// Third input: should remain unchanged
|
|
217
|
+
expect(result).toContain('size="small"')
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
describe("complex JSX scenarios", () => {
|
|
222
|
+
it("should handle nested Input elements", () => {
|
|
223
|
+
const source = `
|
|
224
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
225
|
+
|
|
226
|
+
export function TestComponent() {
|
|
227
|
+
return (
|
|
228
|
+
<div>
|
|
229
|
+
<div className="toolbar">
|
|
230
|
+
<Input kind="primary" disabled kind="secondary">
|
|
231
|
+
Save
|
|
232
|
+
</Input>
|
|
233
|
+
</div>
|
|
234
|
+
<Input size="large" onClick={handler} size="small">
|
|
235
|
+
Cancel
|
|
236
|
+
</Input>
|
|
237
|
+
</div>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
`
|
|
241
|
+
|
|
242
|
+
const result = applyTransform(source)
|
|
243
|
+
|
|
244
|
+
expect(result).not.toBeNull()
|
|
245
|
+
expect(result).toContain('kind="primary"')
|
|
246
|
+
expect(result).not.toContain('kind="secondary"')
|
|
247
|
+
expect(result).toContain('size="large"')
|
|
248
|
+
expect(result).not.toContain('size="small"')
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it("should handle Input elements with children and complex attributes", () => {
|
|
252
|
+
const source = `
|
|
253
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
254
|
+
|
|
255
|
+
export function TestComponent() {
|
|
256
|
+
return (
|
|
257
|
+
<Input
|
|
258
|
+
kind="primary"
|
|
259
|
+
onClick={() => console.log('first')}
|
|
260
|
+
className="btn-primary"
|
|
261
|
+
kind="secondary"
|
|
262
|
+
onClick={() => console.log('second')}
|
|
263
|
+
data-testid="save-input"
|
|
264
|
+
>
|
|
265
|
+
<span>Save Changes</span>
|
|
266
|
+
</Input>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
`
|
|
270
|
+
|
|
271
|
+
const result = applyTransform(source)
|
|
272
|
+
|
|
273
|
+
expect(result).not.toBeNull()
|
|
274
|
+
expect(result).toContain('kind="primary"')
|
|
275
|
+
expect(result).not.toContain('kind="secondary"')
|
|
276
|
+
expect(result).toContain("onClick={() => console.log('first')}")
|
|
277
|
+
expect(result).not.toContain("onClick={() => console.log('second')}")
|
|
278
|
+
expect(result).toContain('data-testid="save-input"')
|
|
279
|
+
expect(result).toContain("<span>Save Changes</span>")
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
describe("self-closing Input elements", () => {
|
|
284
|
+
it("should handle self-closing Input elements with duplicates", () => {
|
|
285
|
+
const source = `
|
|
286
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
287
|
+
|
|
288
|
+
export function TestComponent() {
|
|
289
|
+
return <Input kind="primary" disabled kind="secondary" />
|
|
290
|
+
}
|
|
291
|
+
`
|
|
292
|
+
|
|
293
|
+
const result = applyTransform(source)
|
|
294
|
+
|
|
295
|
+
expect(result).not.toBeNull()
|
|
296
|
+
expect(result).toContain('kind="primary"')
|
|
297
|
+
expect(result).not.toContain('kind="secondary"')
|
|
298
|
+
expect(result).toContain("disabled")
|
|
299
|
+
expect(result).toContain("/>")
|
|
300
|
+
})
|
|
301
|
+
})
|
|
302
|
+
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { removeDuplicateKeys } from "../../shared/actions/removeDuplicateKeys"
|
|
2
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
3
|
+
import { transformableInput } from "../transformableInput"
|
|
4
|
+
|
|
5
|
+
export default attributeTransformFactory({
|
|
6
|
+
condition: transformableInput,
|
|
7
|
+
targetComponent: "Input",
|
|
8
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
+
transform: removeDuplicateKeys,
|
|
10
|
+
})
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./removeInputBox"
|
|
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("removeInputBox transform", () => {
|
|
18
|
+
describe("basic unwrap", () => {
|
|
19
|
+
it("removes InputBox wrapper and promotes Input child", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return (
|
|
25
|
+
<Box>
|
|
26
|
+
<Input.InputBox>
|
|
27
|
+
<Input id="name" value={name} />
|
|
28
|
+
</Input.InputBox>
|
|
29
|
+
</Box>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
`.trim()
|
|
33
|
+
|
|
34
|
+
const result = applyTransform(input)
|
|
35
|
+
expect(result).not.toBeNull()
|
|
36
|
+
expect(result).not.toContain("InputBox")
|
|
37
|
+
expect(result).toContain("<Input")
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe("aliased import", () => {
|
|
42
|
+
it("handles aliased Input imports", () => {
|
|
43
|
+
const input = `
|
|
44
|
+
import { Input as TapInput } from "@planningcenter/tapestry-react"
|
|
45
|
+
|
|
46
|
+
function Test() {
|
|
47
|
+
return (
|
|
48
|
+
<Box>
|
|
49
|
+
<TapInput.InputBox>
|
|
50
|
+
<TapInput id="name" value={name} />
|
|
51
|
+
</TapInput.InputBox>
|
|
52
|
+
</Box>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
`.trim()
|
|
56
|
+
|
|
57
|
+
const result = applyTransform(input)
|
|
58
|
+
expect(result).not.toBeNull()
|
|
59
|
+
expect(result).not.toContain("InputBox")
|
|
60
|
+
expect(result).toContain("<TapInput")
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe("InputBox has props", () => {
|
|
65
|
+
it("adds TODO to Input child and does not unwrap when InputBox has a single prop", () => {
|
|
66
|
+
const input = `
|
|
67
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
68
|
+
|
|
69
|
+
function Test() {
|
|
70
|
+
return (
|
|
71
|
+
<Box>
|
|
72
|
+
<Input.InputBox size="sm">
|
|
73
|
+
<Input id="name" value={name} />
|
|
74
|
+
</Input.InputBox>
|
|
75
|
+
</Box>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
`.trim()
|
|
79
|
+
|
|
80
|
+
const result = applyTransform(input)
|
|
81
|
+
expect(result).not.toBeNull()
|
|
82
|
+
expect(result).toContain("InputBox")
|
|
83
|
+
expect(result).toContain("TODO: tapestry-migration (removeInputBox)")
|
|
84
|
+
expect(result).toContain(
|
|
85
|
+
"Tapestry doesn't support InputBox passing props to children (size)"
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it("lists all props in the TODO comment when InputBox has multiple props", () => {
|
|
90
|
+
const input = `
|
|
91
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
92
|
+
|
|
93
|
+
function Test() {
|
|
94
|
+
return (
|
|
95
|
+
<Box>
|
|
96
|
+
<Input.InputBox size="sm" spacing={1}>
|
|
97
|
+
<Input id="name" value={name} />
|
|
98
|
+
</Input.InputBox>
|
|
99
|
+
</Box>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
`.trim()
|
|
103
|
+
|
|
104
|
+
const result = applyTransform(input)
|
|
105
|
+
expect(result).not.toBeNull()
|
|
106
|
+
expect(result).toContain("InputBox")
|
|
107
|
+
expect(result).toContain("size, spacing")
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it("does not add TODO when Input has renderLeft/renderRight (not a convertible child)", () => {
|
|
111
|
+
const input = `
|
|
112
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
113
|
+
|
|
114
|
+
function Test() {
|
|
115
|
+
return (
|
|
116
|
+
<Box>
|
|
117
|
+
<Input.InputBox size="sm">
|
|
118
|
+
<Input id="name" renderLeft={<Icon />} value={name} />
|
|
119
|
+
</Input.InputBox>
|
|
120
|
+
</Box>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
`.trim()
|
|
124
|
+
|
|
125
|
+
const result = applyTransform(input)
|
|
126
|
+
expect(result).toBeNull()
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
describe("InputBox with multiple children", () => {
|
|
131
|
+
it("adds TODO to Input child and does not unwrap when InputBox has multiple non-whitespace children", () => {
|
|
132
|
+
const input = `
|
|
133
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
134
|
+
|
|
135
|
+
function Test() {
|
|
136
|
+
return (
|
|
137
|
+
<Box>
|
|
138
|
+
<Input.InputBox>
|
|
139
|
+
<Input id="name" value={name} />
|
|
140
|
+
<Text>helper</Text>
|
|
141
|
+
</Input.InputBox>
|
|
142
|
+
</Box>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
`.trim()
|
|
146
|
+
|
|
147
|
+
const result = applyTransform(input)
|
|
148
|
+
expect(result).not.toBeNull()
|
|
149
|
+
expect(result).toContain("InputBox")
|
|
150
|
+
expect(result).toContain("TODO: tapestry-migration (removeInputBox)")
|
|
151
|
+
expect(result).toContain("multiple children")
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it("returns null when InputBox has multiple children but none are convertible Input", () => {
|
|
155
|
+
const input = `
|
|
156
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
157
|
+
|
|
158
|
+
function Test() {
|
|
159
|
+
return (
|
|
160
|
+
<Box>
|
|
161
|
+
<Input.InputBox>
|
|
162
|
+
<Select value={val} />
|
|
163
|
+
<Text>helper</Text>
|
|
164
|
+
</Input.InputBox>
|
|
165
|
+
</Box>
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
`.trim()
|
|
169
|
+
|
|
170
|
+
const result = applyTransform(input)
|
|
171
|
+
expect(result).toBeNull()
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
describe("renderLeft/renderRight child with no props → no change", () => {
|
|
176
|
+
it("returns null when the single Input child has renderLeft", () => {
|
|
177
|
+
const input = `
|
|
178
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
179
|
+
|
|
180
|
+
function Test() {
|
|
181
|
+
return (
|
|
182
|
+
<Box>
|
|
183
|
+
<Input.InputBox>
|
|
184
|
+
<Input id="name" renderLeft={<Icon />} value={name} />
|
|
185
|
+
</Input.InputBox>
|
|
186
|
+
</Box>
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
`.trim()
|
|
190
|
+
|
|
191
|
+
const result = applyTransform(input)
|
|
192
|
+
expect(result).toBeNull()
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
it("returns null when the single Input child has renderRight", () => {
|
|
196
|
+
const input = `
|
|
197
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
198
|
+
|
|
199
|
+
function Test() {
|
|
200
|
+
return (
|
|
201
|
+
<Box>
|
|
202
|
+
<Input.InputBox>
|
|
203
|
+
<Input id="name" renderRight={<Icon />} value={name} />
|
|
204
|
+
</Input.InputBox>
|
|
205
|
+
</Box>
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
`.trim()
|
|
209
|
+
|
|
210
|
+
const result = applyTransform(input)
|
|
211
|
+
expect(result).toBeNull()
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
describe("non-Input child only → no change", () => {
|
|
216
|
+
it("returns null when InputBox contains only a non-Input child", () => {
|
|
217
|
+
const input = `
|
|
218
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
219
|
+
|
|
220
|
+
function Test() {
|
|
221
|
+
return (
|
|
222
|
+
<Box>
|
|
223
|
+
<Input.InputBox>
|
|
224
|
+
<Select value={val} />
|
|
225
|
+
</Input.InputBox>
|
|
226
|
+
</Box>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
`.trim()
|
|
230
|
+
|
|
231
|
+
const result = applyTransform(input)
|
|
232
|
+
expect(result).toBeNull()
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it("returns null when InputBox contains a JSX expression as its only child", () => {
|
|
236
|
+
const input = `
|
|
237
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
238
|
+
|
|
239
|
+
function Test() {
|
|
240
|
+
return (
|
|
241
|
+
<Box>
|
|
242
|
+
<Input.InputBox>
|
|
243
|
+
{condition && <Input id="name" value={name} />}
|
|
244
|
+
</Input.InputBox>
|
|
245
|
+
</Box>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
`.trim()
|
|
249
|
+
|
|
250
|
+
const result = applyTransform(input)
|
|
251
|
+
expect(result).toBeNull()
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it("returns null when InputBox has no non-whitespace children", () => {
|
|
255
|
+
const input = `
|
|
256
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
257
|
+
|
|
258
|
+
function Test() {
|
|
259
|
+
return (
|
|
260
|
+
<Box>
|
|
261
|
+
<Input.InputBox>
|
|
262
|
+
</Input.InputBox>
|
|
263
|
+
</Box>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
`.trim()
|
|
267
|
+
|
|
268
|
+
const result = applyTransform(input)
|
|
269
|
+
expect(result).toBeNull()
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
describe("root-level InputBox", () => {
|
|
274
|
+
it("unwraps root-level InputBox with single convertible Input child", () => {
|
|
275
|
+
const input = `
|
|
276
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
277
|
+
|
|
278
|
+
function NameInput() {
|
|
279
|
+
return (
|
|
280
|
+
<Input.InputBox>
|
|
281
|
+
<Input id="name" value={name} />
|
|
282
|
+
</Input.InputBox>
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
`.trim()
|
|
286
|
+
|
|
287
|
+
const result = applyTransform(input)
|
|
288
|
+
expect(result).not.toBeNull()
|
|
289
|
+
expect(result).not.toContain("Input.InputBox")
|
|
290
|
+
expect(result).toContain('<Input id="name"')
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
describe("not from tapestry-react → no change", () => {
|
|
295
|
+
it("returns null when Input is not imported from tapestry-react", () => {
|
|
296
|
+
const input = `
|
|
297
|
+
import { Input } from "@planningcenter/tapestry"
|
|
298
|
+
|
|
299
|
+
function Test() {
|
|
300
|
+
return (
|
|
301
|
+
<Box>
|
|
302
|
+
<Input.InputBox>
|
|
303
|
+
<Input id="name" value={name} />
|
|
304
|
+
</Input.InputBox>
|
|
305
|
+
</Box>
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
`.trim()
|
|
309
|
+
|
|
310
|
+
const result = applyTransform(input)
|
|
311
|
+
expect(result).toBeNull()
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it("returns null when no Input import exists at all", () => {
|
|
315
|
+
const input = `
|
|
316
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
317
|
+
|
|
318
|
+
function Test() {
|
|
319
|
+
return <Button label="Click" />
|
|
320
|
+
}
|
|
321
|
+
`.trim()
|
|
322
|
+
|
|
323
|
+
const result = applyTransform(input)
|
|
324
|
+
expect(result).toBeNull()
|
|
325
|
+
})
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
describe("integration with mergeInputLabel pipeline", () => {
|
|
329
|
+
it("after removeInputBox runs, Input is a direct sibling of InputLabel so mergeInputLabel can merge them", () => {
|
|
330
|
+
const input = `
|
|
331
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
332
|
+
|
|
333
|
+
function Test() {
|
|
334
|
+
return (
|
|
335
|
+
<Box>
|
|
336
|
+
<Input.InputLabel controls="input-name">Name</Input.InputLabel>
|
|
337
|
+
<Input.InputBox>
|
|
338
|
+
<Input id="input-name" value={name} />
|
|
339
|
+
</Input.InputBox>
|
|
340
|
+
</Box>
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
`.trim()
|
|
344
|
+
|
|
345
|
+
const result = applyTransform(input)
|
|
346
|
+
expect(result).not.toBeNull()
|
|
347
|
+
expect(result).not.toContain("InputBox")
|
|
348
|
+
expect(result).toContain("<Input.InputLabel")
|
|
349
|
+
expect(result).toContain("<Input")
|
|
350
|
+
})
|
|
351
|
+
})
|
|
352
|
+
})
|