@multiplatform.one/forms 7.47.4 → 7.48.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 (40) hide show
  1. package/dist/cjs/Button/index.cjs +22 -20
  2. package/dist/cjs/Button/index.native.js +22 -20
  3. package/dist/cjs/Button/index.native.js.map +1 -1
  4. package/dist/cjs/Spinner/index.cjs +2 -1
  5. package/dist/cjs/Spinner/index.native.js +2 -1
  6. package/dist/cjs/Spinner/index.native.js.map +1 -1
  7. package/dist/cjs/Spinner/spinnerSize.native.js.map +1 -1
  8. package/dist/esm/Button/index.mjs +22 -20
  9. package/dist/esm/Button/index.mjs.map +1 -1
  10. package/dist/esm/Button/index.native.js +22 -20
  11. package/dist/esm/Button/index.native.js.map +1 -1
  12. package/dist/esm/Spinner/index.mjs +2 -1
  13. package/dist/esm/Spinner/index.mjs.map +1 -1
  14. package/dist/esm/Spinner/index.native.js +2 -1
  15. package/dist/esm/Spinner/index.native.js.map +1 -1
  16. package/dist/esm/Spinner/spinnerSize.mjs.map +1 -1
  17. package/dist/esm/Spinner/spinnerSize.native.js.map +1 -1
  18. package/dist/jsx/Button/index.mjs +22 -20
  19. package/dist/jsx/Button/index.mjs.map +1 -1
  20. package/dist/jsx/Button/index.native.js +22 -20
  21. package/dist/jsx/Button/index.native.js.map +1 -1
  22. package/dist/jsx/Spinner/index.mjs +2 -1
  23. package/dist/jsx/Spinner/index.mjs.map +1 -1
  24. package/dist/jsx/Spinner/index.native.js +2 -1
  25. package/dist/jsx/Spinner/index.native.js.map +1 -1
  26. package/dist/jsx/Spinner/spinnerSize.mjs.map +1 -1
  27. package/dist/jsx/Spinner/spinnerSize.native.js.map +1 -1
  28. package/package.json +8 -8
  29. package/src/Button/Button.loading.spec.tsx +63 -16
  30. package/src/Button/index.tsx +20 -24
  31. package/src/Spinner/index.native.tsx +2 -2
  32. package/src/Spinner/index.tsx +2 -2
  33. package/src/Spinner/spinnerSize.ts +2 -0
  34. package/types/Button/index.d.ts.map +1 -1
  35. package/types/Spinner/index.d.ts +1 -1
  36. package/types/Spinner/index.d.ts.map +1 -1
  37. package/types/Spinner/index.native.d.ts +1 -1
  38. package/types/Spinner/index.native.d.ts.map +1 -1
  39. package/types/Spinner/spinnerSize.d.ts +2 -0
  40. package/types/Spinner/spinnerSize.d.ts.map +1 -1
@@ -5,9 +5,10 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
5
  import { Button } from "./index";
6
6
 
7
7
  /**
8
- * WP-c2: a loading Button renders the ONE Skeleton (forms, re-exported by
9
- * components) at the button's recipe height, and the bone stops pulsing for a
10
- * user who asked for reduced motion.
8
+ * FZ-7812: a loading Button keeps its label and its rest chrome; a spinner
9
+ * takes the leading icon slot. WP-c2's skeleton bone stays the `skeleton`
10
+ * placeholder (a button that has not loaded), and stops pulsing for a user
11
+ * who asked for reduced motion.
11
12
  */
12
13
 
13
14
  let reduced = false;
@@ -32,30 +33,76 @@ afterEach(() => {
32
33
  });
33
34
 
34
35
  function bone(container: HTMLElement): HTMLElement {
35
- const el = container.querySelector("[data-button-loading] [data-skeleton]");
36
- if (!el) throw new Error("no skeleton bone in the loading button");
36
+ const el = container.querySelector("[data-skeleton]");
37
+ if (!el) throw new Error("no skeleton bone in the skeleton button");
37
38
  return el as HTMLElement;
38
39
  }
39
40
 
40
- describe("Button loading (WP-c2 one Skeleton)", () => {
41
- it("renders the shared rounded Skeleton at the recipe height in the label slot", () => {
41
+ describe("Button loading (FZ-7812 label stays)", () => {
42
+ it("keeps the label and puts a spinner in the leading slot", () => {
42
43
  const { container, getByRole } = renderWithProviders(
43
44
  <Button loading size="$3">
44
45
  Save
45
46
  </Button>,
46
47
  );
48
+ const button = getByRole("button");
49
+ expect(button.getAttribute("aria-busy")).toBe("true");
50
+ expect(button.textContent).toContain("Save");
51
+ const slot = container.querySelector("[data-button-loading]");
52
+ expect(slot).toBeTruthy();
53
+ expect(slot?.getAttribute("aria-hidden")).toBe("true");
54
+ expect(slot?.nextElementSibling?.textContent).toBe("Save");
55
+ expect(container.querySelector("[data-skeleton]")).toBeNull();
56
+ });
57
+
58
+ it("takes an icon's slot instead of adding a second glyph", () => {
59
+ const Icon = () => <svg data-testid="icon" />;
60
+ const { container, queryByTestId } = renderWithProviders(
61
+ <Button loading icon={Icon}>
62
+ Save
63
+ </Button>,
64
+ );
65
+ expect(queryByTestId("icon")).toBeNull();
66
+ expect(container.querySelectorAll("[data-button-loading]")).toHaveLength(1);
67
+ });
68
+
69
+ it("keeps the rest chrome: loading is not the disabled wash", () => {
70
+ const rest = renderWithProviders(<Button chromeless>Log in</Button>);
71
+ const restStyle = getComputedStyle(rest.getByRole("button"));
72
+ const restBg = restStyle.backgroundColor;
73
+ const restInk = getComputedStyle(rest.getByText("Log in")).color;
74
+ rest.unmount();
75
+ const busy = renderWithProviders(
76
+ <Button chromeless loading>
77
+ Log in
78
+ </Button>,
79
+ );
80
+ expect(getComputedStyle(busy.getByRole("button")).backgroundColor).toBe(restBg);
81
+ expect(getComputedStyle(busy.getByText("Log in")).color).toBe(restInk);
82
+ });
83
+
84
+ it("swallows presses while busy", () => {
85
+ const onPress = vi.fn();
86
+ const { getByRole } = renderWithProviders(
87
+ <Button loading onPress={onPress}>
88
+ Save
89
+ </Button>,
90
+ );
91
+ getByRole("button").click();
92
+ expect(onPress).not.toHaveBeenCalled();
93
+ });
94
+ });
95
+
96
+ describe("Button skeleton (WP-c2 one Skeleton)", () => {
97
+ it("renders the shared rounded Skeleton at the recipe height", () => {
98
+ const { container } = renderWithProviders(<Button skeleton size="$3" />);
47
99
  const el = bone(container);
48
100
  expect(el.getAttribute("data-skeleton-variant")).toBe("rounded");
49
101
  expect(el.getAttribute("data-height")).toBe(String(sizeRecipeForToken("$3").height));
50
- expect(el.getAttribute("aria-hidden")).toBe("true");
51
- const button = getByRole("button");
52
- expect(button.getAttribute("aria-busy")).toBe("true");
53
- // The label is swapped out for the bone (busy, not narrated).
54
- expect(button.textContent).not.toContain("Save");
55
102
  });
56
103
 
57
104
  it("pulses with the animation knob on", () => {
58
- const { container } = renderWithProviders(<Button loading>Save</Button>);
105
+ const { container } = renderWithProviders(<Button skeleton />);
59
106
  const el = bone(container);
60
107
  expect(el.getAttribute("data-animate")).toBe("true");
61
108
  expect(el.className).toMatch(/mp-skeleton-pulse/);
@@ -64,7 +111,7 @@ describe("Button loading (WP-c2 one Skeleton)", () => {
64
111
 
65
112
  it("respects prefers-reduced-motion: the bone goes static", () => {
66
113
  reduced = true;
67
- const { container } = renderWithProviders(<Button loading>Save</Button>);
114
+ const { container } = renderWithProviders(<Button skeleton />);
68
115
  const el = bone(container);
69
116
  expect(el.getAttribute("data-animate")).toBe("false");
70
117
  expect(el.className).toMatch(/mp-skeleton-static/);
@@ -74,8 +121,8 @@ describe("Button loading (WP-c2 one Skeleton)", () => {
74
121
  it("mounts one keyframe sheet that also stops motion under reduced motion", () => {
75
122
  renderWithProviders(
76
123
  <>
77
- <Button loading>One</Button>
78
- <Button loading>Two</Button>
124
+ <Button skeleton />
125
+ <Button skeleton />
79
126
  </>,
80
127
  );
81
128
  const sheets = document.querySelectorAll("#__mp-skeleton-pulse");
@@ -52,6 +52,7 @@ import {
52
52
  useTheme,
53
53
  } from "tamagui";
54
54
  import { Skeleton } from "../Skeleton";
55
+ import { Spinner } from "../Spinner";
55
56
  import type { ThemeName } from "@tamagui/web";
56
57
  import type { GestureResponderEvent } from "react-native";
57
58
  import type { AnyFormApi } from "../types";
@@ -501,7 +502,10 @@ export function Button({
501
502
  );
502
503
  }
503
504
 
504
- const isDisabled = disabled || loading || (action === "submit" && resolvedForm && !isValid);
505
+ // Busy is not unavailable: a loading button swallows presses but keeps its
506
+ // rest chrome and label. Washed, it read as stuck pressed (FZ-7812).
507
+ const unavailable = Boolean(disabled || (action === "submit" && resolvedForm && !isValid));
508
+ const isDisabled = unavailable || Boolean(loading);
505
509
 
506
510
  const handlePress = (e: GestureResponderEvent) => {
507
511
  // Explained disable keeps the frame press-able (aria-disabled, focusable)
@@ -542,7 +546,7 @@ export function Button({
542
546
  // where `$color11` misses 4.5:1 (light warning 4.48, success 4.27) and the
543
547
  // hue's `$color12` text step clears it.
544
548
  const washOnHost = Boolean(
545
- isDisabled && activeThemeName && solidPaletteThemes.has(activeThemeName),
549
+ unavailable && activeThemeName && solidPaletteThemes.has(activeThemeName),
546
550
  );
547
551
  const disabledSurface = washOnHost
548
552
  ? hostScoped(disabledState.surfaceKnobProps)
@@ -607,7 +611,7 @@ export function Button({
607
611
  ];
608
612
  const isSelected =
609
613
  selected ?? (ariaPressedProp === true || ariaPressedProp === "true" ? true : undefined);
610
- const selectedFill = Boolean(isSelected) && !isDisabled;
614
+ const selectedFill = Boolean(isSelected) && !unavailable;
611
615
  const iconSize = nested
612
616
  ? Math.min(recipe.iconSize, Math.max(12, Math.round(knobProps.nestedControl.px * 0.45)))
613
617
  : recipe.iconSize;
@@ -647,7 +651,7 @@ export function Button({
647
651
  : undefined)}
648
652
  // LC-40: on the washed disabled chrome the label leaves the on-fill
649
653
  // foreground for the wash's AA ink (keepLabel keeps >=4.5:1).
650
- {...(isDisabled ? disabledText : undefined)}
654
+ {...(unavailable ? disabledText : undefined)}
651
655
  // Boards measure ink.weight on the TEXT NODE, never the frame. The
652
656
  // prop is restated after the spreads because SizableText's size
653
657
  // variant otherwise leaks 500/600 over it (MPO-125: a literal 400
@@ -669,29 +673,21 @@ export function Button({
669
673
  : flattenedChildren?.some(isBareTextChild)
670
674
  ? wrapBareTextChildren(flattenedChildren, renderLabelText)
671
675
  : resolvedChildren;
672
- // Loading shows state, not a narration: the shared Skeleton bone takes the
673
- // label's slot at the recipe height (the label is swapped out; hosts such as
674
- // Toolbar name the busy button through `aria-label`). The frame stays, so
675
- // focus never leaves the button.
676
- const textContent = loading ? (
677
- <View data-button-loading="">
678
- <Skeleton variant="rounded" height={boneHeight} width={boneHeight * 2} aria-hidden />
679
- </View>
680
- ) : (
681
- labelContent
682
- );
683
-
684
- const hasIcon = icon || iconAfter;
676
+ const hasIcon = icon || iconAfter || loading;
685
677
  const nativeIconColor =
686
678
  props.color ??
687
- (isDisabled ? disabledText.color : undefined) ??
679
+ (unavailable ? disabledText.color : undefined) ??
688
680
  (textColor
689
681
  ? outlinedOnHost
690
682
  ? hostScoped({ color: textColor }).color
691
683
  : textColor
692
684
  : outlinedInk) ??
693
685
  "$color";
694
- const iconElement = icon ? (
686
+ const iconElement = loading ? (
687
+ <View flexShrink={0} data-button-loading="" aria-hidden>
688
+ <Spinner px={iconSize} color={nativeIconColor as string} aria-hidden />
689
+ </View>
690
+ ) : icon ? (
695
691
  <View flexShrink={0}>
696
692
  {isWeb ? (
697
693
  normalizeButtonIcon(icon, iconSize)
@@ -710,7 +706,7 @@ export function Button({
710
706
  </View>
711
707
  ) : null;
712
708
 
713
- const explainedDisable = Boolean(disabledReason?.trim()) && Boolean(isDisabled);
709
+ const explainedDisable = Boolean(disabledReason?.trim()) && unavailable;
714
710
  const generatedId = useId();
715
711
  const reasonId = explainedDisable
716
712
  ? `${(props as { id?: string }).id ?? generatedId}-disabled-reason`
@@ -837,7 +833,7 @@ export function Button({
837
833
  // (`color` inherits into currentColor icons); dimWhole dims the
838
834
  // button as its own assembly. Replaces the old hardcoded 0.5
839
835
  // whole-dim that dropped label contrast under AA.
840
- {...(isDisabled
836
+ {...(unavailable
841
837
  ? {
842
838
  ...disabledSurface,
843
839
  ...disabledText,
@@ -880,7 +876,7 @@ export function Button({
880
876
  onPress={handlePress}
881
877
  {...(isWeb ? { onKeyDown: handleKeyDown as unknown as () => void } : undefined)}
882
878
  {...(tabIndex != null ? { tabIndex } : undefined)}
883
- gap={hasIcon && (resolvedChildren || textContent) ? innerGap : undefined}
879
+ gap={hasIcon && (resolvedChildren || labelContent) ? innerGap : undefined}
884
880
  // Form actions hug their label (Clay: no stretch): a Button inside a
885
881
  // Form never takes the column's full width. A consumer width wins.
886
882
  {...(formFromHook && !grouped ? { alignSelf: "flex-start" as const } : undefined)}
@@ -897,7 +893,7 @@ export function Button({
897
893
  {...passthroughSizeProps}
898
894
  {...sizeRecipeEscapeOverride}
899
895
  {...(nested ? nestedFloor : isCircular ? circularPressFloor : pageSlop)}
900
- {...(selectedFill && !isDisabled
896
+ {...(selectedFill && !unavailable
901
897
  ? {
902
898
  backgroundColor: restFill,
903
899
  hoverStyle: {
@@ -955,7 +951,7 @@ export function Button({
955
951
  {...(selectedFill ? { "aria-pressed": true, "data-mp-selected": "true" } : undefined)}
956
952
  >
957
953
  {iconElement}
958
- {textContent}
954
+ {labelContent}
959
955
  {iconAfterElement}
960
956
  </ButtonFrame>
961
957
  );
@@ -11,10 +11,10 @@ export type { SpinnerProps, SpinnerSize } from "./spinnerSize";
11
11
  const SPIN_MS = 700;
12
12
 
13
13
  /** Native Spinner. Size follows the shared size-token scale; spin gates on transition. */
14
- export function Spinner({ size, color, ...props }: SpinnerProps) {
14
+ export function Spinner({ size, px: pxProp, color, ...props }: SpinnerProps) {
15
15
  const { knobProps } = useResolvedKnobs();
16
16
  const token = spinnerSizeToken(size, knobProps.sizeToken);
17
- const px = spinnerSizePx(token);
17
+ const px = pxProp ?? spinnerSizePx(token);
18
18
  const stroke = useSpinnerStroke(color);
19
19
  const [reduceMotion, setReduceMotion] = useState(false);
20
20
  useEffect(() => {
@@ -26,10 +26,10 @@ function ensureMotionStyles() {
26
26
  }
27
27
 
28
28
  /** Form-styled Spinner. Size follows the shared size-token scale; spin gates on transition. */
29
- export function Spinner({ size, color, ...props }: SpinnerProps) {
29
+ export function Spinner({ size, px: pxProp, color, ...props }: SpinnerProps) {
30
30
  const { knobProps } = useResolvedKnobs();
31
31
  const token = spinnerSizeToken(size, knobProps.sizeToken);
32
- const px = spinnerSizePx(token);
32
+ const px = pxProp ?? spinnerSizePx(token);
33
33
  const stroke = useSpinnerStroke(color);
34
34
  const animationsOff = !knobProps.motion.control.transition;
35
35
  ensureMotionStyles();
@@ -6,6 +6,8 @@ export type SpinnerSize = "small" | "large";
6
6
 
7
7
  export interface SpinnerProps {
8
8
  size?: SpinnerSize;
9
+ /** Glyph box in px, over `size`: a host slot that already has a size (Button's icon slot). */
10
+ px?: number;
9
11
  color?: string;
10
12
  [key: string]: unknown;
11
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Button/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Button/index.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { type SpinnerProps } from "./spinnerSize";
2
2
  export type { SpinnerProps, SpinnerSize } from "./spinnerSize";
3
3
  /** Form-styled Spinner. Size follows the shared size-token scale; spin gates on transition. */
4
- export declare function Spinner({ size, color, ...props }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
4
+ export declare function Spinner({ size, px: pxProp, color, ...props }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Spinner/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAEnF,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAqB/D,+FAA+F;AAC/F,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,2CA4B9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Spinner/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAEnF,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAqB/D,+FAA+F;AAC/F,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,2CA4B1E"}
@@ -1,5 +1,5 @@
1
1
  import { type SpinnerProps } from "./spinnerSize";
2
2
  export type { SpinnerProps, SpinnerSize } from "./spinnerSize";
3
3
  /** Native Spinner. Size follows the shared size-token scale; spin gates on transition. */
4
- export declare function Spinner({ size, color, ...props }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
4
+ export declare function Spinner({ size, px: pxProp, color, ...props }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
5
5
  //# sourceMappingURL=index.native.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.native.d.ts","sourceRoot":"","sources":["../../src/Spinner/index.native.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAEnF,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAI/D,0FAA0F;AAC1F,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,2CA+D9D"}
1
+ {"version":3,"file":"index.native.d.ts","sourceRoot":"","sources":["../../src/Spinner/index.native.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAEnF,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAI/D,0FAA0F;AAC1F,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,2CA+D1E"}
@@ -2,6 +2,8 @@ import type { SizeTokens } from "tamagui";
2
2
  export type SpinnerSize = "small" | "large";
3
3
  export interface SpinnerProps {
4
4
  size?: SpinnerSize;
5
+ /** Glyph box in px, over `size`: a host slot that already has a size (Button's icon slot). */
6
+ px?: number;
5
7
  color?: string;
6
8
  [key: string]: unknown;
7
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"spinnerSize.d.ts","sourceRoot":"","sources":["../../src/Spinner/spinnerSize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAI7F;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD"}
1
+ {"version":3,"file":"spinnerSize.d.ts","sourceRoot":"","sources":["../../src/Spinner/spinnerSize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,8FAA8F;IAC9F,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAI7F;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD"}