@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.
Files changed (115) hide show
  1. package/dist/tapestry-react-shim.cjs +7 -1
  2. package/package.json +3 -3
  3. package/src/components/button/transforms/convertStyleProps.test.ts +97 -0
  4. package/src/components/button/transforms/removeTypeButton.test.ts +0 -1
  5. package/src/components/checkbox/transforms/moveCheckboxImport.test.ts +3 -0
  6. package/src/components/input/index.ts +66 -0
  7. package/src/components/input/transformableInput.ts +49 -0
  8. package/src/components/input/transforms/auditSpreadProps.test.ts +192 -0
  9. package/src/components/input/transforms/auditSpreadProps.ts +26 -0
  10. package/src/components/input/transforms/autoWidthTransform.test.ts +172 -0
  11. package/src/components/input/transforms/autoWidthTransform.ts +41 -0
  12. package/src/components/input/transforms/convertStyleProps.test.ts +128 -0
  13. package/src/components/input/transforms/convertStyleProps.ts +12 -0
  14. package/src/components/input/transforms/highlightOnInteractionToSelectTextOnFocus.test.ts +186 -0
  15. package/src/components/input/transforms/highlightOnInteractionToSelectTextOnFocus.ts +27 -0
  16. package/src/components/input/transforms/inputLabelToLabelProp.test.ts +319 -0
  17. package/src/components/input/transforms/inputLabelToLabelProp.ts +203 -0
  18. package/src/components/input/transforms/mergeFieldIntoInput.test.ts +469 -0
  19. package/src/components/input/transforms/mergeFieldIntoInput.ts +7 -0
  20. package/src/components/input/transforms/mergeInputLabel.test.ts +458 -0
  21. package/src/components/input/transforms/mergeInputLabel.ts +204 -0
  22. package/src/components/input/transforms/moveInputImport.test.ts +166 -0
  23. package/src/components/input/transforms/moveInputImport.ts +14 -0
  24. package/src/components/input/transforms/numberFieldAddTypeNumber.test.ts +92 -0
  25. package/src/components/input/transforms/numberFieldAddTypeNumber.ts +14 -0
  26. package/src/components/input/transforms/numberFieldRenameToInput.test.ts +126 -0
  27. package/src/components/input/transforms/numberFieldRenameToInput.ts +9 -0
  28. package/src/components/input/transforms/removeAsInput.test.ts +139 -0
  29. package/src/components/input/transforms/removeAsInput.ts +20 -0
  30. package/src/components/input/transforms/removeDuplicateKeys.test.ts +302 -0
  31. package/src/components/input/transforms/removeDuplicateKeys.ts +10 -0
  32. package/src/components/input/transforms/removeInputBox.test.ts +352 -0
  33. package/src/components/input/transforms/removeInputBox.ts +109 -0
  34. package/src/components/input/transforms/removeRedundantAriaLabel.test.ts +128 -0
  35. package/src/components/input/transforms/removeRedundantAriaLabel.ts +21 -0
  36. package/src/components/input/transforms/removeTypeInput.test.ts +212 -0
  37. package/src/components/input/transforms/removeTypeInput.ts +22 -0
  38. package/src/components/input/transforms/removeTypeText.test.ts +160 -0
  39. package/src/components/input/transforms/removeTypeText.ts +17 -0
  40. package/src/components/input/transforms/sizeMapping.test.ts +198 -0
  41. package/src/components/input/transforms/sizeMapping.ts +17 -0
  42. package/src/components/input/transforms/skipRenderSideProps.test.ts +236 -0
  43. package/src/components/input/transforms/skipRenderSideProps.ts +27 -0
  44. package/src/components/input/transforms/stateToInvalid.test.ts +208 -0
  45. package/src/components/input/transforms/stateToInvalid.ts +59 -0
  46. package/src/components/input/transforms/stateToInvalidTernary.test.ts +159 -0
  47. package/src/components/input/transforms/stateToInvalidTernary.ts +13 -0
  48. package/src/components/input/transforms/unsupportedProps.test.ts +566 -0
  49. package/src/components/input/transforms/unsupportedProps.ts +84 -0
  50. package/src/components/link/transforms/reviewStyles.test.ts +0 -1
  51. package/src/components/select/index.ts +58 -0
  52. package/src/components/select/transformableSelect.ts +7 -0
  53. package/src/components/select/transforms/auditSpreadProps.test.ts +103 -0
  54. package/src/components/select/transforms/auditSpreadProps.ts +26 -0
  55. package/src/components/select/transforms/childrenToOptions.test.ts +367 -0
  56. package/src/components/select/transforms/childrenToOptions.ts +295 -0
  57. package/src/components/select/transforms/convertLegacyOptions.test.ts +150 -0
  58. package/src/components/select/transforms/convertLegacyOptions.ts +105 -0
  59. package/src/components/select/transforms/convertStyleProps.test.ts +73 -0
  60. package/src/components/select/transforms/convertStyleProps.ts +12 -0
  61. package/src/components/select/transforms/emptyValueToPlaceholder.test.ts +122 -0
  62. package/src/components/select/transforms/emptyValueToPlaceholder.ts +22 -0
  63. package/src/components/select/transforms/innerRefToRef.test.ts +89 -0
  64. package/src/components/select/transforms/innerRefToRef.ts +18 -0
  65. package/src/components/select/transforms/mapChildrenToOptions.test.ts +521 -0
  66. package/src/components/select/transforms/mapChildrenToOptions.ts +312 -0
  67. package/src/components/select/transforms/mergeFieldIntoSelect.test.ts +506 -0
  68. package/src/components/select/transforms/mergeFieldIntoSelect.ts +7 -0
  69. package/src/components/select/transforms/mergeSelectLabel.test.ts +458 -0
  70. package/src/components/select/transforms/mergeSelectLabel.ts +225 -0
  71. package/src/components/select/transforms/moveSelectImport.test.ts +148 -0
  72. package/src/components/select/transforms/moveSelectImport.ts +14 -0
  73. package/src/components/select/transforms/removeDefaultProps.test.ts +249 -0
  74. package/src/components/select/transforms/removeDefaultProps.ts +112 -0
  75. package/src/components/select/transforms/sizeMapping.test.ts +188 -0
  76. package/src/components/select/transforms/sizeMapping.ts +17 -0
  77. package/src/components/select/transforms/skipMultipleSelect.test.ts +148 -0
  78. package/src/components/select/transforms/skipMultipleSelect.ts +23 -0
  79. package/src/components/select/transforms/stateToInvalid.test.ts +217 -0
  80. package/src/components/select/transforms/stateToInvalid.ts +59 -0
  81. package/src/components/select/transforms/stateToInvalidTernary.test.ts +146 -0
  82. package/src/components/select/transforms/stateToInvalidTernary.ts +13 -0
  83. package/src/components/select/transforms/unsupportedProps.test.ts +252 -0
  84. package/src/components/select/transforms/unsupportedProps.ts +44 -0
  85. package/src/components/shared/helpers/getAttributeExpression.ts +26 -0
  86. package/src/components/shared/helpers/unsupportedPropsHelpers.ts +102 -0
  87. package/src/components/shared/transformFactories/helpers/manageImports.ts +14 -12
  88. package/src/components/shared/transformFactories/mergeFieldFactory.ts +244 -0
  89. package/src/components/shared/transformFactories/sizeMappingFactory.ts +9 -2
  90. package/src/components/shared/transformFactories/stylePropTransformFactory.ts +56 -17
  91. package/src/components/shared/transformFactories/ternaryConditionalToPropFactory.ts +65 -0
  92. package/src/components/text-area/index.ts +48 -0
  93. package/src/components/text-area/transforms/auditSpreadProps.test.ts +139 -0
  94. package/src/components/text-area/transforms/auditSpreadProps.ts +10 -0
  95. package/src/components/text-area/transforms/convertStyleProps.test.ts +158 -0
  96. package/src/components/text-area/transforms/convertStyleProps.ts +10 -0
  97. package/src/components/text-area/transforms/innerRefToRef.test.ts +206 -0
  98. package/src/components/text-area/transforms/innerRefToRef.ts +14 -0
  99. package/src/components/text-area/transforms/mergeFieldIntoTextArea.test.ts +477 -0
  100. package/src/components/text-area/transforms/mergeFieldIntoTextArea.ts +5 -0
  101. package/src/components/text-area/transforms/moveTextAreaImport.test.ts +168 -0
  102. package/src/components/text-area/transforms/moveTextAreaImport.ts +13 -0
  103. package/src/components/text-area/transforms/removeDuplicateKeys.test.ts +129 -0
  104. package/src/components/text-area/transforms/removeDuplicateKeys.ts +8 -0
  105. package/src/components/text-area/transforms/removeRedundantAriaLabel.test.ts +183 -0
  106. package/src/components/text-area/transforms/removeRedundantAriaLabel.ts +59 -0
  107. package/src/components/text-area/transforms/sizeMapping.test.ts +199 -0
  108. package/src/components/text-area/transforms/sizeMapping.ts +15 -0
  109. package/src/components/text-area/transforms/stateToInvalid.test.ts +204 -0
  110. package/src/components/text-area/transforms/stateToInvalid.ts +57 -0
  111. package/src/components/text-area/transforms/stateToInvalidTernary.test.ts +133 -0
  112. package/src/components/text-area/transforms/stateToInvalidTernary.ts +11 -0
  113. package/src/components/text-area/transforms/unsupportedProps.test.ts +275 -0
  114. package/src/components/text-area/transforms/unsupportedProps.ts +35 -0
  115. package/src/index.ts +2 -1
@@ -0,0 +1,166 @@
1
+ import jscodeshift from "jscodeshift"
2
+ import { describe, expect, it } from "vitest"
3
+
4
+ import transform from "./moveInputImport"
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("moveInputImport transform", () => {
19
+ describe("import migration", () => {
20
+ it("should change import from tapestry-react to tapestry", () => {
21
+ const input = `
22
+ import { Input } from "@planningcenter/tapestry-react"
23
+
24
+ function Test() {
25
+ return <Input label="Name" />
26
+ }
27
+ `.trim()
28
+
29
+ const result = applyTransform(input)
30
+ expect(result).toContain(
31
+ 'import { Input } from "@planningcenter/tapestry"'
32
+ )
33
+ expect(result).not.toContain("@planningcenter/tapestry-react")
34
+ })
35
+
36
+ it("should only move Input, leaving other imports in place", () => {
37
+ const input = `
38
+ import { Button, Input } from "@planningcenter/tapestry-react"
39
+
40
+ function Test() {
41
+ return (
42
+ <div>
43
+ <Button>Click</Button>
44
+ <Input label="Name" />
45
+ </div>
46
+ )
47
+ }
48
+ `.trim()
49
+
50
+ const result = applyTransform(input)
51
+ expect(result).toContain(
52
+ 'import { Button } from "@planningcenter/tapestry-react"'
53
+ )
54
+ expect(result).toContain(
55
+ 'import { Input } from "@planningcenter/tapestry"'
56
+ )
57
+ })
58
+
59
+ it("should handle Input as sole import in declaration", () => {
60
+ const input = `
61
+ import { Button } from "@planningcenter/tapestry-react"
62
+ import { Input } from "@planningcenter/tapestry-react"
63
+
64
+ function Test() {
65
+ return (
66
+ <div>
67
+ <Button>Click</Button>
68
+ <Input label="Name" />
69
+ </div>
70
+ )
71
+ }
72
+ `.trim()
73
+
74
+ const result = applyTransform(input)
75
+ expect(result).toContain(
76
+ 'import { Button } from "@planningcenter/tapestry-react"'
77
+ )
78
+ expect(result).toContain(
79
+ 'import { Input } from "@planningcenter/tapestry"'
80
+ )
81
+ })
82
+
83
+ it("should not affect other components", () => {
84
+ const input = `
85
+ import { Button } from "@planningcenter/tapestry-react"
86
+
87
+ function Test() {
88
+ return <Button>Click me</Button>
89
+ }
90
+ `.trim()
91
+
92
+ const result = applyTransform(input)
93
+ expect(result).toContain(
94
+ 'import { Button } from "@planningcenter/tapestry-react"'
95
+ )
96
+ })
97
+ })
98
+
99
+ describe("edge cases", () => {
100
+ it("should handle already migrated imports", () => {
101
+ const input = `
102
+ import { Input } from "@planningcenter/tapestry"
103
+
104
+ function Test() {
105
+ return <Input label="Name" />
106
+ }
107
+ `.trim()
108
+
109
+ const result = applyTransform(input)
110
+ expect(result).toContain(
111
+ 'import { Input } from "@planningcenter/tapestry"'
112
+ )
113
+ })
114
+
115
+ it("should handle no imports", () => {
116
+ const input = `
117
+ function Test() {
118
+ return <div>No imports</div>
119
+ }
120
+ `.trim()
121
+
122
+ const result = applyTransform(input)
123
+ expect(result).toBe(input)
124
+ })
125
+
126
+ it("should preserve all attributes", () => {
127
+ const input = `
128
+ import { Input } from "@planningcenter/tapestry-react"
129
+
130
+ function Test() {
131
+ return (
132
+ <Input
133
+ label="Name"
134
+ placeholder="Enter name"
135
+ disabled
136
+ onChange={() => {}}
137
+ />
138
+ )
139
+ }
140
+ `.trim()
141
+
142
+ const result = applyTransform(input)
143
+ expect(result).toContain(
144
+ 'import { Input } from "@planningcenter/tapestry"'
145
+ )
146
+ expect(result).toContain('label="Name"')
147
+ expect(result).toContain('placeholder="Enter name"')
148
+ expect(result).toContain("disabled")
149
+ expect(result).toContain("onChange={() => {}}")
150
+ })
151
+
152
+ it("should handle alias import", () => {
153
+ const input = `
154
+ import { Input as TapestryInput } from "@planningcenter/tapestry-react"
155
+
156
+ function Test() {
157
+ return <TapestryInput label="Name" />
158
+ }
159
+ `.trim()
160
+
161
+ const result = applyTransform(input)
162
+ expect(result).toContain("@planningcenter/tapestry")
163
+ expect(result).not.toContain("@planningcenter/tapestry-react")
164
+ })
165
+ })
166
+ })
@@ -0,0 +1,14 @@
1
+ import { Transform } from "jscodeshift"
2
+
3
+ import { componentTransformFactory } from "../../shared/transformFactories/componentTransformFactory"
4
+ import { transformableInput } from "../transformableInput"
5
+
6
+ const transform: Transform = componentTransformFactory({
7
+ condition: transformableInput,
8
+ fromComponent: "Input",
9
+ fromPackage: "@planningcenter/tapestry-react",
10
+ toComponent: "Input",
11
+ toPackage: "@planningcenter/tapestry",
12
+ })
13
+
14
+ export default transform
@@ -0,0 +1,92 @@
1
+ import jscodeshift from "jscodeshift"
2
+ import { describe, expect, it } from "vitest"
3
+
4
+ import transform from "./numberFieldAddTypeNumber"
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("numberFieldAddTypeNumber transform", () => {
18
+ it("adds type='number' to NumberField", () => {
19
+ const input = `
20
+ import { NumberField } from "@planningcenter/tapestry-react"
21
+
22
+ function Test() {
23
+ return <NumberField label="Amount" />
24
+ }
25
+ `.trim()
26
+
27
+ const result = applyTransform(input)
28
+ expect(result).not.toBeNull()
29
+ expect(result).toContain('type="number"')
30
+ })
31
+
32
+ it("preserves other props when adding type", () => {
33
+ const input = `
34
+ import { NumberField } from "@planningcenter/tapestry-react"
35
+
36
+ function Test() {
37
+ return <NumberField label="Amount" value={42} onChange={handleChange} />
38
+ }
39
+ `.trim()
40
+
41
+ const result = applyTransform(input)
42
+ expect(result).toContain('type="number"')
43
+ expect(result).toContain('label="Amount"')
44
+ expect(result).toContain("value={42}")
45
+ expect(result).toContain("onChange={handleChange}")
46
+ })
47
+
48
+ it("does not add type if already present", () => {
49
+ const input = `
50
+ import { NumberField } from "@planningcenter/tapestry-react"
51
+
52
+ function Test() {
53
+ return <NumberField label="Amount" type="number" />
54
+ }
55
+ `.trim()
56
+
57
+ const result = applyTransform(input)
58
+ expect(result).toBeNull()
59
+ })
60
+
61
+ it("returns null when not imported from @planningcenter/tapestry-react", () => {
62
+ const input = `
63
+ import { NumberField } from "other-library"
64
+
65
+ function Test() {
66
+ return <NumberField label="Amount" />
67
+ }
68
+ `.trim()
69
+
70
+ expect(applyTransform(input)).toBeNull()
71
+ })
72
+
73
+ it("handles multiple NumberField elements", () => {
74
+ const input = `
75
+ import { NumberField } from "@planningcenter/tapestry-react"
76
+
77
+ function Test() {
78
+ return (
79
+ <div>
80
+ <NumberField label="Qty" />
81
+ <NumberField label="Price" />
82
+ </div>
83
+ )
84
+ }
85
+ `.trim()
86
+
87
+ const result = applyTransform(input)
88
+ expect(result).not.toBeNull()
89
+ const matches = result!.match(/type="number"/g)
90
+ expect(matches).toHaveLength(2)
91
+ })
92
+ })
@@ -0,0 +1,14 @@
1
+ import { addAttribute } from "../../shared/actions/addAttribute"
2
+ import { getAttribute } from "../../shared/actions/getAttribute"
3
+ import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
4
+
5
+ export default attributeTransformFactory({
6
+ targetComponent: "NumberField",
7
+ targetPackage: "@planningcenter/tapestry-react",
8
+ transform: (element, { j }) => {
9
+ if (getAttribute({ element, name: "type" })) return false
10
+
11
+ addAttribute({ element, j, name: "type", value: "number" })
12
+ return true
13
+ },
14
+ })
@@ -0,0 +1,126 @@
1
+ import jscodeshift from "jscodeshift"
2
+ import { describe, expect, it } from "vitest"
3
+
4
+ import transform from "./numberFieldRenameToInput"
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("numberFieldRenameToInput transform", () => {
18
+ it("renames NumberField to Input", () => {
19
+ const input = `
20
+ import { NumberField } from "@planningcenter/tapestry-react"
21
+
22
+ function Test() {
23
+ return <NumberField label="Amount" type="number" />
24
+ }
25
+ `.trim()
26
+
27
+ const result = applyTransform(input)
28
+ expect(result).not.toBeNull()
29
+ expect(result).toContain("<Input")
30
+ expect(result).not.toContain("NumberField")
31
+ })
32
+
33
+ it("updates import from tapestry-react to tapestry", () => {
34
+ const input = `
35
+ import { NumberField } from "@planningcenter/tapestry-react"
36
+
37
+ function Test() {
38
+ return <NumberField label="Amount" type="number" />
39
+ }
40
+ `.trim()
41
+
42
+ const result = applyTransform(input)
43
+ expect(result).toContain('@planningcenter/tapestry"')
44
+ expect(result).not.toContain("@planningcenter/tapestry-react")
45
+ })
46
+
47
+ it("returns null when not imported from @planningcenter/tapestry-react", () => {
48
+ const input = `
49
+ import { NumberField } from "other-library"
50
+
51
+ function Test() {
52
+ return <NumberField label="Amount" />
53
+ }
54
+ `.trim()
55
+
56
+ expect(applyTransform(input)).toBeNull()
57
+ })
58
+
59
+ it("handles multiple NumberField elements", () => {
60
+ const input = `
61
+ import { NumberField } from "@planningcenter/tapestry-react"
62
+
63
+ function Test() {
64
+ return (
65
+ <div>
66
+ <NumberField label="Qty" type="number" />
67
+ <NumberField label="Price" type="number" />
68
+ </div>
69
+ )
70
+ }
71
+ `.trim()
72
+
73
+ const result = applyTransform(input)
74
+ expect(result).not.toBeNull()
75
+ expect(result).not.toContain("NumberField")
76
+ const inputMatches = result!.match(/<Input/g)
77
+ expect(inputMatches).toHaveLength(2)
78
+ })
79
+
80
+ it("does not create duplicate Input identifier when file already imports Input from tapestry-react", () => {
81
+ const input = `
82
+ import { Input, NumberField } from "@planningcenter/tapestry-react"
83
+
84
+ function Test() {
85
+ return (
86
+ <div>
87
+ <Input label="Name" />
88
+ <NumberField label="Amount" type="number" />
89
+ </div>
90
+ )
91
+ }
92
+ `.trim()
93
+
94
+ const result = applyTransform(input)
95
+ expect(result).not.toBeNull()
96
+ expect(result).not.toContain("NumberField")
97
+ // Should not produce two separate Input declarations (would cause parse error)
98
+ const importDeclarationMatches = result!.match(/import.*Input/g)
99
+ expect(importDeclarationMatches).toHaveLength(1)
100
+ })
101
+
102
+ it("merges Input import when file already imports Input from tapestry", () => {
103
+ const input = `
104
+ import { Input } from "@planningcenter/tapestry"
105
+ import { NumberField } from "@planningcenter/tapestry-react"
106
+
107
+ function Test() {
108
+ return (
109
+ <div>
110
+ <Input label="Name" />
111
+ <NumberField label="Amount" type="number" />
112
+ </div>
113
+ )
114
+ }
115
+ `.trim()
116
+
117
+ const result = applyTransform(input)
118
+ expect(result).not.toBeNull()
119
+ expect(result).not.toContain("NumberField")
120
+ // Should not produce two separate Input imports
121
+ const importMatches = result!.match(
122
+ /import.*Input.*from.*@planningcenter\/tapestry/g
123
+ )
124
+ expect(importMatches).toHaveLength(1)
125
+ })
126
+ })
@@ -0,0 +1,9 @@
1
+ import { componentTransformFactory } from "../../shared/transformFactories/componentTransformFactory"
2
+
3
+ export default componentTransformFactory({
4
+ condition: () => true,
5
+ fromComponent: "NumberField",
6
+ fromPackage: "@planningcenter/tapestry-react",
7
+ toComponent: "Input",
8
+ toPackage: "@planningcenter/tapestry",
9
+ })
@@ -0,0 +1,139 @@
1
+ import jscodeshift from "jscodeshift"
2
+ import { describe, expect, it } from "vitest"
3
+
4
+ import transform from "./removeAsInput"
5
+
6
+ const j = jscodeshift.withParser("tsx")
7
+
8
+ function applyTransform(source: string): string | null {
9
+ const fileInfo = { path: "test.tsx", source }
10
+ const api = {
11
+ j,
12
+ jscodeshift: j,
13
+ report: () => {},
14
+ stats: () => {},
15
+ }
16
+ const result = transform(fileInfo, api, {})
17
+ return result as string | null
18
+ }
19
+
20
+ describe("removeAsInput transform", () => {
21
+ describe("basic transformation", () => {
22
+ it("should remove as='input' from Input", () => {
23
+ const input = `
24
+ import { Input } from "@planningcenter/tapestry-react"
25
+
26
+ function Component() {
27
+ return <Input as="input" />
28
+ }
29
+ `
30
+
31
+ const result = applyTransform(input)
32
+
33
+ expect(result).toContain("<Input />")
34
+ expect(result).not.toContain('as="input"')
35
+ })
36
+
37
+ it('should remove as="input" from Input with other props', () => {
38
+ const input = `
39
+ import { Input } from "@planningcenter/tapestry-react"
40
+
41
+ function Component() {
42
+ return <Input as="input" onChange={handleChange} />
43
+ }
44
+ `
45
+
46
+ const result = applyTransform(input)
47
+
48
+ expect(result).toContain("<Input onChange={handleChange} />")
49
+ expect(result).not.toContain('as="input"')
50
+ })
51
+
52
+ it("should preserve Input without as prop", () => {
53
+ const input = `
54
+ import { Input } from "@planningcenter/tapestry-react"
55
+
56
+ function Component() {
57
+ return <Input onChange={handleChange} />
58
+ }
59
+ `
60
+
61
+ const result = applyTransform(input)
62
+
63
+ expect(result).toBeNull()
64
+ })
65
+
66
+ it("should preserve Input with as='textarea'", () => {
67
+ const input = `
68
+ import { Input } from "@planningcenter/tapestry-react"
69
+
70
+ function Component() {
71
+ return <Input as="textarea" />
72
+ }
73
+ `
74
+
75
+ const result = applyTransform(input)
76
+
77
+ expect(result).toBeNull()
78
+ })
79
+ })
80
+
81
+ describe("edge cases", () => {
82
+ it("should not affect other components", () => {
83
+ const input = `
84
+ import { Input } from "@planningcenter/tapestry-react"
85
+
86
+ function Component() {
87
+ return (
88
+ <div>
89
+ <Input as="input" />
90
+ <input as="input" />
91
+ </div>
92
+ )
93
+ }
94
+ `
95
+
96
+ const result = applyTransform(input)
97
+
98
+ expect(result).toContain("<Input />")
99
+ expect(result).toContain('<input as="input" />')
100
+ })
101
+
102
+ it("should handle expression syntax as={'input'}", () => {
103
+ const input = `
104
+ import { Input } from "@planningcenter/tapestry-react"
105
+
106
+ function Component() {
107
+ return <Input as={"input"} />
108
+ }
109
+ `
110
+
111
+ const result = applyTransform(input)
112
+
113
+ if (result) {
114
+ expect(result).toContain("<Input />")
115
+ expect(result).not.toContain('as={"input"}')
116
+ } else {
117
+ expect(input).toContain('as={"input"}')
118
+ }
119
+ })
120
+ })
121
+
122
+ describe("import handling", () => {
123
+ it("should not affect imports", () => {
124
+ const input = `
125
+ import { Input } from "@planningcenter/tapestry-react"
126
+
127
+ function Component() {
128
+ return <Input as="input" />
129
+ }
130
+ `
131
+
132
+ const result = applyTransform(input)
133
+
134
+ expect(result).toContain(
135
+ 'import { Input } from "@planningcenter/tapestry-react"'
136
+ )
137
+ })
138
+ })
139
+ })
@@ -0,0 +1,20 @@
1
+ import { Transform } from "jscodeshift"
2
+
3
+ import { removeAttribute } from "../../shared/actions/removeAttribute"
4
+ import { andConditions } from "../../shared/conditions/andConditions"
5
+ import { hasAttributeValue } from "../../shared/conditions/hasAttributeValue"
6
+ import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
7
+ import { transformableInput } from "../transformableInput"
8
+
9
+ const transform: Transform = attributeTransformFactory({
10
+ condition: andConditions(
11
+ transformableInput,
12
+ hasAttributeValue("as", "input")
13
+ ),
14
+ targetComponent: "Input",
15
+ targetPackage: "@planningcenter/tapestry-react",
16
+ transform: (element, { j, source }) =>
17
+ removeAttribute("as", { element, j, source }),
18
+ })
19
+
20
+ export default transform