@jobber/components-native 0.94.0 → 0.95.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.
@@ -1,3 +1,3 @@
1
1
  import React from "react";
2
2
  import type { ProgressBarProps } from "./types";
3
- export declare function ProgressBar({ loading, total, current, inProgress, reverseTheme, header, variation, size, }: ProgressBarProps): React.JSX.Element;
3
+ export declare function ProgressBar({ loading, total, current, inProgress, reverseTheme, header, variation, size, UNSAFE_style, }: ProgressBarProps): React.JSX.Element;
@@ -1,9 +1,12 @@
1
1
  import React from "react";
2
+ import type { StyleProp, ViewStyle } from "react-native";
2
3
  interface ProgressBarInnerProps {
3
4
  readonly width: number;
4
5
  readonly animationDuration?: number;
5
6
  readonly color?: string;
7
+ readonly style?: StyleProp<ViewStyle>;
8
+ readonly testID?: string;
6
9
  }
7
- export declare function ProgressBarInner({ width, color, }: ProgressBarInnerProps): React.JSX.Element;
10
+ export declare function ProgressBarInner({ width, color, style, testID, }: ProgressBarInnerProps): React.JSX.Element;
8
11
  export declare function calculateWidth(total: number, current: number): number;
9
12
  export {};
@@ -1,10 +1,12 @@
1
1
  import React from "react";
2
+ import type { ProgressBarUnsafeStyle } from "./types";
2
3
  interface ProgressBarSteppedProps {
3
4
  readonly total: number;
4
5
  readonly current: number;
5
6
  readonly color?: string;
6
7
  readonly loading?: boolean;
7
8
  readonly inProgress?: number;
9
+ readonly UNSAFE_style?: ProgressBarUnsafeStyle;
8
10
  }
9
- export declare function ProgressBarStepped({ total, current, color, loading, inProgress, }: ProgressBarSteppedProps): React.JSX.Element;
11
+ export declare function ProgressBarStepped({ total, current, color, loading, inProgress, UNSAFE_style, }: ProgressBarSteppedProps): React.JSX.Element;
10
12
  export {};
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
+ import type { StyleProp, ViewStyle } from "react-native";
2
3
  export interface ProgressBarProps {
3
4
  /**
4
5
  * The total number of items to be completed
@@ -36,4 +37,23 @@ export interface ProgressBarProps {
36
37
  * @default base
37
38
  */
38
39
  readonly size?: "smaller" | "small" | "base";
40
+ /** **Use at your own risk:** Custom style for specific elements. This should only be used as a
41
+ * **last resort**. Using this may result in unexpected side effects.
42
+ * More information in the [Customizing components Guide](https://atlantis.getjobber.com/guides/customizing-components).
43
+ */
44
+ readonly UNSAFE_style?: ProgressBarUnsafeStyle;
45
+ }
46
+ export interface ProgressBarUnsafeStyle {
47
+ /** Styles applied to the outer accessible wrapper (role="progressbar"). */
48
+ container?: StyleProp<ViewStyle>;
49
+ /** Styles applied to the inner bar container (track container) – controls bar height/rounding. */
50
+ progressBarContainer?: StyleProp<ViewStyle>;
51
+ /** Styles applied to each step block in the stepped variation. */
52
+ step?: StyleProp<ViewStyle>;
53
+ /** Track/background bar in 'progress' variation (full-width inner) */
54
+ track?: StyleProp<ViewStyle>;
55
+ /** Filled/completed portion in 'progress' variation */
56
+ fill?: StyleProp<ViewStyle>;
57
+ /** In-progress overlay portion in 'progress' variation */
58
+ inProgressFill?: StyleProp<ViewStyle>;
39
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.94.0",
3
+ "version": "0.95.0",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -96,5 +96,5 @@
96
96
  "react-native-safe-area-context": "^5.4.0",
97
97
  "react-native-svg": ">=12.0.0"
98
98
  },
99
- "gitHead": "c781bba08e241b32046afba42dff63d213e4a7fd"
99
+ "gitHead": "ea9cbb709f05442a469763a4f12a332a59cb6350"
100
100
  }
@@ -65,3 +65,112 @@ describe("with stepped variation", () => {
65
65
  expect(stepElements).toHaveLength(1);
66
66
  });
67
67
  });
68
+
69
+ describe("ProgressBar (native) - UNSAFE_style", () => {
70
+ it("applies UNSAFE_style.container to the outer wrapper", () => {
71
+ const containerStyle = { padding: 12, backgroundColor: "papayawhip" };
72
+ render(
73
+ <ProgressBar
74
+ total={100}
75
+ current={40}
76
+ UNSAFE_style={{ container: containerStyle }}
77
+ />,
78
+ );
79
+
80
+ const progressBar = screen.getByRole("progressbar");
81
+
82
+ expect(progressBar.props.style).toEqual(
83
+ expect.objectContaining(containerStyle),
84
+ );
85
+ });
86
+
87
+ it("applies UNSAFE_style.progressBarContainer and track in progress variation", () => {
88
+ const progressBarContainer = { height: 22, borderRadius: 10 };
89
+ const track = { backgroundColor: "#eee" };
90
+
91
+ render(
92
+ <ProgressBar
93
+ total={100}
94
+ current={30}
95
+ UNSAFE_style={{
96
+ progressBarContainer,
97
+ track,
98
+ }}
99
+ />,
100
+ );
101
+
102
+ const progressContainer = screen.getByTestId("progressbar-container");
103
+
104
+ expect(progressContainer.props.style).toEqual(
105
+ expect.arrayContaining([expect.objectContaining(progressBarContainer)]),
106
+ );
107
+
108
+ const trackView = screen.getByTestId("progressbar-track");
109
+ expect(trackView.props.style).toEqual(
110
+ expect.arrayContaining([expect.objectContaining(track)]),
111
+ );
112
+ });
113
+
114
+ it("applies UNSAFE_style.inProgressFill and fill in progress variation", () => {
115
+ const inProgressFill = { backgroundColor: "#f59e0b" };
116
+ const fill = { backgroundColor: "#10b981" };
117
+
118
+ render(
119
+ <ProgressBar
120
+ total={100}
121
+ current={30}
122
+ inProgress={10}
123
+ UNSAFE_style={{
124
+ inProgressFill,
125
+ fill,
126
+ }}
127
+ />,
128
+ );
129
+
130
+ const inProgressView = screen.getByTestId("progressbar-inprogress");
131
+ const fillView = screen.getByTestId("progressbar-fill");
132
+
133
+ expect(inProgressView.props.style).toEqual(
134
+ expect.arrayContaining([expect.objectContaining(inProgressFill)]),
135
+ );
136
+ expect(fillView.props.style).toEqual(
137
+ expect.arrayContaining([expect.objectContaining(fill)]),
138
+ );
139
+ });
140
+
141
+ it("applies UNSAFE_style.progressBarContainer and step in stepped variation", () => {
142
+ const progressBarContainer = { height: 12 };
143
+ const step = { backgroundColor: "#d1d5db" };
144
+
145
+ render(
146
+ <ProgressBar
147
+ variation="stepped"
148
+ total={4}
149
+ current={2}
150
+ inProgress={1}
151
+ UNSAFE_style={{ progressBarContainer, step }}
152
+ />,
153
+ );
154
+
155
+ const steppedContainer = screen.getByTestId("progressbar-container");
156
+
157
+ expect(steppedContainer.props.style).toEqual(
158
+ expect.arrayContaining([expect.objectContaining(progressBarContainer)]),
159
+ );
160
+
161
+ // Steps are rendered with testIDs depending on state; collect all steps by known IDs
162
+ const stepNodes = [
163
+ ...screen.getAllByTestId("progress-step-completed"),
164
+ ...screen.getAllByTestId("progress-step-in-progress"),
165
+ ...screen.getAllByTestId("progress-step-incomplete"),
166
+ ];
167
+
168
+ // At least one step should exist and contain the custom style
169
+ expect(stepNodes.length).toBeGreaterThan(0);
170
+ stepNodes.forEach(node => {
171
+ expect(node.props.style).toEqual(
172
+ expect.arrayContaining([expect.objectContaining(step)]),
173
+ );
174
+ });
175
+ });
176
+ });
@@ -17,6 +17,7 @@ export function ProgressBar({
17
17
  header,
18
18
  variation = "progress",
19
19
  size = "base",
20
+ UNSAFE_style,
20
21
  }: ProgressBarProps) {
21
22
  const { t } = useAtlantisI18n();
22
23
  const styles = useStyles();
@@ -27,6 +28,7 @@ export function ProgressBar({
27
28
  accessible
28
29
  accessibilityRole="progressbar"
29
30
  accessibilityLabel={getA11yLabel()}
31
+ style={UNSAFE_style?.container}
30
32
  >
31
33
  {header}
32
34
  {variation === "stepped" ? (
@@ -38,31 +40,45 @@ export function ProgressBar({
38
40
  }
39
41
  loading={loading}
40
42
  inProgress={inProgress}
43
+ UNSAFE_style={UNSAFE_style}
41
44
  />
42
45
  ) : (
43
- <View style={[styles.progressBarContainer, sizeStyles[size]]}>
46
+ <View
47
+ testID="progressbar-container"
48
+ style={[
49
+ styles.progressBarContainer,
50
+ sizeStyles[size],
51
+ UNSAFE_style?.progressBarContainer,
52
+ ]}
53
+ >
44
54
  <ProgressBarInner
55
+ testID="progressbar-track"
45
56
  width={100}
46
57
  animationDuration={0}
47
58
  color={
48
59
  reverseTheme ? undefined : tokens["color-interactive--background"]
49
60
  }
61
+ style={UNSAFE_style?.track}
50
62
  />
51
63
  {!loading && (
52
64
  <>
53
65
  {inProgress && inProgress > 0 ? (
54
66
  <ProgressBarInner
67
+ testID="progressbar-inprogress"
55
68
  width={calculateWidth(total, current + inProgress)}
56
69
  color={tokens["color-informative"]}
57
70
  animationDuration={800}
71
+ style={UNSAFE_style?.inProgressFill}
58
72
  />
59
73
  ) : (
60
74
  <></>
61
75
  )}
62
76
  <ProgressBarInner
77
+ testID="progressbar-fill"
63
78
  width={calculateWidth(total, current)}
64
79
  color={tokens["color-interactive"]}
65
80
  animationDuration={600}
81
+ style={UNSAFE_style?.fill}
66
82
  />
67
83
  </>
68
84
  )}
@@ -1,37 +1,34 @@
1
1
  import React from "react";
2
2
  import { View } from "react-native";
3
- // import Reanimated from "react-native-reanimated";
4
- // import { useTiming } from "utils/reanimated";
3
+ import type { StyleProp, ViewStyle } from "react-native";
5
4
  import { useStyles } from "./ProgressBar.style";
6
5
 
7
6
  interface ProgressBarInnerProps {
8
7
  readonly width: number;
9
8
  readonly animationDuration?: number;
10
9
  readonly color?: string;
10
+ readonly style?: StyleProp<ViewStyle>;
11
+ readonly testID?: string;
11
12
  }
12
13
 
13
14
  export function ProgressBarInner({
14
15
  width,
15
- // animationDuration = 0,
16
16
  color,
17
+ style,
18
+ testID,
17
19
  }: ProgressBarInnerProps) {
18
- // Animation breaking on Android
19
- // const [animatedOpacity] = useTiming({
20
- // duration: animationDuration,
21
- // fromValue: 0,
22
- // toValue: 1,
23
- // });
24
20
  const styles = useStyles();
25
21
 
26
22
  return (
27
23
  <View
24
+ testID={testID}
28
25
  style={[
29
26
  styles.progressBarInner,
30
27
  {
31
28
  width: `${width}%`,
32
- // opacity: animatedOpacity,
33
29
  ...(color && { backgroundColor: color }),
34
30
  },
31
+ style,
35
32
  ]}
36
33
  />
37
34
  );
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { View } from "react-native";
3
3
  import { useStyles } from "./ProgressBar.style";
4
+ import type { ProgressBarUnsafeStyle } from "./types";
4
5
 
5
6
  interface ProgressBarSteppedProps {
6
7
  readonly total: number;
@@ -8,6 +9,7 @@ interface ProgressBarSteppedProps {
8
9
  readonly color?: string;
9
10
  readonly loading?: boolean;
10
11
  readonly inProgress?: number;
12
+ readonly UNSAFE_style?: ProgressBarUnsafeStyle;
11
13
  }
12
14
 
13
15
  export function ProgressBarStepped({
@@ -16,11 +18,19 @@ export function ProgressBarStepped({
16
18
  color,
17
19
  loading,
18
20
  inProgress,
21
+ UNSAFE_style,
19
22
  }: ProgressBarSteppedProps) {
20
23
  const styles = useStyles();
21
24
 
22
25
  return (
23
- <View style={[styles.progressBarContainer, { height: 10 }]}>
26
+ <View
27
+ testID="progressbar-container"
28
+ style={[
29
+ styles.progressBarContainer,
30
+ { height: 10 },
31
+ UNSAFE_style?.progressBarContainer,
32
+ ]}
33
+ >
24
34
  {Array.from({ length: total }).map((_, index) => {
25
35
  const step = index + 1;
26
36
  const isCompleted = step <= current;
@@ -39,6 +49,7 @@ export function ProgressBarStepped({
39
49
  step <= inProgressSteps &&
40
50
  styles.inProgressStep,
41
51
  lastStep && { marginRight: 0 },
52
+ UNSAFE_style?.step,
42
53
  ]}
43
54
  testID={
44
55
  isCompleted
@@ -18,8 +18,10 @@ exports[`renders blue inProgress bar when 1 or more jobs is in progress 1`] = `
18
18
  {
19
19
  "height": 16,
20
20
  },
21
+ undefined,
21
22
  ]
22
23
  }
24
+ testID="progressbar-container"
23
25
  >
24
26
  <View
25
27
  style={
@@ -37,8 +39,10 @@ exports[`renders blue inProgress bar when 1 or more jobs is in progress 1`] = `
37
39
  "backgroundColor": "hsl(51, 17%, 85%)",
38
40
  "width": "100%",
39
41
  },
42
+ undefined,
40
43
  ]
41
44
  }
45
+ testID="progressbar-track"
42
46
  />
43
47
  <View
44
48
  style={
@@ -56,8 +60,10 @@ exports[`renders blue inProgress bar when 1 or more jobs is in progress 1`] = `
56
60
  "backgroundColor": "hsl(207, 79%, 57%)",
57
61
  "width": "0%",
58
62
  },
63
+ undefined,
59
64
  ]
60
65
  }
66
+ testID="progressbar-inprogress"
61
67
  />
62
68
  <View
63
69
  style={
@@ -75,8 +81,10 @@ exports[`renders blue inProgress bar when 1 or more jobs is in progress 1`] = `
75
81
  "backgroundColor": "hsl(107, 58%, 33%)",
76
82
  "width": "0%",
77
83
  },
84
+ undefined,
78
85
  ]
79
86
  }
87
+ testID="progressbar-fill"
80
88
  />
81
89
  </View>
82
90
  </View>
@@ -100,8 +108,10 @@ exports[`renders green CompletedProgress bar when 1 or more jobs is completed 1`
100
108
  {
101
109
  "height": 16,
102
110
  },
111
+ undefined,
103
112
  ]
104
113
  }
114
+ testID="progressbar-container"
105
115
  >
106
116
  <View
107
117
  style={
@@ -119,8 +129,10 @@ exports[`renders green CompletedProgress bar when 1 or more jobs is completed 1`
119
129
  "backgroundColor": "hsl(51, 17%, 85%)",
120
130
  "width": "100%",
121
131
  },
132
+ undefined,
122
133
  ]
123
134
  }
135
+ testID="progressbar-track"
124
136
  />
125
137
  <View
126
138
  style={
@@ -138,8 +150,10 @@ exports[`renders green CompletedProgress bar when 1 or more jobs is completed 1`
138
150
  "backgroundColor": "hsl(107, 58%, 33%)",
139
151
  "width": "50%",
140
152
  },
153
+ undefined,
141
154
  ]
142
155
  }
156
+ testID="progressbar-fill"
143
157
  />
144
158
  </View>
145
159
  </View>
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
+ import type { StyleProp, ViewStyle } from "react-native";
2
3
 
3
4
  export interface ProgressBarProps {
4
5
  /**
@@ -44,4 +45,25 @@ export interface ProgressBarProps {
44
45
  * @default base
45
46
  */
46
47
  readonly size?: "smaller" | "small" | "base";
48
+
49
+ /** **Use at your own risk:** Custom style for specific elements. This should only be used as a
50
+ * **last resort**. Using this may result in unexpected side effects.
51
+ * More information in the [Customizing components Guide](https://atlantis.getjobber.com/guides/customizing-components).
52
+ */
53
+ readonly UNSAFE_style?: ProgressBarUnsafeStyle;
54
+ }
55
+
56
+ export interface ProgressBarUnsafeStyle {
57
+ /** Styles applied to the outer accessible wrapper (role="progressbar"). */
58
+ container?: StyleProp<ViewStyle>;
59
+ /** Styles applied to the inner bar container (track container) – controls bar height/rounding. */
60
+ progressBarContainer?: StyleProp<ViewStyle>;
61
+ /** Styles applied to each step block in the stepped variation. */
62
+ step?: StyleProp<ViewStyle>;
63
+ /** Track/background bar in 'progress' variation (full-width inner) */
64
+ track?: StyleProp<ViewStyle>;
65
+ /** Filled/completed portion in 'progress' variation */
66
+ fill?: StyleProp<ViewStyle>;
67
+ /** In-progress overlay portion in 'progress' variation */
68
+ inProgressFill?: StyleProp<ViewStyle>;
47
69
  }