@planningcenter/tapestry-migration-cli 3.4.1-rc.8 → 3.4.1
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 +34 -64
- package/package.json +3 -3
- package/src/components/dropdown/index.test.ts +177 -4
- package/src/components/dropdown/index.ts +24 -2
- package/src/components/dropdown/transforms/auditSpreadProps.test.ts +110 -0
- package/src/components/dropdown/transforms/auditSpreadProps.ts +10 -0
- package/src/components/dropdown/transforms/dividerToSeparator.test.ts +134 -0
- package/src/components/dropdown/transforms/dividerToSeparator.ts +67 -0
- package/src/components/dropdown/transforms/itemToAction.test.ts +19 -1
- package/src/components/dropdown/transforms/itemToAction.ts +14 -2
- package/src/components/dropdown/transforms/linkToLink.test.ts +19 -1
- package/src/components/dropdown/transforms/linkToLink.ts +13 -2
- package/src/components/dropdown/transforms/moveDropdownImport.test.ts +93 -0
- package/src/components/dropdown/transforms/moveDropdownImport.ts +13 -0
- package/src/components/dropdown/transforms/placementIdToMenu.test.ts +135 -0
- package/src/components/dropdown/transforms/placementIdToMenu.ts +96 -0
- package/src/components/dropdown/transforms/unsupportedProps.test.ts +145 -0
- package/src/components/dropdown/transforms/unsupportedProps.ts +12 -0
- package/src/components/dropdown/transforms/unsupportedPropsDivider.test.ts +143 -0
- package/src/components/dropdown/transforms/unsupportedPropsDivider.ts +26 -0
- package/src/components/dropdown/transforms/unsupportedPropsItem.test.ts +123 -0
- package/src/components/dropdown/transforms/unsupportedPropsItem.ts +12 -0
- package/src/components/dropdown/transforms/unsupportedPropsLink.test.ts +107 -0
- package/src/components/dropdown/transforms/unsupportedPropsLink.ts +12 -0
- package/src/components/dropdown/transforms/wrapMenu.test.ts +153 -0
- package/src/components/dropdown/transforms/wrapMenu.ts +54 -0
- package/src/components/dropdown/transforms/wrapTrigger.test.ts +283 -0
- package/src/components/dropdown/transforms/wrapTrigger.ts +98 -0
- package/src/components/input/transforms/unsupportedProps.test.ts +14 -13
- package/src/components/shared/conditions/isChildOf.test.ts +89 -0
- package/src/components/shared/conditions/isChildOf.ts +43 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +36 -0
|
@@ -2512,6 +2512,33 @@ const React = /* @__PURE__ */ getDefaultExportFromCjs(reactExports);
|
|
|
2512
2512
|
function LoadingSpinner() {
|
|
2513
2513
|
return React.createElement("div", { className: "tds-loading-spinner" });
|
|
2514
2514
|
}
|
|
2515
|
+
const COMPONENT_KIND_CLASS_MAP = {
|
|
2516
|
+
delete: "tds-btn--delete",
|
|
2517
|
+
ghost: "tds-btn--ghost-neutral",
|
|
2518
|
+
"ghost-delete": "tds-btn--ghost-delete",
|
|
2519
|
+
"ghost-interaction": "tds-btn--ghost-interaction",
|
|
2520
|
+
"inline-text": "tds-btn--inline-text",
|
|
2521
|
+
neutral: "tds-btn--neutral",
|
|
2522
|
+
"neutral-inline": "tds-btn--neutral-inline",
|
|
2523
|
+
pill: "tds-btn--pill",
|
|
2524
|
+
primary: "tds-btn--interaction",
|
|
2525
|
+
"primary-page-header": "tds-btn--primary-page-header",
|
|
2526
|
+
secondary: "tds-btn--outline-neutral",
|
|
2527
|
+
"secondary-delete": "tds-btn--outline-delete",
|
|
2528
|
+
"secondary-interaction": "tds-btn--outline-interaction",
|
|
2529
|
+
"secondary-page-header": "tds-btn--secondary-page-header",
|
|
2530
|
+
"staff-only": "tds-btn--staff-only"
|
|
2531
|
+
};
|
|
2532
|
+
const COMPONENT_SIZE_CLASS_MAP = {
|
|
2533
|
+
lg: "tds-btn--lg",
|
|
2534
|
+
md: "",
|
|
2535
|
+
sm: "tds-btn--sm",
|
|
2536
|
+
xl: "tds-btn--xl",
|
|
2537
|
+
xs: "tds-btn--xs"
|
|
2538
|
+
};
|
|
2539
|
+
function wrapStringWithSpan(content) {
|
|
2540
|
+
return typeof content === "string" ? React.createElement("span", null, content) : content;
|
|
2541
|
+
}
|
|
2515
2542
|
var classnames = { exports: {} };
|
|
2516
2543
|
/*!
|
|
2517
2544
|
Copyright (c) 2018 Jed Watson.
|
|
@@ -2577,68 +2604,16 @@ function requireClassnames() {
|
|
|
2577
2604
|
}
|
|
2578
2605
|
var classnamesExports = requireClassnames();
|
|
2579
2606
|
const classNames = /* @__PURE__ */ getDefaultExportFromCjs(classnamesExports);
|
|
2580
|
-
const COMPONENT_KIND_CLASS_MAP = {
|
|
2581
|
-
delete: "tds-btn--delete",
|
|
2582
|
-
ghost: "tds-btn--ghost-neutral",
|
|
2583
|
-
"ghost-delete": "tds-btn--ghost-delete",
|
|
2584
|
-
"ghost-interaction": "tds-btn--ghost-interaction",
|
|
2585
|
-
"inline-text": "tds-btn--inline-text",
|
|
2586
|
-
neutral: "tds-btn--neutral",
|
|
2587
|
-
"neutral-inline": "tds-btn--neutral-inline",
|
|
2588
|
-
pill: "tds-btn--pill",
|
|
2589
|
-
primary: "tds-btn--interaction",
|
|
2590
|
-
"primary-page-header": "tds-btn--primary-page-header",
|
|
2591
|
-
secondary: "tds-btn--outline-neutral",
|
|
2592
|
-
"secondary-delete": "tds-btn--outline-delete",
|
|
2593
|
-
"secondary-interaction": "tds-btn--outline-interaction",
|
|
2594
|
-
"secondary-page-header": "tds-btn--secondary-page-header",
|
|
2595
|
-
"staff-only": "tds-btn--staff-only"
|
|
2596
|
-
};
|
|
2597
|
-
const COMPONENT_SIZE_CLASS_MAP = {
|
|
2598
|
-
lg: "tds-btn--lg",
|
|
2599
|
-
md: "",
|
|
2600
|
-
sm: "tds-btn--sm",
|
|
2601
|
-
xl: "tds-btn--xl",
|
|
2602
|
-
xs: "tds-btn--xs"
|
|
2603
|
-
};
|
|
2604
|
-
const enhanceElementWithClassName = (element, className) => {
|
|
2605
|
-
if (!element)
|
|
2606
|
-
return null;
|
|
2607
|
-
if (React.isValidElement(element)) {
|
|
2608
|
-
return React.cloneElement(element, {
|
|
2609
|
-
className: classNames(
|
|
2610
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2611
|
-
element.props.className,
|
|
2612
|
-
className
|
|
2613
|
-
)
|
|
2614
|
-
});
|
|
2615
|
-
}
|
|
2616
|
-
if (typeof element === "string" && element.trim()) {
|
|
2617
|
-
return React.createElement("span", { className }, element);
|
|
2618
|
-
}
|
|
2619
|
-
return null;
|
|
2620
|
-
};
|
|
2621
|
-
const buildComponentClassName$1 = ({ className, fullWidth, kind, loading, size }) => {
|
|
2622
|
-
return classNames(kind && "tds-btn", size && size !== "md" && COMPONENT_SIZE_CLASS_MAP[size], kind && COMPONENT_KIND_CLASS_MAP[kind], fullWidth && "tds-btn--full-width", loading && "tds-btn--loading", className);
|
|
2623
|
-
};
|
|
2624
2607
|
const BaseButton = reactExports.forwardRef(({ "aria-label": ariaLabel, className, disabled = false, fullWidth, kind = "secondary", label, loading = false, loadingAriaLabel = "Loading...", prefix, size, suffix, ...restProps }, ref) => {
|
|
2625
|
-
const combinedClassName =
|
|
2626
|
-
className,
|
|
2627
|
-
fullWidth,
|
|
2628
|
-
kind,
|
|
2629
|
-
loading,
|
|
2630
|
-
size
|
|
2631
|
-
});
|
|
2632
|
-
const prefixElement = enhanceElementWithClassName(prefix, "prefix");
|
|
2633
|
-
const suffixElement = enhanceElementWithClassName(suffix, "suffix");
|
|
2608
|
+
const combinedClassName = classNames(kind && "tds-btn", size && size !== "md" && COMPONENT_SIZE_CLASS_MAP[size], kind && COMPONENT_KIND_CLASS_MAP[kind], fullWidth && "tds-btn--full-width", loading && "tds-btn--loading", className, { "tds-btn--prefix": prefix, "tds-btn--suffix": suffix });
|
|
2634
2609
|
const isDisabled = disabled || loading;
|
|
2635
2610
|
return React.createElement(
|
|
2636
2611
|
"button",
|
|
2637
2612
|
{ type: "button", className: combinedClassName, ref, ...restProps, "aria-busy": loading || void 0, "aria-disabled": isDisabled || void 0, disabled: isDisabled, "aria-label": loading ? loadingAriaLabel : ariaLabel },
|
|
2638
2613
|
loading && React.createElement(LoadingSpinner, null),
|
|
2639
|
-
|
|
2614
|
+
wrapStringWithSpan(prefix),
|
|
2640
2615
|
loading ? React.createElement("span", null, label) : label,
|
|
2641
|
-
|
|
2616
|
+
wrapStringWithSpan(suffix)
|
|
2642
2617
|
);
|
|
2643
2618
|
});
|
|
2644
2619
|
BaseButton.displayName = "BaseButton";
|
|
@@ -2754,7 +2729,7 @@ function Icon({ symbol: s2, className, ...props }) {
|
|
|
2754
2729
|
function dropdownProps({ className }) {
|
|
2755
2730
|
return {
|
|
2756
2731
|
className: classNames(className, "tds-btn--dropdown"),
|
|
2757
|
-
suffix: React.createElement(Icon, { symbol: "general#down-caret", "aria-hidden": true })
|
|
2732
|
+
suffix: React.createElement(Icon, { className: "suffix", symbol: "general#down-caret", "aria-hidden": true })
|
|
2758
2733
|
};
|
|
2759
2734
|
}
|
|
2760
2735
|
const DropdownButton = reactExports.forwardRef((props, ref) => {
|
|
@@ -2788,20 +2763,15 @@ const PageHeaderActionsDropdownButton = reactExports.forwardRef(({ className, ne
|
|
|
2788
2763
|
return React.createElement(DropdownButton, { ref, ...props, kind: "secondary-page-header", ...needsAttentionProps({ className, needsAttention }) });
|
|
2789
2764
|
});
|
|
2790
2765
|
PageHeaderActionsDropdownButton.displayName = "PageHeaderActionsDropdownButton";
|
|
2791
|
-
const buildComponentClassName = (size, kind, fullWidth, className) => {
|
|
2792
|
-
return classNames(kind && "tds-btn", size && size !== "md" && kind && COMPONENT_SIZE_CLASS_MAP[size], kind && COMPONENT_KIND_CLASS_MAP[kind], fullWidth && "tds-btn--full-width", className);
|
|
2793
|
-
};
|
|
2794
2766
|
const BaseLink = reactExports.forwardRef(({ children, className, external = false, fullWidth, href, kind, label, prefix, size, suffix, ...restProps }, ref) => {
|
|
2795
|
-
const combinedClassName =
|
|
2796
|
-
const prefixElement = enhanceElementWithClassName(prefix, "prefix");
|
|
2797
|
-
const suffixElement = enhanceElementWithClassName(suffix, "suffix");
|
|
2767
|
+
const combinedClassName = classNames(kind && "tds-btn", size && size !== "md" && kind && COMPONENT_SIZE_CLASS_MAP[size], kind && COMPONENT_KIND_CLASS_MAP[kind], fullWidth && "tds-btn--full-width", className, { "tds-btn--prefix": prefix, "tds-btn--suffix": suffix });
|
|
2798
2768
|
const externalProps = external ? { rel: "noopener noreferrer", target: "_blank" } : {};
|
|
2799
2769
|
return React.createElement(
|
|
2800
2770
|
"a",
|
|
2801
2771
|
{ href, className: combinedClassName, ref, ...restProps, ...externalProps },
|
|
2802
|
-
|
|
2772
|
+
wrapStringWithSpan(prefix),
|
|
2803
2773
|
label || children,
|
|
2804
|
-
|
|
2774
|
+
wrapStringWithSpan(suffix)
|
|
2805
2775
|
);
|
|
2806
2776
|
});
|
|
2807
2777
|
BaseLink.displayName = "BaseLink";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/tapestry-migration-cli",
|
|
3
|
-
"version": "3.4.1
|
|
3
|
+
"version": "3.4.1",
|
|
4
4
|
"description": "CLI tool for Tapestry migrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@emotion/react": "^11.14.0",
|
|
33
|
-
"@planningcenter/tapestry": "^3.4.1
|
|
33
|
+
"@planningcenter/tapestry": "^3.4.1",
|
|
34
34
|
"@planningcenter/tapestry-react": "^4.11.5",
|
|
35
35
|
"@types/jscodeshift": "^17.3.0",
|
|
36
36
|
"@types/node": "^20.0.0",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "f005d23b54d1a95f03e0823e6ab0d139af17cead"
|
|
54
54
|
}
|
|
@@ -40,7 +40,7 @@ import { Dropdown } from "@planningcenter/tapestry-react"
|
|
|
40
40
|
|
|
41
41
|
export default function Test() {
|
|
42
42
|
return (
|
|
43
|
-
<Dropdown
|
|
43
|
+
<Dropdown onClose={handleClose}>
|
|
44
44
|
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
45
45
|
</Dropdown>
|
|
46
46
|
)
|
|
@@ -53,12 +53,185 @@ export default function Test() {
|
|
|
53
53
|
expect(result).not.toContain("Dropdown.Item")
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
-
it("
|
|
56
|
+
it("applies wrapMenu through the chain", () => {
|
|
57
|
+
const input = `
|
|
58
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
59
|
+
|
|
60
|
+
export default function Test() {
|
|
61
|
+
return (
|
|
62
|
+
<Dropdown onClose={handleClose}>
|
|
63
|
+
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
64
|
+
</Dropdown>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
`.trim()
|
|
68
|
+
|
|
69
|
+
const result = applyTransform(input)
|
|
70
|
+
expect(result).toContain("<DropdownMenu>")
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it("applies placementIdToMenu through the chain", () => {
|
|
74
|
+
const input = `
|
|
75
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
76
|
+
|
|
77
|
+
export default function Test() {
|
|
78
|
+
return (
|
|
79
|
+
<Dropdown placement="bottom-end">
|
|
80
|
+
<DropdownMenu>
|
|
81
|
+
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
82
|
+
</DropdownMenu>
|
|
83
|
+
</Dropdown>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
`.trim()
|
|
87
|
+
|
|
88
|
+
const result = applyTransform(input)
|
|
89
|
+
expect(result).toContain('<DropdownMenu placement="bottom end"')
|
|
90
|
+
expect(result).not.toMatch(/<Dropdown\s[^>]*placement/)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it("applies wrapTrigger through the chain", () => {
|
|
94
|
+
const input = `
|
|
95
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
96
|
+
|
|
97
|
+
export default function Test() {
|
|
98
|
+
return (
|
|
99
|
+
<Dropdown title="Actions">
|
|
100
|
+
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
101
|
+
</Dropdown>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
`.trim()
|
|
105
|
+
|
|
106
|
+
const result = applyTransform(input)
|
|
107
|
+
expect(result).toContain("<DropdownTrigger>")
|
|
108
|
+
expect(result).toContain('<DropdownButton label="Actions"')
|
|
109
|
+
expect(result).not.toContain('title="Actions"')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it("applies unsupportedPropsDivider through the chain", () => {
|
|
113
|
+
const input = `
|
|
114
|
+
import { Dropdown, Divider } from "@planningcenter/tapestry-react"
|
|
115
|
+
|
|
116
|
+
export default function Test() {
|
|
117
|
+
return <Dropdown><Divider axis="vertical" /></Dropdown>
|
|
118
|
+
}
|
|
119
|
+
`.trim()
|
|
120
|
+
|
|
121
|
+
const result = applyTransform(input)
|
|
122
|
+
expect(result).toContain("TODO: tapestry-migration (axis)")
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it("applies dividerToSeparator through the chain", () => {
|
|
126
|
+
const input = `
|
|
127
|
+
import { Dropdown, Divider } from "@planningcenter/tapestry-react"
|
|
128
|
+
|
|
129
|
+
export default function Test() {
|
|
130
|
+
return <Dropdown><Divider /></Dropdown>
|
|
131
|
+
}
|
|
132
|
+
`.trim()
|
|
133
|
+
|
|
134
|
+
const result = applyTransform(input)
|
|
135
|
+
expect(result).toContain("<DropdownSeparator")
|
|
136
|
+
expect(result).not.toContain("<Divider")
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it("applies unsupportedPropsItem through the chain", () => {
|
|
57
140
|
const input = `
|
|
58
141
|
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
59
142
|
|
|
60
|
-
export default function Test(
|
|
61
|
-
return
|
|
143
|
+
export default function Test() {
|
|
144
|
+
return (
|
|
145
|
+
<Dropdown title="Actions">
|
|
146
|
+
<Dropdown.Item as={Link} onSelect={fn}>Edit</Dropdown.Item>
|
|
147
|
+
</Dropdown>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
`.trim()
|
|
151
|
+
|
|
152
|
+
const result = applyTransform(input)
|
|
153
|
+
expect(result).toContain("TODO: tapestry-migration (as)")
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it("applies unsupportedPropsLink through the chain", () => {
|
|
157
|
+
const input = `
|
|
158
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
159
|
+
|
|
160
|
+
export default function Test() {
|
|
161
|
+
return (
|
|
162
|
+
<Dropdown title="Actions">
|
|
163
|
+
<Dropdown.Link to="/edit" disabled>Edit</Dropdown.Link>
|
|
164
|
+
</Dropdown>
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
`.trim()
|
|
168
|
+
|
|
169
|
+
const result = applyTransform(input)
|
|
170
|
+
expect(result).toContain("TODO: tapestry-migration (disabled)")
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it("applies auditSpreadProps through the chain", () => {
|
|
174
|
+
const input = `
|
|
175
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
176
|
+
|
|
177
|
+
export default function Test() {
|
|
178
|
+
return (
|
|
179
|
+
<Dropdown {...props} title="Actions">
|
|
180
|
+
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
181
|
+
</Dropdown>
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
`.trim()
|
|
185
|
+
|
|
186
|
+
const result = applyTransform(input)
|
|
187
|
+
expect(result).toContain("TODO: tapestry-migration (spreadAttribute)")
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it("applies unsupportedProps through the chain", () => {
|
|
191
|
+
const input = `
|
|
192
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
193
|
+
|
|
194
|
+
export default function Test() {
|
|
195
|
+
return <Dropdown variant="outline" onClose={fn}><Dropdown.Item onSelect={fn}>Edit</Dropdown.Item></Dropdown>
|
|
196
|
+
}
|
|
197
|
+
`.trim()
|
|
198
|
+
|
|
199
|
+
const result = applyTransform(input)
|
|
200
|
+
expect(result).toContain("TODO: tapestry-migration (variant)")
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it("applies moveDropdownImport through the chain", () => {
|
|
204
|
+
const input = `
|
|
205
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
206
|
+
|
|
207
|
+
export default function Test() {
|
|
208
|
+
return (
|
|
209
|
+
<Dropdown onClose={fn}>
|
|
210
|
+
<DropdownTrigger><DropdownButton label="Actions" /></DropdownTrigger>
|
|
211
|
+
<DropdownMenu><DropdownAction onAction={fn}>Edit</DropdownAction></DropdownMenu>
|
|
212
|
+
</Dropdown>
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
`.trim()
|
|
216
|
+
|
|
217
|
+
const result = applyTransform(input)
|
|
218
|
+
expect(result).toContain('from "@planningcenter/tapestry"')
|
|
219
|
+
expect(result).not.toContain('from "@planningcenter/tapestry-react"')
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it("returns null when there is nothing to transform", () => {
|
|
223
|
+
const input = `
|
|
224
|
+
import { Dropdown } from "@planningcenter/tapestry"
|
|
225
|
+
|
|
226
|
+
export default function Test() {
|
|
227
|
+
return (
|
|
228
|
+
<Dropdown onClose={handleClose}>
|
|
229
|
+
<DropdownTrigger><DropdownButton label="Actions" /></DropdownTrigger>
|
|
230
|
+
<DropdownMenu>
|
|
231
|
+
<DropdownAction onAction={fn}>Edit</DropdownAction>
|
|
232
|
+
</DropdownMenu>
|
|
233
|
+
</Dropdown>
|
|
234
|
+
)
|
|
62
235
|
}
|
|
63
236
|
`.trim()
|
|
64
237
|
|
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
import { Transform } from "jscodeshift"
|
|
2
2
|
|
|
3
|
+
import auditSpreadProps from "./transforms/auditSpreadProps"
|
|
4
|
+
import dividerToSeparator from "./transforms/dividerToSeparator"
|
|
3
5
|
import itemToAction from "./transforms/itemToAction"
|
|
4
6
|
import linkToLink from "./transforms/linkToLink"
|
|
7
|
+
import moveDropdownImport from "./transforms/moveDropdownImport"
|
|
8
|
+
import placementIdToMenu from "./transforms/placementIdToMenu"
|
|
9
|
+
import unsupportedProps from "./transforms/unsupportedProps"
|
|
10
|
+
import unsupportedPropsDivider from "./transforms/unsupportedPropsDivider"
|
|
11
|
+
import unsupportedPropsItem from "./transforms/unsupportedPropsItem"
|
|
12
|
+
import unsupportedPropsLink from "./transforms/unsupportedPropsLink"
|
|
13
|
+
import wrapMenu from "./transforms/wrapMenu"
|
|
14
|
+
import wrapTrigger from "./transforms/wrapTrigger"
|
|
5
15
|
|
|
6
|
-
// When moveDropdownImport is added, it must remain last in the chain.
|
|
7
16
|
const transform: Transform = (fileInfo, api, options) => {
|
|
8
17
|
let currentSource = fileInfo.source
|
|
9
18
|
let hasAnyChanges = false
|
|
10
19
|
|
|
11
|
-
const transforms: Transform[] = [
|
|
20
|
+
const transforms: Transform[] = [
|
|
21
|
+
unsupportedPropsItem,
|
|
22
|
+
unsupportedPropsLink,
|
|
23
|
+
unsupportedPropsDivider,
|
|
24
|
+
itemToAction,
|
|
25
|
+
linkToLink,
|
|
26
|
+
wrapMenu,
|
|
27
|
+
placementIdToMenu,
|
|
28
|
+
wrapTrigger,
|
|
29
|
+
dividerToSeparator,
|
|
30
|
+
auditSpreadProps,
|
|
31
|
+
unsupportedProps,
|
|
32
|
+
moveDropdownImport,
|
|
33
|
+
]
|
|
12
34
|
|
|
13
35
|
for (const individualTransform of transforms) {
|
|
14
36
|
const result = individualTransform(
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./auditSpreadProps"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
const AUDIT_COMMENT =
|
|
9
|
+
"TODO: tapestry-migration (spreadAttribute): Spread props can contain unsupported props, please explore usages and migrate as needed."
|
|
10
|
+
|
|
11
|
+
function applyTransform(source: string): string | null {
|
|
12
|
+
const fileInfo = { path: "test.tsx", source }
|
|
13
|
+
return transform(
|
|
14
|
+
fileInfo,
|
|
15
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
16
|
+
{}
|
|
17
|
+
) as string | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("auditSpreadProps transform", () => {
|
|
21
|
+
it("adds a comment to Dropdown with spread props", () => {
|
|
22
|
+
const input = `
|
|
23
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
24
|
+
|
|
25
|
+
export default function Test() {
|
|
26
|
+
const props = { onClose: handleClose }
|
|
27
|
+
return (
|
|
28
|
+
<Dropdown {...props} title="Actions">
|
|
29
|
+
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
30
|
+
</Dropdown>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
`.trim()
|
|
34
|
+
|
|
35
|
+
const result = applyTransform(input)
|
|
36
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
37
|
+
expect(result).toContain("{...props}")
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("adds a comment for each spread", () => {
|
|
41
|
+
const input = `
|
|
42
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
43
|
+
|
|
44
|
+
export default function Test() {
|
|
45
|
+
return (
|
|
46
|
+
<Dropdown {...baseProps} {...extraProps} title="Actions">
|
|
47
|
+
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
48
|
+
</Dropdown>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
`.trim()
|
|
52
|
+
|
|
53
|
+
const result = applyTransform(input)
|
|
54
|
+
const commentCount = (result?.match(/spreadAttribute/g) || []).length
|
|
55
|
+
expect(commentCount).toBe(2)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it("preserves all other attributes", () => {
|
|
59
|
+
const input = `
|
|
60
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
61
|
+
|
|
62
|
+
export default function Test() {
|
|
63
|
+
return (
|
|
64
|
+
<Dropdown title="Actions" placement="bottom-start" {...props}>
|
|
65
|
+
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
66
|
+
</Dropdown>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
`.trim()
|
|
70
|
+
|
|
71
|
+
const result = applyTransform(input)
|
|
72
|
+
expect(result).toContain('title="Actions"')
|
|
73
|
+
expect(result).toContain('placement="bottom-start"')
|
|
74
|
+
expect(result).toContain("{...props}")
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe("no changes scenarios", () => {
|
|
78
|
+
it("returns null when no spread props", () => {
|
|
79
|
+
const input = `
|
|
80
|
+
import { Dropdown } from "@planningcenter/tapestry-react"
|
|
81
|
+
|
|
82
|
+
export default function Test() {
|
|
83
|
+
return (
|
|
84
|
+
<Dropdown title="Actions">
|
|
85
|
+
<Dropdown.Item onSelect={fn}>Edit</Dropdown.Item>
|
|
86
|
+
</Dropdown>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
`.trim()
|
|
90
|
+
|
|
91
|
+
expect(applyTransform(input)).toBe(null)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it("returns null when not imported from tapestry-react", () => {
|
|
95
|
+
const input = `
|
|
96
|
+
import { Dropdown } from "some-other-library"
|
|
97
|
+
|
|
98
|
+
export default function Test() {
|
|
99
|
+
return <Dropdown {...props} title="Actions" />
|
|
100
|
+
}
|
|
101
|
+
`.trim()
|
|
102
|
+
|
|
103
|
+
expect(applyTransform(input)).toBe(null)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it("returns null for empty file", () => {
|
|
107
|
+
expect(applyTransform("")).toBe(null)
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { commentOnSpreadPropsFactory } from "../../shared/transformFactories/commentOnSpreadPropsFactory"
|
|
4
|
+
|
|
5
|
+
const transform: Transform = commentOnSpreadPropsFactory({
|
|
6
|
+
targetComponent: "Dropdown",
|
|
7
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export default transform
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./dividerToSeparator"
|
|
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("dividerToSeparator transform", () => {
|
|
18
|
+
it("renames Divider to DropdownSeparator when inside a Dropdown", () => {
|
|
19
|
+
const input = `
|
|
20
|
+
import { Dropdown, Divider } from "@planningcenter/tapestry-react"
|
|
21
|
+
|
|
22
|
+
export default function Test() {
|
|
23
|
+
return <Dropdown><Divider /></Dropdown>
|
|
24
|
+
}
|
|
25
|
+
`.trim()
|
|
26
|
+
|
|
27
|
+
const result = applyTransform(input)
|
|
28
|
+
expect(result).toContain("<DropdownSeparator")
|
|
29
|
+
expect(result).not.toContain("<Divider")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("handles non-self-closing Divider inside a Dropdown", () => {
|
|
33
|
+
const input = `
|
|
34
|
+
import { Dropdown, Divider } from "@planningcenter/tapestry-react"
|
|
35
|
+
|
|
36
|
+
export default function Test() {
|
|
37
|
+
return <Dropdown><Divider></Divider></Dropdown>
|
|
38
|
+
}
|
|
39
|
+
`.trim()
|
|
40
|
+
|
|
41
|
+
const result = applyTransform(input)
|
|
42
|
+
expect(result).toContain("<DropdownSeparator>")
|
|
43
|
+
expect(result).toContain("</DropdownSeparator>")
|
|
44
|
+
expect(result).not.toContain("<Divider")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("renames multiple Dividers inside a Dropdown", () => {
|
|
48
|
+
const input = `
|
|
49
|
+
import { Dropdown, Divider } from "@planningcenter/tapestry-react"
|
|
50
|
+
|
|
51
|
+
export default function Test() {
|
|
52
|
+
return (
|
|
53
|
+
<Dropdown>
|
|
54
|
+
<Divider />
|
|
55
|
+
<Divider />
|
|
56
|
+
</Dropdown>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
`.trim()
|
|
60
|
+
|
|
61
|
+
const result = applyTransform(input)
|
|
62
|
+
const count = (result?.match(/<DropdownSeparator/g) || []).length
|
|
63
|
+
expect(count).toBe(2)
|
|
64
|
+
expect(result).not.toContain("<Divider")
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it("adds DropdownSeparator import from tapestry and removes Divider import when all Dividers are inside a Dropdown", () => {
|
|
68
|
+
const input = `
|
|
69
|
+
import { Dropdown, Divider } from "@planningcenter/tapestry-react"
|
|
70
|
+
|
|
71
|
+
export default function Test() {
|
|
72
|
+
return <Dropdown><Divider /></Dropdown>
|
|
73
|
+
}
|
|
74
|
+
`.trim()
|
|
75
|
+
|
|
76
|
+
const result = applyTransform(input)
|
|
77
|
+
expect(result).toContain('from "@planningcenter/tapestry"')
|
|
78
|
+
expect(result).toContain("DropdownSeparator")
|
|
79
|
+
expect(result).not.toContain(
|
|
80
|
+
'import { Dropdown, Divider } from "@planningcenter/tapestry-react"'
|
|
81
|
+
)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it("keeps Divider import when some Dividers are outside a Dropdown", () => {
|
|
85
|
+
const input = `
|
|
86
|
+
import { Dropdown, Divider } from "@planningcenter/tapestry-react"
|
|
87
|
+
|
|
88
|
+
export default function Test() {
|
|
89
|
+
return (
|
|
90
|
+
<div>
|
|
91
|
+
<Divider />
|
|
92
|
+
<Dropdown><Divider /></Dropdown>
|
|
93
|
+
</div>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
`.trim()
|
|
97
|
+
|
|
98
|
+
const result = applyTransform(input)
|
|
99
|
+
expect(result).toContain("DropdownSeparator")
|
|
100
|
+
expect(result).toContain(
|
|
101
|
+
'import { Dropdown, Divider } from "@planningcenter/tapestry-react"'
|
|
102
|
+
)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
describe("no changes scenarios", () => {
|
|
106
|
+
it("returns null when Divider is not inside a Dropdown", () => {
|
|
107
|
+
const input = `
|
|
108
|
+
import { Divider } from "@planningcenter/tapestry-react"
|
|
109
|
+
|
|
110
|
+
export default function Test() {
|
|
111
|
+
return <Divider />
|
|
112
|
+
}
|
|
113
|
+
`.trim()
|
|
114
|
+
|
|
115
|
+
expect(applyTransform(input)).toBe(null)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it("returns null when Divider is not imported from tapestry-react", () => {
|
|
119
|
+
const input = `
|
|
120
|
+
import { Divider } from "some-other-library"
|
|
121
|
+
|
|
122
|
+
export default function Test() {
|
|
123
|
+
return <Divider />
|
|
124
|
+
}
|
|
125
|
+
`.trim()
|
|
126
|
+
|
|
127
|
+
expect(applyTransform(input)).toBe(null)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it("returns null for empty file", () => {
|
|
131
|
+
expect(applyTransform("")).toBe(null)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
})
|