@planningcenter/tapestry-migration-cli 3.2.2-rc.8 → 3.2.2
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/checkbox/transforms/moveCheckboxImport.test.ts +26 -0
- package/src/components/checkbox/transforms/moveCheckboxImport.ts +1 -0
- package/src/components/input/transforms/moveInputImport.test.ts +26 -0
- package/src/components/input/transforms/moveInputImport.ts +1 -0
- package/src/components/input/transforms/numberFieldRenameToInput.test.ts +51 -0
- package/src/components/input/transforms/numberFieldRenameToInput.ts +1 -0
- package/src/components/radio/transforms/moveRadioImport.test.ts +26 -0
- package/src/components/radio/transforms/moveRadioImport.ts +1 -0
- package/src/components/select/transforms/moveSelectImport.test.ts +24 -0
- package/src/components/select/transforms/moveSelectImport.ts +1 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +24 -0
- package/src/components/shared/transformFactories/helpers/manageImports.ts +25 -1
- package/src/components/shared/transformFactories/stylePropTransformFactory.test.ts +330 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +156 -15
- package/src/components/shared/transformFactories/ternaryConditionalToPropFactory.ts +17 -12
- package/src/components/text-area/transforms/moveTextAreaImport.test.ts +26 -0
- package/src/components/text-area/transforms/moveTextAreaImport.ts +1 -0
- package/src/components/time-field/index.ts +48 -0
- package/src/components/time-field/transforms/auditSpreadProps.test.ts +76 -0
- package/src/components/time-field/transforms/auditSpreadProps.ts +10 -0
- package/src/components/time-field/transforms/convertStyleProps.test.ts +43 -0
- package/src/components/time-field/transforms/convertStyleProps.ts +10 -0
- package/src/components/time-field/transforms/flagMinMax.test.ts +103 -0
- package/src/components/time-field/transforms/flagMinMax.ts +31 -0
- package/src/components/time-field/transforms/mergeFieldIntoTimeField.test.ts +106 -0
- package/src/components/time-field/transforms/mergeFieldIntoTimeField.ts +5 -0
- package/src/components/time-field/transforms/moveTimeFieldImport.test.ts +153 -0
- package/src/components/time-field/transforms/moveTimeFieldImport.ts +14 -0
- package/src/components/time-field/transforms/sizeMapping.test.ts +173 -0
- package/src/components/time-field/transforms/sizeMapping.ts +15 -0
- package/src/components/time-field/transforms/stateToInvalid.test.ts +87 -0
- package/src/components/time-field/transforms/stateToInvalid.ts +56 -0
- package/src/components/time-field/transforms/stateToInvalidTernary.test.ts +100 -0
- package/src/components/time-field/transforms/stateToInvalidTernary.ts +11 -0
- package/src/components/time-field/transforms/tupleToTime.test.ts +182 -0
- package/src/components/time-field/transforms/tupleToTime.ts +107 -0
- package/src/components/time-field/transforms/twelveHourClockToHourCycle.test.ts +117 -0
- package/src/components/time-field/transforms/twelveHourClockToHourCycle.ts +65 -0
- package/src/components/time-field/transforms/unsupportedProps.test.ts +160 -0
- package/src/components/time-field/transforms/unsupportedProps.ts +37 -0
- package/src/components/toggle-switch/transforms/moveToggleSwitchImport.test.ts +28 -0
- package/src/components/toggle-switch/transforms/moveToggleSwitchImport.ts +1 -0
- package/src/index.ts +2 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
|
|
4
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
5
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
6
|
+
|
|
7
|
+
const TIME_PROPS = ["min", "max"]
|
|
8
|
+
|
|
9
|
+
const transform: Transform = attributeTransformFactory({
|
|
10
|
+
targetComponent: "TimeField",
|
|
11
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
12
|
+
transform: (element, { j }) => {
|
|
13
|
+
let hasChanges = false
|
|
14
|
+
|
|
15
|
+
for (const propName of TIME_PROPS) {
|
|
16
|
+
const attr = getAttribute({ element, name: propName })
|
|
17
|
+
if (!attr) continue
|
|
18
|
+
|
|
19
|
+
addCommentToAttribute({
|
|
20
|
+
attribute: attr,
|
|
21
|
+
j,
|
|
22
|
+
text: `'${propName}' previously accepted an hour number (0–23). TimeField now expects a TimeValue (e.g. new Time(hour, minute)) from @internationalized/date. Migrate this value manually.`,
|
|
23
|
+
})
|
|
24
|
+
hasChanges = true
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return hasChanges
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export default transform
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./mergeFieldIntoTimeField"
|
|
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("mergeFieldIntoTimeField transform", () => {
|
|
18
|
+
it("merges Field label into TimeField and unwraps Field", () => {
|
|
19
|
+
const input = `
|
|
20
|
+
import { Field, TimeField } from "@planningcenter/tapestry-react"
|
|
21
|
+
|
|
22
|
+
function Test() {
|
|
23
|
+
return (
|
|
24
|
+
<Field label="Start time"><TimeField value={time} onChange={setTime} /></Field>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
`.trim()
|
|
28
|
+
|
|
29
|
+
const result = applyTransform(input)
|
|
30
|
+
expect(result).not.toBeNull()
|
|
31
|
+
expect(result).not.toContain("<Field")
|
|
32
|
+
expect(result).toContain('label="Start time"')
|
|
33
|
+
expect(result).toContain("<TimeField")
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it("renames feedbackText to description on TimeField", () => {
|
|
37
|
+
const input = `
|
|
38
|
+
import { Field, TimeField } from "@planningcenter/tapestry-react"
|
|
39
|
+
|
|
40
|
+
function Test() {
|
|
41
|
+
return (
|
|
42
|
+
<Field feedbackText="Required"><TimeField value={time} onChange={setTime} /></Field>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
`.trim()
|
|
46
|
+
|
|
47
|
+
const result = applyTransform(input)
|
|
48
|
+
expect(result).not.toBeNull()
|
|
49
|
+
expect(result).not.toContain("<Field")
|
|
50
|
+
expect(result).not.toContain("feedbackText")
|
|
51
|
+
expect(result).toContain('description="Required"')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("flags helpContent as unsupported and removes Field", () => {
|
|
55
|
+
const input = `
|
|
56
|
+
import { Field, TimeField } from "@planningcenter/tapestry-react"
|
|
57
|
+
|
|
58
|
+
function Test() {
|
|
59
|
+
return (
|
|
60
|
+
<Field helpContent="Help"><TimeField value={time} onChange={setTime} /></Field>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
`.trim()
|
|
64
|
+
|
|
65
|
+
const result = applyTransform(input)
|
|
66
|
+
expect(result).not.toBeNull()
|
|
67
|
+
expect(result).not.toContain("<Field")
|
|
68
|
+
expect(result).toContain(
|
|
69
|
+
"TODO: tapestry-migration (mergeFieldIntoTimeField)"
|
|
70
|
+
)
|
|
71
|
+
expect(result).toContain("helpContent")
|
|
72
|
+
expect(result).toContain("not supported by TimeField")
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("returns null when Field is not imported from tapestry-react", () => {
|
|
76
|
+
const input = `
|
|
77
|
+
import { Field } from "some-other-library"
|
|
78
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
79
|
+
|
|
80
|
+
function Test() {
|
|
81
|
+
return (
|
|
82
|
+
<Field label="Time"><TimeField value={time} onChange={setTime} /></Field>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
`.trim()
|
|
86
|
+
|
|
87
|
+
const result = applyTransform(input)
|
|
88
|
+
expect(result).toBeNull()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it("returns null when TimeField is not imported from tapestry-react", () => {
|
|
92
|
+
const input = `
|
|
93
|
+
import { Field } from "@planningcenter/tapestry-react"
|
|
94
|
+
import { TimeField } from "some-other-library"
|
|
95
|
+
|
|
96
|
+
function Test() {
|
|
97
|
+
return (
|
|
98
|
+
<Field label="Time"><TimeField value={time} onChange={setTime} /></Field>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
`.trim()
|
|
102
|
+
|
|
103
|
+
const result = applyTransform(input)
|
|
104
|
+
expect(result).toBeNull()
|
|
105
|
+
})
|
|
106
|
+
})
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./moveTimeFieldImport"
|
|
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("moveTimeFieldImport transform", () => {
|
|
19
|
+
describe("import migration", () => {
|
|
20
|
+
it("should change import package and keep TimeField name", () => {
|
|
21
|
+
const input = `
|
|
22
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
23
|
+
|
|
24
|
+
function Test() {
|
|
25
|
+
return <TimeField value={time} onChange={setTime} />
|
|
26
|
+
}
|
|
27
|
+
`.trim()
|
|
28
|
+
|
|
29
|
+
const result = applyTransform(input)
|
|
30
|
+
expect(result).toContain(
|
|
31
|
+
'import { TimeField } from "@planningcenter/tapestry"'
|
|
32
|
+
)
|
|
33
|
+
expect(result).not.toContain("@planningcenter/tapestry-react")
|
|
34
|
+
expect(result).toContain("<TimeField")
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("should only move TimeField, leaving other tapestry-react imports", () => {
|
|
38
|
+
const input = `
|
|
39
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
40
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
41
|
+
|
|
42
|
+
function Test() {
|
|
43
|
+
return (
|
|
44
|
+
<div>
|
|
45
|
+
<Button>Click</Button>
|
|
46
|
+
<TimeField value={time} onChange={setTime} />
|
|
47
|
+
</div>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
`.trim()
|
|
51
|
+
|
|
52
|
+
const result = applyTransform(input)
|
|
53
|
+
expect(result).toContain(
|
|
54
|
+
'import { Button } from "@planningcenter/tapestry-react"'
|
|
55
|
+
)
|
|
56
|
+
expect(result).toContain(
|
|
57
|
+
'import { TimeField } from "@planningcenter/tapestry"'
|
|
58
|
+
)
|
|
59
|
+
expect(result).toContain("<TimeField")
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe("import conflict handling", () => {
|
|
64
|
+
it("should use TdsTimeField alias when TimeField is already imported elsewhere", () => {
|
|
65
|
+
const input = `
|
|
66
|
+
import { TimeField } from "some-other-library"
|
|
67
|
+
import { TimeField as LegacyTimeField } from "@planningcenter/tapestry-react"
|
|
68
|
+
|
|
69
|
+
function Test() {
|
|
70
|
+
return (
|
|
71
|
+
<div>
|
|
72
|
+
<TimeField />
|
|
73
|
+
<LegacyTimeField value={time} onChange={setTime} />
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
`.trim()
|
|
78
|
+
|
|
79
|
+
const result = applyTransform(input)
|
|
80
|
+
expect(result).toContain('import { TimeField } from "some-other-library"')
|
|
81
|
+
expect(result).toContain(
|
|
82
|
+
'import { TimeField as TdsTimeField } from "@planningcenter/tapestry"'
|
|
83
|
+
)
|
|
84
|
+
expect(result).toContain("<TdsTimeField")
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe("edge cases", () => {
|
|
89
|
+
it("should not affect other components", () => {
|
|
90
|
+
const input = `
|
|
91
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
92
|
+
|
|
93
|
+
function Test() {
|
|
94
|
+
return <Button>Click me</Button>
|
|
95
|
+
}
|
|
96
|
+
`.trim()
|
|
97
|
+
|
|
98
|
+
const result = applyTransform(input)
|
|
99
|
+
expect(result).toContain(
|
|
100
|
+
'import { Button } from "@planningcenter/tapestry-react"'
|
|
101
|
+
)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it("should handle no imports", () => {
|
|
105
|
+
const input = `
|
|
106
|
+
function Test() {
|
|
107
|
+
return <div>No imports</div>
|
|
108
|
+
}
|
|
109
|
+
`.trim()
|
|
110
|
+
|
|
111
|
+
const result = applyTransform(input)
|
|
112
|
+
expect(result).toBe(input)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it("should preserve all attributes", () => {
|
|
116
|
+
const input = `
|
|
117
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
118
|
+
|
|
119
|
+
function Test() {
|
|
120
|
+
return (
|
|
121
|
+
<TimeField
|
|
122
|
+
value={time}
|
|
123
|
+
onChange={setTime}
|
|
124
|
+
disabled
|
|
125
|
+
/>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
`.trim()
|
|
129
|
+
|
|
130
|
+
const result = applyTransform(input)
|
|
131
|
+
expect(result).toContain(
|
|
132
|
+
'import { TimeField } from "@planningcenter/tapestry"'
|
|
133
|
+
)
|
|
134
|
+
expect(result).toContain("value={time}")
|
|
135
|
+
expect(result).toContain("onChange={setTime}")
|
|
136
|
+
expect(result).toContain("disabled")
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it("should handle alias import", () => {
|
|
140
|
+
const input = `
|
|
141
|
+
import { TimeField as TapestryTimeField } from "@planningcenter/tapestry-react"
|
|
142
|
+
|
|
143
|
+
function Test() {
|
|
144
|
+
return <TapestryTimeField value={time} onChange={setTime} />
|
|
145
|
+
}
|
|
146
|
+
`.trim()
|
|
147
|
+
|
|
148
|
+
const result = applyTransform(input)
|
|
149
|
+
expect(result).toContain("@planningcenter/tapestry")
|
|
150
|
+
expect(result).not.toContain("@planningcenter/tapestry-react")
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { componentTransformFactory } from "../../shared/transformFactories/componentTransformFactory"
|
|
4
|
+
|
|
5
|
+
const transform: Transform = componentTransformFactory({
|
|
6
|
+
condition: () => true,
|
|
7
|
+
conflictAlias: "TdsTimeField",
|
|
8
|
+
fromComponent: "TimeField",
|
|
9
|
+
fromPackage: "@planningcenter/tapestry-react",
|
|
10
|
+
toComponent: "TimeField",
|
|
11
|
+
toPackage: "@planningcenter/tapestry",
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default transform
|
|
@@ -0,0 +1,173 @@
|
|
|
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 { TimeField } from "@planningcenter/tapestry-react"
|
|
23
|
+
|
|
24
|
+
function Test() {
|
|
25
|
+
return <TimeField size="xs" value={time} onChange={setTime} />
|
|
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 { TimeField } from "@planningcenter/tapestry-react"
|
|
38
|
+
|
|
39
|
+
function Test() {
|
|
40
|
+
return <TimeField size="sm" value={time} onChange={setTime} />
|
|
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 { TimeField } from "@planningcenter/tapestry-react"
|
|
53
|
+
|
|
54
|
+
function Test() {
|
|
55
|
+
return <TimeField size="xl" value={time} onChange={setTime} />
|
|
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={"sm"} expression container', () => {
|
|
66
|
+
const input = `
|
|
67
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
68
|
+
|
|
69
|
+
function Test() {
|
|
70
|
+
return <TimeField size={"sm"} value={time} onChange={setTime} />
|
|
71
|
+
}
|
|
72
|
+
`.trim()
|
|
73
|
+
|
|
74
|
+
const result = applyTransform(input)
|
|
75
|
+
expect(result).toContain('size="md"')
|
|
76
|
+
expect(result).toContain('Size "sm" was mapped to "md"')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("should not transform already valid sizes", () => {
|
|
80
|
+
const input = `
|
|
81
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
82
|
+
|
|
83
|
+
function Test() {
|
|
84
|
+
return (
|
|
85
|
+
<div>
|
|
86
|
+
<TimeField size="md" value={time1} onChange={setTime1} />
|
|
87
|
+
<TimeField size="lg" value={time2} onChange={setTime2} />
|
|
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 TimeField without size prop", () => {
|
|
102
|
+
const input = `
|
|
103
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
104
|
+
|
|
105
|
+
function Test() {
|
|
106
|
+
return <TimeField value={time} onChange={setTime} />
|
|
107
|
+
}
|
|
108
|
+
`.trim()
|
|
109
|
+
|
|
110
|
+
const result = applyTransform(input)
|
|
111
|
+
expect(result).toBe(input)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("should not transform expression values (variables)", () => {
|
|
115
|
+
const input = `
|
|
116
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
117
|
+
|
|
118
|
+
function Test() {
|
|
119
|
+
const size = "sm"
|
|
120
|
+
return <TimeField size={size} value={time} onChange={setTime} />
|
|
121
|
+
}
|
|
122
|
+
`.trim()
|
|
123
|
+
|
|
124
|
+
const result = applyTransform(input)
|
|
125
|
+
expect(result).toContain("size={size}")
|
|
126
|
+
expect(result).not.toContain("Size")
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("should not affect other components", () => {
|
|
130
|
+
const input = `
|
|
131
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
132
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
133
|
+
|
|
134
|
+
function Test() {
|
|
135
|
+
return (
|
|
136
|
+
<div>
|
|
137
|
+
<Input size="sm" label="Name" />
|
|
138
|
+
<TimeField size="md" value={time} onChange={setTime} />
|
|
139
|
+
</div>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
`.trim()
|
|
143
|
+
|
|
144
|
+
const result = applyTransform(input)
|
|
145
|
+
expect(result).toContain('<Input size="sm"')
|
|
146
|
+
expect(result).toContain('size="md"')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it("should handle multiple TimeFields with different sizes", () => {
|
|
150
|
+
const input = `
|
|
151
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
152
|
+
|
|
153
|
+
function Test() {
|
|
154
|
+
return (
|
|
155
|
+
<div>
|
|
156
|
+
<TimeField size="xs" value={time1} onChange={setTime1} />
|
|
157
|
+
<TimeField size="sm" value={time2} onChange={setTime2} />
|
|
158
|
+
<TimeField size="md" value={time3} onChange={setTime3} />
|
|
159
|
+
<TimeField size="xl" value={time4} onChange={setTime4} />
|
|
160
|
+
</div>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
`.trim()
|
|
164
|
+
|
|
165
|
+
const result = applyTransform(input)
|
|
166
|
+
expect(result).not.toContain('size="xs"')
|
|
167
|
+
expect(result).not.toContain('size="sm"')
|
|
168
|
+
expect(result).not.toContain('size="xl"')
|
|
169
|
+
const sizeMappingMatches = result.match(/Size ".*" was mapped to/g)
|
|
170
|
+
expect(sizeMappingMatches).toHaveLength(3)
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { sizeMappingFactory } from "../../shared/transformFactories/sizeMappingFactory"
|
|
4
|
+
|
|
5
|
+
const transform: Transform = sizeMappingFactory({
|
|
6
|
+
sizeMapping: {
|
|
7
|
+
sm: "md",
|
|
8
|
+
xl: "lg",
|
|
9
|
+
xs: "md",
|
|
10
|
+
},
|
|
11
|
+
targetComponent: "TimeField",
|
|
12
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export default transform
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./stateToInvalid"
|
|
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("stateToInvalid transform", () => {
|
|
19
|
+
it("converts state='error' to invalid", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return <TimeField state="error" value={time} onChange={setTime} />
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(input)
|
|
29
|
+
expect(result).toContain("invalid")
|
|
30
|
+
expect(result).not.toContain('state="error"')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("removes state='success' and adds TODO", () => {
|
|
34
|
+
const input = `
|
|
35
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
36
|
+
|
|
37
|
+
function Test() {
|
|
38
|
+
return <TimeField state="success" value={time} onChange={setTime} />
|
|
39
|
+
}
|
|
40
|
+
`.trim()
|
|
41
|
+
|
|
42
|
+
const result = applyTransform(input)
|
|
43
|
+
expect(result).not.toContain('<TimeField state="success"')
|
|
44
|
+
expect(result).toContain("TODO: tapestry-migration")
|
|
45
|
+
expect(result).toContain("no equivalent")
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it("adds TODO comment for dynamic state value", () => {
|
|
49
|
+
const input = `
|
|
50
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
51
|
+
|
|
52
|
+
function Test() {
|
|
53
|
+
return <TimeField state={fieldState} value={time} onChange={setTime} />
|
|
54
|
+
}
|
|
55
|
+
`.trim()
|
|
56
|
+
|
|
57
|
+
const result = applyTransform(input)
|
|
58
|
+
expect(result).toContain("TODO: tapestry-migration")
|
|
59
|
+
expect(result).toContain("Migrate this prop manually")
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it("returns null when state prop is absent", () => {
|
|
63
|
+
const input = `
|
|
64
|
+
import { TimeField } from "@planningcenter/tapestry-react"
|
|
65
|
+
|
|
66
|
+
function Test() {
|
|
67
|
+
return <TimeField value={time} onChange={setTime} />
|
|
68
|
+
}
|
|
69
|
+
`.trim()
|
|
70
|
+
|
|
71
|
+
const result = applyTransform(input)
|
|
72
|
+
expect(result).toBe(input)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("does not affect other components", () => {
|
|
76
|
+
const input = `
|
|
77
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
78
|
+
|
|
79
|
+
function Test() {
|
|
80
|
+
return <Button state="error">Click</Button>
|
|
81
|
+
}
|
|
82
|
+
`.trim()
|
|
83
|
+
|
|
84
|
+
const result = applyTransform(input)
|
|
85
|
+
expect(result).toBe(input)
|
|
86
|
+
})
|
|
87
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addAttribute } from "../../shared/actions/addAttribute"
|
|
4
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
5
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
6
|
+
import { getAttributeValue } from "../../shared/actions/getAttributeValue"
|
|
7
|
+
import { removeAttribute } from "../../shared/actions/removeAttribute"
|
|
8
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
9
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
10
|
+
|
|
11
|
+
const transform: Transform = attributeTransformFactory({
|
|
12
|
+
condition: hasAttribute("state"),
|
|
13
|
+
targetComponent: "TimeField",
|
|
14
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
15
|
+
transform: (element, { j, source }) => {
|
|
16
|
+
const attr = getAttribute({ element, name: "state" })
|
|
17
|
+
if (!attr) return false
|
|
18
|
+
|
|
19
|
+
const value = getAttributeValue({ attribute: attr, j })
|
|
20
|
+
|
|
21
|
+
if (value === "error") {
|
|
22
|
+
removeAttribute("state", { element, j, source })
|
|
23
|
+
addAttribute({
|
|
24
|
+
booleanAsShorthand: true,
|
|
25
|
+
element,
|
|
26
|
+
j,
|
|
27
|
+
name: "invalid",
|
|
28
|
+
value: true,
|
|
29
|
+
})
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (value === "success") {
|
|
34
|
+
removeAttribute("state", { element, j, source })
|
|
35
|
+
addComment({
|
|
36
|
+
element,
|
|
37
|
+
j,
|
|
38
|
+
scope: "state",
|
|
39
|
+
source,
|
|
40
|
+
text: "'state=\"success\"' has no equivalent in the new TimeField API. Remove this prop and implement success feedback separately.",
|
|
41
|
+
})
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
addComment({
|
|
46
|
+
element,
|
|
47
|
+
j,
|
|
48
|
+
scope: "state",
|
|
49
|
+
source,
|
|
50
|
+
text: "'state' has been replaced by 'invalid' (boolean). For error state, use invalid={true}. 'success' has no equivalent. Migrate this prop manually.",
|
|
51
|
+
})
|
|
52
|
+
return true
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
export default transform
|