@planningcenter/tapestry-migration-cli 3.1.0-rc.6 → 3.1.0-rc.7
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 +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 +8 -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 +391 -0
- package/src/components/input/transforms/mergeFieldIntoInput.ts +213 -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/removeTypeText.test.ts +160 -0
- package/src/components/input/transforms/removeTypeText.ts +18 -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/shared/helpers/unsupportedPropsHelpers.ts +52 -0
- package/src/components/shared/transformFactories/helpers/manageImports.ts +14 -12
- package/src/components/shared/transformFactories/sizeMappingFactory.ts +9 -2
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +54 -16
- package/src/components/shared/transformFactories/ternaryConditionalToPropFactory.ts +65 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { JSXElement, JSXText, Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
type JSXChild = NonNullable<JSXElement["children"]>[number]
|
|
4
|
+
|
|
5
|
+
import { addAttribute } from "../../shared/actions/addAttribute"
|
|
6
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
7
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
8
|
+
import { extractTextContent } from "../../shared/helpers/childrenToLabelHelpers"
|
|
9
|
+
import { getImportName } from "../../shared/transformFactories/helpers/manageImports"
|
|
10
|
+
|
|
11
|
+
const SCOPE = "inputLabelToLabelProp"
|
|
12
|
+
|
|
13
|
+
function isInputLabelElement(node: JSXChild, localName: string): boolean {
|
|
14
|
+
return (
|
|
15
|
+
node.type === "JSXElement" &&
|
|
16
|
+
node.openingElement.name.type === "JSXMemberExpression" &&
|
|
17
|
+
node.openingElement.name.object.type === "JSXIdentifier" &&
|
|
18
|
+
node.openingElement.name.object.name === localName &&
|
|
19
|
+
node.openingElement.name.property.name === "InputLabel"
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isInputElement(node: JSXChild, localName: string): boolean {
|
|
24
|
+
return (
|
|
25
|
+
node.type === "JSXElement" &&
|
|
26
|
+
node.openingElement.name.type === "JSXIdentifier" &&
|
|
27
|
+
node.openingElement.name.name === localName
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isHorizontalAxis(stackViewElement: JSXElement): boolean {
|
|
32
|
+
const axisAttr = getAttribute({ element: stackViewElement, name: "axis" })
|
|
33
|
+
if (!axisAttr) return false
|
|
34
|
+
const value = axisAttr.value
|
|
35
|
+
if (!value) return false
|
|
36
|
+
if (value.type === "StringLiteral" && value.value === "horizontal")
|
|
37
|
+
return true
|
|
38
|
+
if (
|
|
39
|
+
value.type === "JSXExpressionContainer" &&
|
|
40
|
+
value.expression.type === "StringLiteral" &&
|
|
41
|
+
value.expression.value === "horizontal"
|
|
42
|
+
)
|
|
43
|
+
return true
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasRenderSideProp(inputElement: JSXElement): boolean {
|
|
48
|
+
const attrs = inputElement.openingElement.attributes || []
|
|
49
|
+
return attrs.some(
|
|
50
|
+
(attr) =>
|
|
51
|
+
attr.type === "JSXAttribute" &&
|
|
52
|
+
attr.name.type === "JSXIdentifier" &&
|
|
53
|
+
(attr.name.name === "renderLeft" || attr.name.name === "renderRight")
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const transform: Transform = (fileInfo, api) => {
|
|
58
|
+
const j = api.jscodeshift
|
|
59
|
+
const source = j(fileInfo.source)
|
|
60
|
+
|
|
61
|
+
const inputLocalName = getImportName(
|
|
62
|
+
"Input",
|
|
63
|
+
"@planningcenter/tapestry-react",
|
|
64
|
+
{
|
|
65
|
+
j,
|
|
66
|
+
source,
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
if (!inputLocalName) return null
|
|
70
|
+
|
|
71
|
+
const stackViewLocalName = getImportName(
|
|
72
|
+
"StackView",
|
|
73
|
+
"@planningcenter/tapestry-react",
|
|
74
|
+
{
|
|
75
|
+
j,
|
|
76
|
+
source,
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
if (!stackViewLocalName) return null
|
|
80
|
+
|
|
81
|
+
let hasChanges = false
|
|
82
|
+
|
|
83
|
+
source.find(j.JSXElement).forEach((path) => {
|
|
84
|
+
const el = path.value
|
|
85
|
+
const opening = el.openingElement
|
|
86
|
+
|
|
87
|
+
if (opening.name.type !== "JSXIdentifier") return
|
|
88
|
+
if (opening.name.name !== stackViewLocalName) return
|
|
89
|
+
|
|
90
|
+
if (isHorizontalAxis(el)) return
|
|
91
|
+
|
|
92
|
+
const children = el.children || []
|
|
93
|
+
let i = 0
|
|
94
|
+
|
|
95
|
+
while (i < children.length) {
|
|
96
|
+
const child = children[i]
|
|
97
|
+
|
|
98
|
+
// Skip whitespace JSXText nodes
|
|
99
|
+
if (child.type === "JSXText" && child.value.trim() === "") {
|
|
100
|
+
i++
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Find next non-whitespace sibling
|
|
105
|
+
let nextIdx = i + 1
|
|
106
|
+
while (
|
|
107
|
+
nextIdx < children.length &&
|
|
108
|
+
children[nextIdx].type === "JSXText" &&
|
|
109
|
+
(children[nextIdx] as JSXText).value.trim() === ""
|
|
110
|
+
) {
|
|
111
|
+
nextIdx++
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (nextIdx >= children.length) {
|
|
115
|
+
i++
|
|
116
|
+
continue
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const nextChild = children[nextIdx]
|
|
120
|
+
|
|
121
|
+
let inputLabelEl: JSXElement | null = null
|
|
122
|
+
let inputEl: JSXElement | null = null
|
|
123
|
+
let labelFirst = false
|
|
124
|
+
|
|
125
|
+
if (
|
|
126
|
+
isInputLabelElement(child, inputLocalName) &&
|
|
127
|
+
isInputElement(nextChild, inputLocalName)
|
|
128
|
+
) {
|
|
129
|
+
inputLabelEl = child as JSXElement
|
|
130
|
+
inputEl = nextChild as JSXElement
|
|
131
|
+
labelFirst = true
|
|
132
|
+
} else if (
|
|
133
|
+
isInputElement(child, inputLocalName) &&
|
|
134
|
+
isInputLabelElement(nextChild, inputLocalName)
|
|
135
|
+
) {
|
|
136
|
+
inputEl = child as JSXElement
|
|
137
|
+
inputLabelEl = nextChild as JSXElement
|
|
138
|
+
labelFirst = false
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!inputLabelEl || !inputEl) {
|
|
142
|
+
i++
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Skip if Input has renderLeft or renderRight
|
|
147
|
+
if (hasRenderSideProp(inputEl)) {
|
|
148
|
+
i++
|
|
149
|
+
continue
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const { isSimpleText, textContent } = extractTextContent(
|
|
153
|
+
inputLabelEl.children || []
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if (!isSimpleText) {
|
|
157
|
+
addComment({
|
|
158
|
+
element: inputEl,
|
|
159
|
+
j,
|
|
160
|
+
scope: SCOPE,
|
|
161
|
+
source,
|
|
162
|
+
text: "InputLabel children are complex and cannot be auto-converted to a label prop. Please migrate manually.",
|
|
163
|
+
})
|
|
164
|
+
hasChanges = true
|
|
165
|
+
i = nextIdx + 1
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const existingLabel = getAttribute({ element: inputEl, name: "label" })
|
|
170
|
+
if (existingLabel) {
|
|
171
|
+
addComment({
|
|
172
|
+
element: inputEl,
|
|
173
|
+
j,
|
|
174
|
+
scope: SCOPE,
|
|
175
|
+
source,
|
|
176
|
+
text: `Input already has a label prop. The InputLabel had text: "${textContent}". Please review manually.`,
|
|
177
|
+
})
|
|
178
|
+
hasChanges = true
|
|
179
|
+
i = nextIdx + 1
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
addAttribute({ element: inputEl, j, name: "label", value: textContent })
|
|
184
|
+
|
|
185
|
+
if (labelFirst) {
|
|
186
|
+
// InputLabel is at i, Input is at nextIdx — remove InputLabel + whitespace between
|
|
187
|
+
children.splice(i, nextIdx - i)
|
|
188
|
+
// Input is now at i; advance past it
|
|
189
|
+
} else {
|
|
190
|
+
// Input is at i, InputLabel is at nextIdx — remove whitespace + InputLabel after Input
|
|
191
|
+
children.splice(i + 1, nextIdx - i)
|
|
192
|
+
// Input is still at i; advance past it
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
hasChanges = true
|
|
196
|
+
i++
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
return hasChanges ? source.toSource() : null
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export default transform
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./mergeFieldIntoInput"
|
|
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("mergeFieldIntoInput transform", () => {
|
|
18
|
+
describe("basic prop merging", () => {
|
|
19
|
+
it("merges label prop from Field into Input", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return (
|
|
25
|
+
<Box>
|
|
26
|
+
<Field label="Name"><Input /></Field>
|
|
27
|
+
</Box>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
`.trim()
|
|
31
|
+
|
|
32
|
+
const result = applyTransform(input)
|
|
33
|
+
expect(result).not.toBeNull()
|
|
34
|
+
expect(result).not.toContain("<Field")
|
|
35
|
+
expect(result).toContain('label="Name"')
|
|
36
|
+
expect(result).toContain("<Input")
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it("renames feedbackText to description on Input", () => {
|
|
40
|
+
const input = `
|
|
41
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
42
|
+
|
|
43
|
+
function Test() {
|
|
44
|
+
return (
|
|
45
|
+
<Field feedbackText="Required"><Input /></Field>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
`.trim()
|
|
49
|
+
|
|
50
|
+
const result = applyTransform(input)
|
|
51
|
+
expect(result).not.toBeNull()
|
|
52
|
+
expect(result).not.toContain("<Field")
|
|
53
|
+
expect(result).not.toContain("feedbackText")
|
|
54
|
+
expect(result).toContain('description="Required"')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it("copies state prop as-is to Input", () => {
|
|
58
|
+
const input = `
|
|
59
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
60
|
+
|
|
61
|
+
function Test() {
|
|
62
|
+
return (
|
|
63
|
+
<Field state="error"><Input /></Field>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
`.trim()
|
|
67
|
+
|
|
68
|
+
const result = applyTransform(input)
|
|
69
|
+
expect(result).not.toBeNull()
|
|
70
|
+
expect(result).not.toContain("<Field")
|
|
71
|
+
expect(result).toContain('state="error"')
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it("preserves dynamic label value", () => {
|
|
75
|
+
const input = `
|
|
76
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
77
|
+
|
|
78
|
+
function Test() {
|
|
79
|
+
return (
|
|
80
|
+
<Field label={labelText}><Input /></Field>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
`.trim()
|
|
84
|
+
|
|
85
|
+
const result = applyTransform(input)
|
|
86
|
+
expect(result).not.toBeNull()
|
|
87
|
+
expect(result).not.toContain("<Field")
|
|
88
|
+
expect(result).toContain("label={labelText}")
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe("unsupported props", () => {
|
|
93
|
+
it("adds TODO comment for helpContent and removes Field", () => {
|
|
94
|
+
const input = `
|
|
95
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
96
|
+
|
|
97
|
+
function Test() {
|
|
98
|
+
return (
|
|
99
|
+
<Field helpContent="Help"><Input /></Field>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
`.trim()
|
|
103
|
+
|
|
104
|
+
const result = applyTransform(input)
|
|
105
|
+
expect(result).not.toBeNull()
|
|
106
|
+
expect(result).not.toContain("<Field")
|
|
107
|
+
expect(result).toContain("TODO: tapestry-migration (mergeFieldIntoInput)")
|
|
108
|
+
expect(result).toContain("helpContent")
|
|
109
|
+
expect(result).toContain("not supported by Input")
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it("adds a comment per unsupported prop when multiple unsupported props are present", () => {
|
|
113
|
+
const input = `
|
|
114
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
115
|
+
|
|
116
|
+
function Test() {
|
|
117
|
+
return (
|
|
118
|
+
<Field inline compact spacing={8}><Input /></Field>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
`.trim()
|
|
122
|
+
|
|
123
|
+
const result = applyTransform(input)
|
|
124
|
+
expect(result).not.toBeNull()
|
|
125
|
+
expect(result).not.toContain("<Field")
|
|
126
|
+
expect(result).toContain("inline")
|
|
127
|
+
expect(result).toContain("compact")
|
|
128
|
+
expect(result).toContain("spacing")
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe("Field with no props", () => {
|
|
133
|
+
it("unwraps Field with no props cleanly", () => {
|
|
134
|
+
const input = `
|
|
135
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
136
|
+
|
|
137
|
+
function Test() {
|
|
138
|
+
return (
|
|
139
|
+
<Field><Input /></Field>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
`.trim()
|
|
143
|
+
|
|
144
|
+
const result = applyTransform(input)
|
|
145
|
+
expect(result).not.toBeNull()
|
|
146
|
+
expect(result).not.toContain("<Field")
|
|
147
|
+
expect(result).toContain("<Input")
|
|
148
|
+
expect(result).not.toContain("TODO")
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
describe("conflict detection", () => {
|
|
153
|
+
it("adds warning comment when Input already has label and keeps Input's existing label", () => {
|
|
154
|
+
const input = `
|
|
155
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
156
|
+
|
|
157
|
+
function Test() {
|
|
158
|
+
return (
|
|
159
|
+
<Field label="Name"><Input label="Other" /></Field>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
`.trim()
|
|
163
|
+
|
|
164
|
+
const result = applyTransform(input)
|
|
165
|
+
expect(result).not.toBeNull()
|
|
166
|
+
expect(result).not.toContain("<Field")
|
|
167
|
+
expect(result).toContain('label="Other"')
|
|
168
|
+
expect(result).toContain("TODO: tapestry-migration (mergeFieldIntoInput)")
|
|
169
|
+
expect(result).toContain("already has label")
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it("merges label from Field when Input has description but not label", () => {
|
|
173
|
+
const input = `
|
|
174
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
175
|
+
|
|
176
|
+
function Test() {
|
|
177
|
+
return (
|
|
178
|
+
<Field label="Name"><Input description="x" /></Field>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
`.trim()
|
|
182
|
+
|
|
183
|
+
const result = applyTransform(input)
|
|
184
|
+
expect(result).not.toBeNull()
|
|
185
|
+
expect(result).not.toContain("<Field")
|
|
186
|
+
expect(result).toContain('label="Name"')
|
|
187
|
+
expect(result).toContain('description="x"')
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe("multiple children", () => {
|
|
192
|
+
it("adds comment to Input and leaves Field when Field has multiple children", () => {
|
|
193
|
+
const input = `
|
|
194
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
195
|
+
|
|
196
|
+
function Test() {
|
|
197
|
+
return (
|
|
198
|
+
<Field><Input /><button /></Field>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
`.trim()
|
|
202
|
+
|
|
203
|
+
const result = applyTransform(input)
|
|
204
|
+
expect(result).not.toBeNull()
|
|
205
|
+
expect(result).toContain("<Field")
|
|
206
|
+
expect(result).toContain("TODO: tapestry-migration (mergeFieldIntoInput)")
|
|
207
|
+
expect(result).toContain("multiple children")
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
describe("non-Input child", () => {
|
|
212
|
+
it("returns null when Field contains only a non-Input child", () => {
|
|
213
|
+
const input = `
|
|
214
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
215
|
+
|
|
216
|
+
function Test() {
|
|
217
|
+
return (
|
|
218
|
+
<Field><textarea /></Field>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
`.trim()
|
|
222
|
+
|
|
223
|
+
const result = applyTransform(input)
|
|
224
|
+
expect(result).toBeNull()
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe("import cleanup", () => {
|
|
229
|
+
it("removes Field from import specifiers when all Fields are converted", () => {
|
|
230
|
+
const input = `
|
|
231
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
232
|
+
|
|
233
|
+
function Test() {
|
|
234
|
+
return (
|
|
235
|
+
<Field label="Name"><Input /></Field>
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
`.trim()
|
|
239
|
+
|
|
240
|
+
const result = applyTransform(input)
|
|
241
|
+
expect(result).not.toBeNull()
|
|
242
|
+
expect(result).not.toContain("Field")
|
|
243
|
+
expect(result).toContain("Input")
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it("removes entire import declaration when Field was the only specifier", () => {
|
|
247
|
+
const input = `
|
|
248
|
+
import { Field } from "@planningcenter/tapestry-react"
|
|
249
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
250
|
+
|
|
251
|
+
function Test() {
|
|
252
|
+
return (
|
|
253
|
+
<Field><Input /></Field>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
`.trim()
|
|
257
|
+
|
|
258
|
+
const result = applyTransform(input)
|
|
259
|
+
expect(result).not.toBeNull()
|
|
260
|
+
expect(result).not.toContain("{ Field }")
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it("keeps Field in import when some Fields cannot be converted", () => {
|
|
264
|
+
const input = `
|
|
265
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
266
|
+
|
|
267
|
+
function Test() {
|
|
268
|
+
return (
|
|
269
|
+
<div>
|
|
270
|
+
<Field label="Name"><Input /></Field>
|
|
271
|
+
<Field><Input /><button /></Field>
|
|
272
|
+
</div>
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
`.trim()
|
|
276
|
+
|
|
277
|
+
const result = applyTransform(input)
|
|
278
|
+
expect(result).not.toBeNull()
|
|
279
|
+
expect(result).toContain("Field")
|
|
280
|
+
})
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
describe("full prop set", () => {
|
|
284
|
+
it("merges label, feedbackText, state and flags helpContent as unsupported", () => {
|
|
285
|
+
const input = `
|
|
286
|
+
import { Field, Input } from "@planningcenter/tapestry-react"
|
|
287
|
+
|
|
288
|
+
function Test() {
|
|
289
|
+
return (
|
|
290
|
+
<Field label="Name" feedbackText="Required" state="error" helpContent="Help">
|
|
291
|
+
<Input />
|
|
292
|
+
</Field>
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
`.trim()
|
|
296
|
+
|
|
297
|
+
const result = applyTransform(input)
|
|
298
|
+
expect(result).not.toBeNull()
|
|
299
|
+
expect(result).not.toContain("<Field")
|
|
300
|
+
expect(result).toContain('label="Name"')
|
|
301
|
+
expect(result).toContain('description="Required"')
|
|
302
|
+
expect(result).toContain('state="error"')
|
|
303
|
+
expect(result).toContain("helpContent")
|
|
304
|
+
expect(result).toContain("not supported by Input")
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
describe("not from tapestry-react", () => {
|
|
309
|
+
it("returns null when Field is not imported from tapestry-react", () => {
|
|
310
|
+
const input = `
|
|
311
|
+
import { Field } from "some-other-library"
|
|
312
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
313
|
+
|
|
314
|
+
function Test() {
|
|
315
|
+
return (
|
|
316
|
+
<Field label="Name"><Input /></Field>
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
`.trim()
|
|
320
|
+
|
|
321
|
+
const result = applyTransform(input)
|
|
322
|
+
expect(result).toBeNull()
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it("returns null when Input is not imported from tapestry-react", () => {
|
|
326
|
+
const input = `
|
|
327
|
+
import { Field } from "@planningcenter/tapestry-react"
|
|
328
|
+
import { Input } from "some-other-library"
|
|
329
|
+
|
|
330
|
+
function Test() {
|
|
331
|
+
return (
|
|
332
|
+
<Field label="Name"><Input /></Field>
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
`.trim()
|
|
336
|
+
|
|
337
|
+
const result = applyTransform(input)
|
|
338
|
+
expect(result).toBeNull()
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it("returns null when neither import exists", () => {
|
|
342
|
+
const input = `
|
|
343
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
344
|
+
|
|
345
|
+
function Test() {
|
|
346
|
+
return <Button label="Click" />
|
|
347
|
+
}
|
|
348
|
+
`.trim()
|
|
349
|
+
|
|
350
|
+
const result = applyTransform(input)
|
|
351
|
+
expect(result).toBeNull()
|
|
352
|
+
})
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
describe("aliased imports", () => {
|
|
356
|
+
it("handles aliased Field import", () => {
|
|
357
|
+
const input = `
|
|
358
|
+
import { Field as TapField, Input } from "@planningcenter/tapestry-react"
|
|
359
|
+
|
|
360
|
+
function Test() {
|
|
361
|
+
return (
|
|
362
|
+
<TapField label="Name"><Input /></TapField>
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
`.trim()
|
|
366
|
+
|
|
367
|
+
const result = applyTransform(input)
|
|
368
|
+
expect(result).not.toBeNull()
|
|
369
|
+
expect(result).not.toContain("<TapField")
|
|
370
|
+
expect(result).toContain('label="Name"')
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
it("handles aliased Input import", () => {
|
|
374
|
+
const input = `
|
|
375
|
+
import { Field, Input as TapInput } from "@planningcenter/tapestry-react"
|
|
376
|
+
|
|
377
|
+
function Test() {
|
|
378
|
+
return (
|
|
379
|
+
<Field label="Name"><TapInput /></Field>
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
`.trim()
|
|
383
|
+
|
|
384
|
+
const result = applyTransform(input)
|
|
385
|
+
expect(result).not.toBeNull()
|
|
386
|
+
expect(result).not.toContain("<Field")
|
|
387
|
+
expect(result).toContain('label="Name"')
|
|
388
|
+
expect(result).toContain("<TapInput")
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
})
|