@planningcenter/tapestry-migration-cli 3.5.0 → 3.6.1-qa-1035.0

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 (52) hide show
  1. package/README.md +37 -0
  2. package/dist/tapestry-react-shim.cjs +195 -11
  3. package/package.json +3 -3
  4. package/src/availableComponents.ts +1 -0
  5. package/src/components/button/transforms/tooltipToWrapper.test.ts +50 -3
  6. package/src/components/button/transforms/tooltipToWrapper.ts +18 -0
  7. package/src/components/flex/index.test.ts +291 -0
  8. package/src/components/flex/index.ts +54 -0
  9. package/src/components/flex/transforms/alignmentToAlign.test.ts +185 -0
  10. package/src/components/flex/transforms/alignmentToAlign.ts +73 -0
  11. package/src/components/flex/transforms/auditSpreadProps.test.ts +93 -0
  12. package/src/components/flex/transforms/auditSpreadProps.ts +6 -0
  13. package/src/components/flex/transforms/axisToDirection.test.ts +155 -0
  14. package/src/components/flex/transforms/axisToDirection.ts +77 -0
  15. package/src/components/flex/transforms/convertStyleProps.test.ts +147 -0
  16. package/src/components/flex/transforms/convertStyleProps.ts +23 -0
  17. package/src/components/flex/transforms/cssFlexAliasesToProps.test.ts +202 -0
  18. package/src/components/flex/transforms/cssFlexAliasesToProps.ts +110 -0
  19. package/src/components/flex/transforms/distributionToJustify.test.ts +214 -0
  20. package/src/components/flex/transforms/distributionToJustify.ts +76 -0
  21. package/src/components/flex/transforms/flexPropToExpand.test.ts +156 -0
  22. package/src/components/flex/transforms/flexPropToExpand.ts +100 -0
  23. package/src/components/flex/transforms/innerRefToRef.test.ts +100 -0
  24. package/src/components/flex/transforms/innerRefToRef.ts +14 -0
  25. package/src/components/flex/transforms/mediaQueriesToResponsive.test.ts +714 -0
  26. package/src/components/flex/transforms/mediaQueriesToResponsive.ts +523 -0
  27. package/src/components/flex/transforms/moveFlexImport.test.ts +202 -0
  28. package/src/components/flex/transforms/moveFlexImport.ts +14 -0
  29. package/src/components/flex/transforms/paddingPropsToFlex.test.ts +252 -0
  30. package/src/components/flex/transforms/paddingPropsToFlex.ts +150 -0
  31. package/src/components/flex/transforms/setDefaultAxis.test.ts +126 -0
  32. package/src/components/flex/transforms/setDefaultAxis.ts +30 -0
  33. package/src/components/flex/transforms/spacingToGap.test.ts +176 -0
  34. package/src/components/flex/transforms/spacingToGap.ts +49 -0
  35. package/src/components/flex/transforms/unsupportedProps.test.ts +141 -0
  36. package/src/components/flex/transforms/unsupportedProps.ts +17 -0
  37. package/src/components/select/index.ts +2 -0
  38. package/src/components/select/transforms/onChangeSignature.test.ts +224 -0
  39. package/src/components/select/transforms/onChangeSignature.ts +172 -0
  40. package/src/components/shared/actions/resolveConstantAttributeValue.test.ts +122 -0
  41. package/src/components/shared/actions/resolveConstantAttributeValue.ts +31 -0
  42. package/src/components/shared/helpers/unsupportedPropsHelpers.ts +41 -0
  43. package/src/components/shared/transformFactories/stylePropTransformFactory.test.ts +47 -0
  44. package/src/components/shared/transformFactories/stylePropTransformFactory.ts +16 -0
  45. package/src/components/toggle-switch/index.ts +2 -0
  46. package/src/components/toggle-switch/transforms/onClickToOnChange.test.ts +210 -0
  47. package/src/components/toggle-switch/transforms/onClickToOnChange.ts +71 -0
  48. package/src/index.test.ts +79 -0
  49. package/src/index.ts +29 -0
  50. package/src/migrationList.ts +22 -0
  51. package/src/utils/componentLabel.test.ts +61 -0
  52. package/src/utils/componentLabel.ts +15 -0
package/README.md CHANGED
@@ -127,11 +127,48 @@ npx @planningcenter/tapestry-migration-cli run button -p ./src/components --js-t
127
127
  npx @planningcenter/tapestry-migration-cli run button -p ./src/components --js-theme ./theme.js --report-path ./migration-report.md
128
128
  ```
129
129
 
130
+ ### Listing available migrations
131
+
132
+ Use `list --json` to discover available migrations programmatically:
133
+
134
+ ```bash
135
+ npx @planningcenter/tapestry-migration-cli list --json
136
+ ```
137
+
138
+ **Stable JSON contract (stdout, exit 0):**
139
+
140
+ ```json
141
+ {
142
+ "schemaVersion": 1,
143
+ "migrations": [
144
+ { "id": "button", "label": "Button" },
145
+ { "id": "checkbox", "label": "Checkbox" }
146
+ ]
147
+ }
148
+ ```
149
+
150
+ Contract guarantees:
151
+
152
+ - Exit code `0` and **JSON only** on stdout — no progress text or emoji.
153
+ - `schemaVersion` is the integer `1`. Future breaking changes increment this version.
154
+ - Each `id` is the exact value accepted by `run <component-name>`.
155
+ - Each `label` is nonempty display text (Spaced Title Case).
156
+ - `id`s are unique; ordering is the CLI's recommended display order.
157
+ - No `--path` required; no filesystem writes.
158
+ - Diagnostics go to **stderr** with a **nonzero** exit code.
159
+
160
+ Human-readable listing (non-contractual):
161
+
162
+ ```bash
163
+ npx @planningcenter/tapestry-migration-cli list
164
+ ```
165
+
130
166
  ## Available Components
131
167
 
132
168
  - `button` - Migrate Button components
133
169
  - `checkbox` - Migrate Checkbox components
134
170
  - `date-picker` - Migrate DatePicker components
171
+ - `dropdown` - Migrate Dropdown components
135
172
  - `input` - Migrate Input components
136
173
  - `link` - Migrate Link components
137
174
  - `radio` - Migrate Radio components
@@ -1,5 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function _mergeNamespaces(n2, m2) {
4
+ for (var i2 = 0; i2 < m2.length; i2++) {
5
+ const e2 = m2[i2];
6
+ if (typeof e2 !== "string" && !Array.isArray(e2)) {
7
+ for (const k2 in e2) {
8
+ if (k2 !== "default" && !(k2 in n2)) {
9
+ const d2 = Object.getOwnPropertyDescriptor(e2, k2);
10
+ if (d2) {
11
+ Object.defineProperty(n2, k2, d2.get ? d2 : {
12
+ enumerable: true,
13
+ get: () => e2[k2]
14
+ });
15
+ }
16
+ }
17
+ }
18
+ }
19
+ }
20
+ return Object.freeze(Object.defineProperty(n2, Symbol.toStringTag, { value: "Module" }));
21
+ }
3
22
  const bellMuted = "M6.13,2.07l-1.04,-1.03c0.67,-0.48 1.54,-1.04 3.08,-1.04c1.54,0.01 3.01,0.62 4.09,1.68c1.07529,1.04811 1.67813,2.48843 1.67,3.99v4.27l-1.54,-1.55v-2.71c0,-0.54 -0.11,-1.07 -0.32,-1.56c-0.21,-0.5 -0.52,-0.95 -0.92,-1.33c-0.39,-0.38 -0.86,-0.68 -1.37,-0.89c-0.51,-0.21 -1.06,-0.31 -1.62,-0.31c-0.70992,0.00342 -1.40896,0.17475 -2.04,0.5zM16,14.59l-1.4,1.41l-1.74,-1.74h-2.15c-0.2,0.48 -0.54,0.9 -0.99,1.19c-0.45,0.29 -0.97,0.45 -1.51,0.45c-0.54,0 -1.06,-0.16 -1.51,-0.45c-0.45,-0.29 -0.79,-0.71 -0.99,-1.19h-4.08c-0.31,0 -0.62,-0.09 -0.88,-0.25c-0.26165,-0.16091 -0.4676,-0.39827 -0.59,-0.68c-0.13,-0.28 -0.18,-0.59 -0.14,-0.89c0.04,-0.3 0.17,-0.59 0.38,-0.82l2.06,-2.38v-3.58c0,-0.45 0.16,-1.2 0.25,-1.59l-2.11,-2.1l1.4,-1.41zM11.34,12.73l-7.34,-7.36c0,0.1 -0.02,0.21 -0.02,0.29v3.85c0,0.19 -0.07,0.38 -0.19,0.52l-2.23,2.58l0.07,0.12z";
4
23
  const bell = "M15.233,11.506c0.38455,0.4554 0.46962,1.09267 0.218,1.633c-0.23682,0.53936 -0.77094,0.88694 -1.36,0.885h-3.782c-0.377,0.9449 -1.29167,1.56474 -2.309,1.56474c-1.01733,0 -1.932,-0.61985 -2.309,-1.56474h-3.78c-0.59015,0.00313 -1.12575,-0.34463 -1.363,-0.885c-0.25178,-0.54079 -0.16629,-1.1786 0.219,-1.634l1.905,-2.271v-3.417c-0.01886,-2.9628 2.36425,-5.38169 5.327,-5.407c2.9633,0.02476 5.3473,2.44365 5.329,5.407v3.417l1.905,2.272";
5
24
  const bill = "M15.805,2.176c0.12423,0.10317 0.19577,0.25652 0.195,0.418v10.795c0.00021,0.08218 -0.01647,0.16353 -0.049,0.239c-0.11474,0.23112 -0.352,0.37581 -0.61,0.372h-14.665c-0.177,0.003 -0.349,-0.06 -0.482,-0.177c-0.12591,-0.10868 -0.19699,-0.2677 -0.194,-0.434v-10.795c-0.00102,-0.16132 0.07014,-0.31464 0.194,-0.418c0.1326,-0.11756 0.30485,-0.18046 0.482,-0.176h14.664c0.17184,-0.00314 0.33831,0.05986 0.465,0.176M14.798,5.565c-0.11067,0.022 -0.22267,0.03367 -0.336,0.035c-0.64408,-0.00048 -1.26271,-0.25152 -1.725,-0.7c-0.461,-0.447 -0.73,-1.057 -0.75,-1.7h-7.947c-0.0426,1.33678 -1.13754,2.39898 -2.475,2.401c-0.122,-0.00067 -0.243,-0.01333 -0.363,-0.038v4.876c0.11959,-0.0237 0.24109,-0.03642 0.363,-0.038c1.33921,-0.00325 2.43705,1.06133 2.475,2.4h7.948c0.03847,-1.33789 1.13556,-2.40172 2.474,-2.399c0.11276,0.00184 0.22516,0.01321 0.336,0.034zM10.343,5.231c0.31189,0.36828 0.55631,0.78873 0.722,1.242c0.36,0.987 0.36,2.068 0,3.055c-0.166,0.453 -0.41,0.873 -0.721,1.243c-0.2937,0.34506 -0.65341,0.62794 -1.058,0.832c-0.81776,0.40581 -1.77824,0.40581 -2.596,0c-0.406,-0.204 -0.766,-0.487 -1.06,-0.832c-0.31,-0.367 -0.549,-0.789 -0.706,-1.243c-0.34328,-0.9894 -0.34328,-2.0656 0,-3.055c0.15504,-0.45349 0.39234,-0.87453 0.7,-1.242c0.29429,-0.34528 0.6547,-0.62816 1.06,-0.832c0.40423,-0.2014 0.8504,-0.3042 1.302,-0.3c0.45185,-0.00653 0.89857,0.0964 1.302,0.3c0.40351,0.2044 0.76218,0.48725 1.055,0.832M9.334,9.351h-0.422c-0.08583,0.00545 -0.16953,-0.02816 -0.22776,-0.09144c-0.05824,-0.06328 -0.08478,-0.14948 -0.07224,-0.23456v-3.205h-0.883c-0.12661,0.35602 -0.4801,0.57943 -0.856,0.541h-0.17v0.57h0.735v2.093c0.01178,0.08493 -0.01503,0.17068 -0.07309,0.23377c-0.05806,0.06309 -0.14129,0.09692 -0.22691,0.09223h-0.448v0.57h2.642z";
@@ -648,7 +667,7 @@ function requireReact_development() {
648
667
  var REACT_SUSPENSE_TYPE = Symbol.for("react.suspense");
649
668
  var REACT_SUSPENSE_LIST_TYPE = Symbol.for("react.suspense_list");
650
669
  var REACT_MEMO_TYPE = Symbol.for("react.memo");
651
- var REACT_LAZY_TYPE = Symbol.for("react.lazy");
670
+ var REACT_LAZY_TYPE2 = Symbol.for("react.lazy");
652
671
  var REACT_OFFSCREEN_TYPE = Symbol.for("react.offscreen");
653
672
  var MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
654
673
  var FAUX_ITERATOR_SYMBOL = "@@iterator";
@@ -984,7 +1003,7 @@ function requireReact_development() {
984
1003
  return outerName;
985
1004
  }
986
1005
  return getComponentNameFromType(type.type) || "Memo";
987
- case REACT_LAZY_TYPE: {
1006
+ case REACT_LAZY_TYPE2: {
988
1007
  var lazyComponent = type;
989
1008
  var payload = lazyComponent._payload;
990
1009
  var init = lazyComponent._init;
@@ -1534,7 +1553,7 @@ function requireReact_development() {
1534
1553
  _result: ctor
1535
1554
  };
1536
1555
  var lazyType = {
1537
- $$typeof: REACT_LAZY_TYPE,
1556
+ $$typeof: REACT_LAZY_TYPE2,
1538
1557
  _payload: payload,
1539
1558
  _init: lazyInitializer
1540
1559
  };
@@ -1623,7 +1642,7 @@ function requireReact_development() {
1623
1642
  return true;
1624
1643
  }
1625
1644
  if (typeof type === "object" && type !== null) {
1626
- if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
1645
+ if (type.$$typeof === REACT_LAZY_TYPE2 || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
1627
1646
  // types supported by any Flight configuration anywhere since
1628
1647
  // we don't know which Flight build this will end up being used
1629
1648
  // with.
@@ -1978,7 +1997,7 @@ function requireReact_development() {
1978
1997
  return describeFunctionComponentFrame(type.render);
1979
1998
  case REACT_MEMO_TYPE:
1980
1999
  return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn);
1981
- case REACT_LAZY_TYPE: {
2000
+ case REACT_LAZY_TYPE2: {
1982
2001
  var lazyComponent = type;
1983
2002
  var payload = lazyComponent._payload;
1984
2003
  var init = lazyComponent._init;
@@ -2513,6 +2532,10 @@ function requireReact() {
2513
2532
  }
2514
2533
  var reactExports = requireReact();
2515
2534
  const React = /* @__PURE__ */ getDefaultExportFromCjs(reactExports);
2535
+ const React$1 = /* @__PURE__ */ _mergeNamespaces({
2536
+ __proto__: null,
2537
+ default: React
2538
+ }, [reactExports]);
2516
2539
  function LoadingSpinner() {
2517
2540
  return React.createElement("div", { className: "tds-loading-spinner" });
2518
2541
  }
@@ -2767,14 +2790,175 @@ const PageHeaderActionsDropdownButton = reactExports.forwardRef(({ className, ne
2767
2790
  return React.createElement(DropdownButton, { ref, ...props, kind: "secondary-page-header", ...needsAttentionProps({ className, needsAttention }) });
2768
2791
  });
2769
2792
  PageHeaderActionsDropdownButton.displayName = "PageHeaderActionsDropdownButton";
2770
- const BaseLink = reactExports.forwardRef(({ children, className, external = false, fullWidth, href, kind, label, prefix, size, suffix, ...restProps }, ref) => {
2793
+ function setRef(ref, value) {
2794
+ if (typeof ref === "function") {
2795
+ return ref(value);
2796
+ } else if (ref !== null && ref !== void 0) {
2797
+ ref.current = value;
2798
+ }
2799
+ }
2800
+ function composeRefs(...refs) {
2801
+ return (node) => {
2802
+ let hasCleanup = false;
2803
+ const cleanups = refs.map((ref) => {
2804
+ const cleanup = setRef(ref, node);
2805
+ if (!hasCleanup && typeof cleanup == "function") {
2806
+ hasCleanup = true;
2807
+ }
2808
+ return cleanup;
2809
+ });
2810
+ if (hasCleanup) {
2811
+ return () => {
2812
+ for (let i2 = 0; i2 < cleanups.length; i2++) {
2813
+ const cleanup = cleanups[i2];
2814
+ if (typeof cleanup == "function") {
2815
+ cleanup();
2816
+ } else {
2817
+ setRef(refs[i2], null);
2818
+ }
2819
+ }
2820
+ };
2821
+ }
2822
+ };
2823
+ }
2824
+ function useComposedRefs(...refs) {
2825
+ return reactExports.useCallback(composeRefs(...refs), refs);
2826
+ }
2827
+ // @__NO_SIDE_EFFECTS__
2828
+ function createSlot(ownerName) {
2829
+ const Slot2 = reactExports.forwardRef((props, forwardedRef) => {
2830
+ let { children, ...slotProps } = props;
2831
+ let slottableElement = null;
2832
+ let hasSlottable = false;
2833
+ const newChildren = [];
2834
+ if (isLazyComponent(children) && typeof use === "function") {
2835
+ children = use(children._payload);
2836
+ }
2837
+ reactExports.Children.forEach(children, (maybeSlottable) => {
2838
+ if (isSlottable(maybeSlottable)) {
2839
+ hasSlottable = true;
2840
+ const slottable = maybeSlottable;
2841
+ let child = "child" in slottable.props ? slottable.props.child : slottable.props.children;
2842
+ if (isLazyComponent(child) && typeof use === "function") {
2843
+ child = use(child._payload);
2844
+ }
2845
+ slottableElement = getSlottableElementFromSlottable(slottable, child);
2846
+ newChildren.push(slottableElement?.props?.children);
2847
+ } else {
2848
+ newChildren.push(maybeSlottable);
2849
+ }
2850
+ });
2851
+ if (slottableElement) {
2852
+ slottableElement = reactExports.cloneElement(slottableElement, void 0, newChildren);
2853
+ } else if (
2854
+ // A `Slottable` was found but it didn't resolve to a single element (e.g.
2855
+ // it wrapped multiple elements, text, or a render-prop `child` that
2856
+ // wasn't an element). Don't fall back to treating the `Slottable` wrapper
2857
+ // itself as the slot target — throw a descriptive error below instead.
2858
+ !hasSlottable && reactExports.Children.count(children) === 1 && reactExports.isValidElement(children)
2859
+ ) {
2860
+ slottableElement = children;
2861
+ }
2862
+ const slottableElementRef = slottableElement ? getElementRef(slottableElement) : void 0;
2863
+ const composedRef = useComposedRefs(forwardedRef, slottableElementRef);
2864
+ if (!slottableElement) {
2865
+ if (children || children === 0) {
2866
+ throw new Error(
2867
+ hasSlottable ? createSlottableError(ownerName) : createSlotError(ownerName)
2868
+ );
2869
+ }
2870
+ return children;
2871
+ }
2872
+ const mergedProps = mergeProps(slotProps, slottableElement.props ?? {});
2873
+ if (slottableElement.type !== reactExports.Fragment) {
2874
+ mergedProps.ref = forwardedRef ? composedRef : slottableElementRef;
2875
+ }
2876
+ return reactExports.cloneElement(slottableElement, mergedProps);
2877
+ });
2878
+ Slot2.displayName = `${ownerName}.Slot`;
2879
+ return Slot2;
2880
+ }
2881
+ var Slot = /* @__PURE__ */ createSlot("Slot");
2882
+ var SLOTTABLE_IDENTIFIER = Symbol.for("radix.slottable");
2883
+ // @__NO_SIDE_EFFECTS__
2884
+ function createSlottable(ownerName) {
2885
+ const Slottable2 = (props) => "child" in props ? props.children(props.child) : props.children;
2886
+ Slottable2.displayName = `${ownerName}.Slottable`;
2887
+ Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
2888
+ return Slottable2;
2889
+ }
2890
+ var Slottable = /* @__PURE__ */ createSlottable("Slottable");
2891
+ var getSlottableElementFromSlottable = (slottable, child) => {
2892
+ if ("child" in slottable.props) {
2893
+ const child2 = slottable.props.child;
2894
+ if (!reactExports.isValidElement(child2)) return null;
2895
+ return reactExports.cloneElement(child2, void 0, slottable.props.children(child2.props.children));
2896
+ }
2897
+ return reactExports.isValidElement(child) ? child : null;
2898
+ };
2899
+ function mergeProps(slotProps, childProps) {
2900
+ const overrideProps = { ...childProps };
2901
+ for (const propName in childProps) {
2902
+ const slotPropValue = slotProps[propName];
2903
+ const childPropValue = childProps[propName];
2904
+ const isHandler = /^on[A-Z]/.test(propName);
2905
+ if (isHandler) {
2906
+ if (slotPropValue && childPropValue) {
2907
+ overrideProps[propName] = (...args) => {
2908
+ const result = childPropValue(...args);
2909
+ slotPropValue(...args);
2910
+ return result;
2911
+ };
2912
+ } else if (slotPropValue) {
2913
+ overrideProps[propName] = slotPropValue;
2914
+ }
2915
+ } else if (propName === "style") {
2916
+ overrideProps[propName] = { ...slotPropValue, ...childPropValue };
2917
+ } else if (propName === "className") {
2918
+ overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
2919
+ }
2920
+ }
2921
+ return { ...slotProps, ...overrideProps };
2922
+ }
2923
+ function getElementRef(element) {
2924
+ let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
2925
+ let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
2926
+ if (mayWarn) {
2927
+ return element.ref;
2928
+ }
2929
+ getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
2930
+ mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
2931
+ if (mayWarn) {
2932
+ return element.props.ref;
2933
+ }
2934
+ return element.props.ref || element.ref;
2935
+ }
2936
+ function isSlottable(child) {
2937
+ return reactExports.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
2938
+ }
2939
+ var REACT_LAZY_TYPE = Symbol.for("react.lazy");
2940
+ function isLazyComponent(element) {
2941
+ return element != null && typeof element === "object" && "$$typeof" in element && element.$$typeof === REACT_LAZY_TYPE && "_payload" in element && isPromiseLike(element._payload);
2942
+ }
2943
+ function isPromiseLike(value) {
2944
+ return typeof value === "object" && value !== null && "then" in value;
2945
+ }
2946
+ var createSlotError = (ownerName) => {
2947
+ return `${ownerName} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`;
2948
+ };
2949
+ var createSlottableError = (ownerName) => {
2950
+ return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
2951
+ };
2952
+ var use = React$1[" use ".trim().toString()];
2953
+ const BaseLink = reactExports.forwardRef(({ asChild = false, children, className, external = false, fullWidth, href, kind, label, prefix, size, suffix, ...restProps }, ref) => {
2771
2954
  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 });
2772
2955
  const externalProps = external ? { rel: "noopener noreferrer", target: "_blank" } : {};
2956
+ const Comp = asChild ? Slot : "a";
2773
2957
  return React.createElement(
2774
- "a",
2775
- { href, className: combinedClassName, ref, ...restProps, ...externalProps },
2958
+ Comp,
2959
+ { ...!asChild && { href }, className: combinedClassName, ref, ...restProps, ...externalProps },
2776
2960
  wrapStringWithSpan(prefix),
2777
- label || children,
2961
+ React.createElement(Slottable, null, label || children),
2778
2962
  wrapStringWithSpan(suffix)
2779
2963
  );
2780
2964
  });
@@ -2784,8 +2968,8 @@ const IconLink = reactExports.forwardRef(({ className, href, icon, ...restProps
2784
2968
  return React.createElement(BaseLink, { href, label: icon, className: iconOnlyClassName, ref, ...restProps });
2785
2969
  });
2786
2970
  IconLink.displayName = "IconLink";
2787
- const Link = reactExports.forwardRef(({ href, ...restProps }, ref) => {
2788
- return React.createElement(BaseLink, { href, ref, ...restProps });
2971
+ const Link = reactExports.forwardRef((props, ref) => {
2972
+ return React.createElement(BaseLink, { ref, ...props });
2789
2973
  });
2790
2974
  Link.displayName = "Link";
2791
2975
  const tokens = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/tapestry-migration-cli",
3
- "version": "3.5.0",
3
+ "version": "3.6.1-qa-1035.0",
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.5.0",
33
+ "@planningcenter/tapestry": "^3.6.1-qa-1035.0",
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": "958f1f269ca37d78e9a9c0ec66c208a7115331dc"
53
+ "gitHead": "2b87acce23b1df6872d7bbeadd648b117c9962c4"
54
54
  }
@@ -3,6 +3,7 @@ export const AVAILABLE_CLI_COMPONENTS = new Set([
3
3
  "checkbox",
4
4
  "date-picker",
5
5
  "dropdown",
6
+ "flex",
6
7
  "input",
7
8
  "link",
8
9
  "radio",
@@ -84,9 +84,9 @@ export default function Test() {
84
84
 
85
85
  expect(result).toContain('<Tooltip {...{title: "Help"}}')
86
86
  expect(result).toContain("refAsInnerRef={false}")
87
- expect(result).toContain(
88
- '<Button variant="primary" onClick={handler} disabled>Save</Button>'
89
- )
87
+ expect(result).toContain('variant="primary"')
88
+ expect(result).toContain("onClick={handler}")
89
+ expect(result).toContain("disabled>Save</Button>")
90
90
  expect(result).not.toContain("tooltip=")
91
91
  })
92
92
 
@@ -323,6 +323,53 @@ export default function Test({ items }) {
323
323
  expect(result).not.toContain("tooltip=")
324
324
  })
325
325
 
326
+ it("should add a TODO comment when the Button is disabled", () => {
327
+ const input = `
328
+ import { Button } from "@planningcenter/tapestry-react"
329
+
330
+ export default function Test() {
331
+ return <Button tooltip={{ title: "Save" }} disabled>Save</Button>
332
+ }
333
+ `.trim()
334
+
335
+ const result = applyTransform(input)
336
+
337
+ expect(result).toContain("TODO: tapestry-migration (disabled):")
338
+ expect(result).toContain("<Tooltip")
339
+ expect(result).toContain("disabled>Save</Button>")
340
+ expect(result).not.toContain("tooltip=")
341
+ })
342
+
343
+ it("should add a TODO comment when disabled has an expression value", () => {
344
+ const input = `
345
+ import { Button } from "@planningcenter/tapestry-react"
346
+
347
+ export default function Test({ isDisabled }) {
348
+ return <Button tooltip={{ title: "Save" }} disabled={isDisabled}>Save</Button>
349
+ }
350
+ `.trim()
351
+
352
+ const result = applyTransform(input)
353
+
354
+ expect(result).toContain("TODO: tapestry-migration (disabled):")
355
+ expect(result).toContain("disabled={isDisabled}")
356
+ })
357
+
358
+ it("should not add a disabled TODO comment when the Button is not disabled", () => {
359
+ const input = `
360
+ import { Button } from "@planningcenter/tapestry-react"
361
+
362
+ export default function Test() {
363
+ return <Button tooltip={{ title: "Save" }}>Save</Button>
364
+ }
365
+ `.trim()
366
+
367
+ const result = applyTransform(input)
368
+
369
+ expect(result).toContain("<Tooltip")
370
+ expect(result).not.toContain("TODO: tapestry-migration (disabled):")
371
+ })
372
+
326
373
  it("should handle empty tooltip attribute", () => {
327
374
  const input = `
328
375
  import { Button } from "@planningcenter/tapestry-react"
@@ -1,5 +1,7 @@
1
1
  import { addAttribute } from "../../shared/actions/addAttribute"
2
+ import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
2
3
  import { createWrapper } from "../../shared/actions/createWrapper"
4
+ import { getAttribute } from "../../shared/actions/getAttribute"
3
5
  import { getAttributeValueAsProps } from "../../shared/actions/getAttributeValueAsProps"
4
6
  import { removeAttribute } from "../../shared/actions/removeAttribute"
5
7
  import { hasAttribute } from "../../shared/conditions/hasAttribute"
@@ -19,6 +21,11 @@ const transform = attributeTransformFactory({
19
21
 
20
22
  removeAttribute("tooltip", { element, j, source })
21
23
 
24
+ // Capture the original Button's `disabled` attribute before `createWrapper`
25
+ // replaces `element` in the AST, so the comment below clearly attaches to
26
+ // the original Button attribute node.
27
+ const disabledAttribute = getAttribute({ element, name: "disabled" })
28
+
22
29
  const wrapperElement = createWrapper({
23
30
  conflictAlias: "TRTooltip",
24
31
  element,
@@ -36,6 +43,17 @@ const transform = attributeTransformFactory({
36
43
  value: false,
37
44
  })
38
45
 
46
+ if (disabledAttribute) {
47
+ addCommentToAttribute({
48
+ attribute: disabledAttribute,
49
+ j,
50
+ text:
51
+ "This Button is disabled. Disabled buttons don't emit pointer or focus events, " +
52
+ "so the wrapping Tooltip will not appear. Keep the button interactive (e.g. handle " +
53
+ "the disabled state without the native `disabled` attribute) so the Tooltip can trigger.",
54
+ })
55
+ }
56
+
39
57
  return true
40
58
  },
41
59
  })