@jobber/components-native 0.114.17-JOB-90457--9c066de.4 → 0.115.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.
@@ -58,13 +58,10 @@ The indicator does not participate in the keyboard tab order.
58
58
 
59
59
  ### Reduced motion
60
60
 
61
- The width transition between `value` updates is intentionally **preserved**
62
- under
61
+ The width transition between `value` updates is disabled under
63
62
  [`prefers-reduced-motion: reduce`](https://developer.mozilla.org/docs/Web/CSS/@media/prefers-reduced-motion).
64
- The slide between progress values conveys functional information about progress,
65
- direction, and magnitude — it is exempt under
66
- [WCAG 2.3.3 ("Animation from Interactions")](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions.html).
67
- Removing the transition would make progress harder to perceive, not easier.
63
+ The fill updates immediately to the new value, so current progress remains
64
+ visible without interpolation. The stepped variation does not animate.
68
65
 
69
66
  ## Relationship to ProgressBar
70
67
 
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.114.17-JOB-90457--9c066de.4+9c066de0",
3
+ "version": "0.115.1",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -123,5 +123,5 @@
123
123
  "react-native-screens": ">=4.18.0",
124
124
  "react-native-svg": ">=12.0.0"
125
125
  },
126
- "gitHead": "9c066de0ed5e3a4a021d14ff31ba5f57ce0813a0"
126
+ "gitHead": "df5ca5cd691c56be40fad67fae7ea644724656cc"
127
127
  }
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
- import { render, screen, within } from "@testing-library/react-native";
2
+ import { fireEvent, render, screen, within, } from "@testing-library/react-native";
3
+ import { Pressable } from "react-native";
3
4
  import { SelectRoot } from "./SelectRoot";
4
5
  import { SelectTrigger } from "./components/SelectTrigger";
5
6
  import { SelectLabel } from "./components/SelectLabel";
@@ -12,6 +13,34 @@ function renderRoot(props = {}) {
12
13
  React.createElement(SelectPrimitive.Value, { placeholder: "Select an option" }))));
13
14
  }
14
15
  describe("SelectRoot chrome", () => {
16
+ it("shows the placeholder when a controlled value is cleared to undefined", () => {
17
+ function ControlledSelect() {
18
+ const [value, setValue] = React.useState();
19
+ return (React.createElement(React.Fragment, null,
20
+ React.createElement(SelectRoot, { value: value, onValueChange: setValue },
21
+ React.createElement(SelectTrigger, null,
22
+ React.createElement(SelectPrimitive.Value, { placeholder: "Select an option" })),
23
+ React.createElement(SelectPrimitive.Item, { testID: "toronto-option", value: "tor", label: "Toronto" })),
24
+ React.createElement(Pressable, { testID: "clear-value", onPress: () => setValue(undefined) })));
25
+ }
26
+ render(React.createElement(ControlledSelect, null));
27
+ fireEvent.press(screen.getByTestId("toronto-option"));
28
+ expect(screen.getByText("Toronto")).toBeDefined();
29
+ fireEvent.press(screen.getByTestId("clear-value"));
30
+ expect(screen.queryByText("Toronto")).toBeNull();
31
+ expect(screen.getByText("Select an option")).toHaveStyle({
32
+ color: tokens["color-text--secondary"],
33
+ });
34
+ });
35
+ it("preserves an uncontrolled default value across rerenders", () => {
36
+ const { rerender } = renderRoot({
37
+ defaultValue: { value: "tor", label: "Toronto" },
38
+ });
39
+ rerender(React.createElement(SelectRoot, { defaultValue: { value: "tor", label: "Toronto" } },
40
+ React.createElement(SelectTrigger, null,
41
+ React.createElement(SelectPrimitive.Value, { placeholder: "Select an option" }))));
42
+ expect(screen.getByText("Toronto")).toBeDefined();
43
+ });
15
44
  it("renders a composed label (visible, hidden from assistive tech)", () => {
16
45
  render(React.createElement(SelectRoot, null,
17
46
  React.createElement(SelectLabel, null, "City"),
@@ -21,9 +21,16 @@ export const SELECT_TRIGGER_DEFAULT_TEST_ID = "ATL-Select-Trigger";
21
21
  function useIsDarkTheme() {
22
22
  return useAtlantisTheme().effectiveTheme === "dark";
23
23
  }
24
- function SelectPrimitiveRoot(_a) {
25
- var props = __rest(_a, []);
26
- return React.createElement(Primitive.Root, Object.assign({}, props));
24
+ // @rn-primitives/select treats `undefined` as uncontrolled. Keep an explicitly
25
+ // controlled empty value defined so clearing the prop cannot reveal stale
26
+ // internal state. Its undefined fields cannot match a public string item value.
27
+ const EMPTY_CONTROLLED_VALUE = {};
28
+ function SelectPrimitiveRoot(props) {
29
+ const isControlled = Object.prototype.hasOwnProperty.call(props, "value");
30
+ const value = isControlled && props.value === undefined
31
+ ? EMPTY_CONTROLLED_VALUE
32
+ : props.value;
33
+ return React.createElement(Primitive.Root, Object.assign({}, props, { value: value }));
27
34
  }
28
35
  /**
29
36
  * Styled trigger field. Renders only its `children` — compose
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import React from "react";
11
11
  import { render, screen, userEvent, within, } from "@testing-library/react-native";
12
+ import { Pressable } from "react-native";
12
13
  import { darkTokens } from "@jobber/design";
13
14
  import { PortalHost } from "@rn-primitives/portal";
14
15
  import { SelectPrimitive } from "./SelectPrimitive";
@@ -30,6 +31,35 @@ function renderSelect({ defaultValue, onOpenChange, disabled, invalid, testID, t
30
31
  React.createElement(SelectPrimitive.Trigger, { ref: triggerRef, testID: testID, invalid: invalid },
31
32
  React.createElement(SelectPrimitive.Value, { placeholder: "Select an option" }))));
32
33
  }
34
+ describe("SelectPrimitive.Root", () => {
35
+ it("does not select an empty-string item when a controlled value is cleared", () => __awaiter(void 0, void 0, void 0, function* () {
36
+ function ControlledSelect() {
37
+ const [value, setValue] = React.useState({ value: "tor", label: "Toronto" });
38
+ return (React.createElement(React.Fragment, null,
39
+ React.createElement(SelectPrimitive.Root, { value: value, onValueChange: setValue },
40
+ React.createElement(SelectPrimitive.Trigger, null,
41
+ React.createElement(SelectPrimitive.Value, { placeholder: "Select an option" })),
42
+ React.createElement(SelectPrimitive.Portal, null,
43
+ React.createElement(SelectPrimitive.Overlay, null),
44
+ React.createElement(SelectPrimitive.Content, null,
45
+ React.createElement(SelectPrimitive.Viewport, null,
46
+ React.createElement(SelectPrimitive.Item, { testID: "empty-option", value: "", label: "Empty" },
47
+ React.createElement(SelectPrimitive.ItemText, null),
48
+ React.createElement(SelectPrimitive.ItemIndicator, null,
49
+ React.createElement(Icon, { name: "checkmark" }))),
50
+ React.createElement(SelectPrimitive.Item, { value: "tor", label: "Toronto" },
51
+ React.createElement(SelectPrimitive.ItemText, null),
52
+ React.createElement(SelectPrimitive.ItemIndicator, null,
53
+ React.createElement(Icon, { name: "checkmark" }))))))),
54
+ React.createElement(Pressable, { testID: "clear-value", onPress: () => setValue(undefined) })));
55
+ }
56
+ render(React.createElement(ControlledSelect, null), { wrapper: PortalHostWrapper });
57
+ yield user.press(screen.getByTestId("clear-value"));
58
+ yield openSelect();
59
+ expect(screen.getByTestId("empty-option").props.accessibilityState).toMatchObject({ checked: false });
60
+ expect(screen.queryByTestId("checkmark")).toBeNull();
61
+ }));
62
+ });
33
63
  describe("SelectPrimitive.Trigger", () => {
34
64
  it("renders with the default testID", () => {
35
65
  renderSelect();