@jobber/components-native 0.111.1 → 0.112.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.111.1",
3
+ "version": "0.112.0",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -73,7 +73,7 @@
73
73
  "devDependencies": {
74
74
  "@babel/runtime": "^7.29.2",
75
75
  "@gorhom/bottom-sheet": "^5.2.8",
76
- "@jobber/design": "0.110.0",
76
+ "@jobber/design": "0.111.0",
77
77
  "@jobber/hooks": "2.21.0",
78
78
  "@react-native-community/datetimepicker": "^8.4.5",
79
79
  "@react-native/babel-preset": "^0.82.1",
@@ -124,5 +124,5 @@
124
124
  "react-native-screens": ">=4.18.0",
125
125
  "react-native-svg": ">=12.0.0"
126
126
  },
127
- "gitHead": "a9b32a282f2f70d07b99600c8ce8dfdd2d6e3db9"
127
+ "gitHead": "71ed27026cd539889c5ce6c627bd74a8d283112e"
128
128
  }
@@ -161,7 +161,7 @@ function Footer({
161
161
  <Divider />
162
162
  </View>
163
163
 
164
- <BottomSheetOption text={cancelLabel} icon="remove" onPress={close} />
164
+ <BottomSheetOption text={cancelLabel} icon="cross" onPress={close} />
165
165
  </View>
166
166
  );
167
167
  }
@@ -71,7 +71,7 @@ it("renders a Chip with the dismiss icon", () => {
71
71
  const { getByTestId } = render(
72
72
  <Chip label="Foo" onPress={jest.fn()} isActive isDismissible={true} />,
73
73
  );
74
- expect(getByTestId("remove")).toBeDefined();
74
+ expect(getByTestId("cross")).toBeDefined();
75
75
  });
76
76
 
77
77
  it("should call the handler with the new value", () => {
package/src/Chip/Chip.tsx CHANGED
@@ -141,7 +141,7 @@ export function Chip({
141
141
  )}
142
142
  {isDismissible && (
143
143
  <View style={[styles.dismissIcon, dismissColor]}>
144
- <Icon name={"remove"} size={"small"} />
144
+ <Icon name={"cross"} size={"small"} />
145
145
  </View>
146
146
  )}
147
147
  </Pressable>
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import type { ComponentProps } from "react";
3
+ import { View } from "react-native";
3
4
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
4
5
  import { Glimmer } from "@jobber/components-native";
5
6
  import { GlimmerBasic, GlimmerInDepth } from "./docs";
@@ -14,10 +15,9 @@ const meta = {
14
15
  decorators: [
15
16
  // Take out Storybook's wrapper layout to preserve native glimmer shapes.
16
17
  (Story: React.ComponentType) => (
17
- // eslint-disable-next-line react/forbid-elements
18
- <div style={{ display: "block" }}>
18
+ <View>
19
19
  <Story />
20
- </div>
20
+ </View>
21
21
  ),
22
22
  ],
23
23
  } satisfies Meta<typeof Glimmer>;
@@ -56,6 +56,30 @@ it("renders an empty svg without throwing for an unknown icon name", () => {
56
56
  expect(svg.UNSAFE_root.findAllByType(Path)).toHaveLength(0);
57
57
  });
58
58
 
59
+ it("renders the multi-color truck icon with its static fills", () => {
60
+ const svg = render(<Icon name="truck" />);
61
+ const fills = svg.UNSAFE_root.findAllByType(Path).map(
62
+ path => path.props.fill as string,
63
+ );
64
+
65
+ expect(fills).toHaveLength(5);
66
+ expect(fills).toContain("#2B2B2B");
67
+ expect(fills.filter(fill => fill.startsWith("#"))).toHaveLength(4);
68
+ });
69
+
70
+ it("applies customColor to the truck body path but not its static artwork", () => {
71
+ const svg = render(<Icon name="truck" customColor="#abc123" />);
72
+ const fills = svg.UNSAFE_root.findAllByType(Path).map(
73
+ path => path.props.fill as string,
74
+ );
75
+
76
+ expect(fills).toHaveLength(5);
77
+ expect(fills.filter(fill => fill === "#abc123")).toHaveLength(1);
78
+ expect(fills).toEqual(
79
+ expect.arrayContaining(["#2B2B2B", "#000000", "#FFFFFF", "#2A2A2A"]),
80
+ );
81
+ });
82
+
59
83
  describe("theme-aware fill", () => {
60
84
  function getPathFills(svg: ReturnType<typeof render>) {
61
85
  return svg.UNSAFE_root.findAllByType(Path).map(p => p.props.fill as string);
package/src/Icon/Icon.tsx CHANGED
@@ -43,16 +43,25 @@ export function Icon({
43
43
  const { svgStyle, paths, viewBox } = getIcon({
44
44
  name,
45
45
  color,
46
+ customColor,
46
47
  size,
47
48
  platform: "mobile",
48
49
  format: "js",
49
50
  tokens,
50
51
  });
51
52
 
53
+ // Paths with their own fill are static artwork; only unfilled paths take
54
+ // the themed color and customColor.
52
55
  // getIcon returns undefined paths for an unknown icon name; render nothing rather than crash.
53
- const icon = (paths ?? []).map((path: string) => {
54
- return <Path key={path} d={path} fill={customColor || svgStyle.fill} />;
55
- });
56
+ const icon = (paths ?? []).map(path => (
57
+ <Path
58
+ key={path.d}
59
+ d={path.d}
60
+ fill={path.fill ?? (customColor || svgStyle.fill)}
61
+ fillOpacity={path.fillOpacity}
62
+ opacity={path.opacity}
63
+ />
64
+ ));
56
65
 
57
66
  return (
58
67
  <Svg
@@ -417,11 +417,6 @@ exports[`renders thumbsDown icon 1`] = `
417
417
  "display": "flex",
418
418
  "fill": "hsl(198, 35%, 21%)",
419
419
  "height": 24,
420
- "transform": [
421
- {
422
- "scaleY": -1,
423
- },
424
- ],
425
420
  "verticalAlign": "middle",
426
421
  "width": 24,
427
422
  },
@@ -433,13 +428,6 @@ exports[`renders thumbsDown icon 1`] = `
433
428
  ]
434
429
  }
435
430
  testID="thumbsDown"
436
- transform={
437
- [
438
- {
439
- "scaleY": -1,
440
- },
441
- ]
442
- }
443
431
  vbHeight={24}
444
432
  vbWidth={24}
445
433
  >
@@ -457,7 +445,7 @@ exports[`renders thumbsDown icon 1`] = `
457
445
  }
458
446
  >
459
447
  <RNSVGPath
460
- d="M8 11.78V19h8l3-5.76v-1.29h-7.639l1.036-7.944L8 11.78ZM6.134 11l4.555-8.034c1.124-1.846 3.971-.844 3.691 1.299l-.74 5.685h6.86a.5.5 0 0 1 .5.5v3a1 1 0 0 1-.084.4l-3.276 6.648a.95.95 0 0 1-.44.453c-.132.065-.276.049-.423.049H4c-1.105 0-2-1.045-2-2.15V13a2 2 0 0 1 2-2h2.134ZM6 13H4v6h2v-6Z"
448
+ d="M8 12.22V5h8l3 5.76v1.29h-7.639l1.036 7.944L8 12.22ZM6.134 13l4.555 8.034c1.124 1.846 3.971.844 3.691-1.299l-.74-5.685h6.86a.5.5 0 0 0 .5-.5v-3a1 1 0 0 0-.084-.4l-3.276-6.648a.95.95 0 0 0-.44-.453c-.132-.065-.276-.049-.423-.049H4c-1.105 0-2 1.045-2 2.15V11a2 2 0 0 0 2 2h2.134ZM6 11H4V5h2v6Z"
461
449
  fill={
462
450
  {
463
451
  "payload": 4280499528,
@@ -17,7 +17,7 @@ export const Basic: Story = {
17
17
  render: IconButtonBasic,
18
18
  args: {
19
19
  accessibilityLabel: "New Job",
20
- name: "remove",
20
+ name: "cross",
21
21
  onPress: () => action("alert")("👍"),
22
22
  },
23
23
  };
@@ -9,7 +9,7 @@ export function IconButtonBasic(props: Partial<IconButtonProps>) {
9
9
  return (
10
10
  <IconButton
11
11
  accessibilityLabel="New Job"
12
- name="remove"
12
+ name="cross"
13
13
  onPress={() => action("alert")("👍")}
14
14
  {...props}
15
15
  />
@@ -3,7 +3,8 @@ import { InputDate } from "@jobber/components-native";
3
3
  import type { InputDateProps } from "@jobber/components-native";
4
4
 
5
5
  export function InputDateBasic(props: Partial<InputDateProps>) {
6
- const [date, setDate] = useState(new Date("11/11/2011"));
6
+ // monthIndex 10 = November (Hermes can't parse a "MM/DD/YYYY" string).
7
+ const [date, setDate] = useState(new Date(2011, 10, 11));
7
8
 
8
9
  return <InputDate {...props} value={date} onChange={setDate} />;
9
10
  }
@@ -3,7 +3,8 @@ import { InputDate } from "@jobber/components-native";
3
3
  import type { InputDateProps } from "@jobber/components-native";
4
4
 
5
5
  export function InputDateEmptyValueLabel(props: Partial<InputDateProps>) {
6
- const [date, setDate] = useState(new Date("11/11/2011"));
6
+ // monthIndex 10 = November (Hermes can't parse a "MM/DD/YYYY" string).
7
+ const [date, setDate] = useState(new Date(2011, 10, 11));
7
8
 
8
9
  return (
9
10
  <InputDate
@@ -3,7 +3,8 @@ import { InputDate } from "@jobber/components-native";
3
3
  import type { InputDateProps } from "@jobber/components-native";
4
4
 
5
5
  export function InputDateInvalid(props: Partial<InputDateProps>) {
6
- const [date, setDate] = useState(new Date("11/11/2011"));
6
+ // monthIndex 10 = November (Hermes can't parse a "MM/DD/YYYY" string).
7
+ const [date, setDate] = useState(new Date(2011, 10, 11));
7
8
 
8
9
  return (
9
10
  <InputDate
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { View } from "react-native";
2
3
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
3
4
  import { SafeAreaProvider } from "react-native-safe-area-context";
4
5
  import { ToastBasic } from "./docs";
@@ -17,11 +18,10 @@ const meta = {
17
18
  Story => {
18
19
  return (
19
20
  <SafeAreaProvider>
20
- {/* eslint-disable-next-line react/forbid-elements -- This is a
21
- storybook decorator so it doesn't affect react-native stuff */}
22
- <div style={{ height: "90vh" }}>
21
+ {/* flex: 1 fills the height so the Toast anchors to the bottom (web and native). */}
22
+ <View style={{ flex: 1 }}>
23
23
  <Story />
24
- </div>
24
+ </View>
25
25
  </SafeAreaProvider>
26
26
  );
27
27
  },
@@ -33,7 +33,7 @@ function DefaultToast({ text1 }: ToastConfigParams<string>) {
33
33
  <View style={styles.toastIcon}>
34
34
  <IconButton
35
35
  onPress={Toast.hide}
36
- name="remove"
36
+ name="cross"
37
37
  customColor={tokens["color-greyBlue--light"]}
38
38
  accessibilityLabel={t("dismiss")}
39
39
  />