@jobber/components-native 0.84.4-JOB-138679-b1552ab.68 → 0.84.4-match-mobi-accfa8a.9
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/dist/package.json +5 -3
- package/dist/src/BottomSheet/BottomSheet.js +58 -32
- package/dist/src/BottomSheet/BottomSheet.style.js +8 -9
- package/dist/src/BottomSheet/hooks/useBottomSheetBackHandler.js +26 -0
- package/dist/src/Chip/Chip.js +12 -1
- package/dist/src/Chip/Chip.style.js +1 -1
- package/dist/src/Content/ContentHorizontal.style.js +15 -0
- package/dist/src/Content/ContentSpaceAround.style.js +15 -0
- package/dist/src/Content/ContentVertical.style.js +15 -0
- package/dist/src/InputText/InputText.js +2 -2
- package/dist/src/ProgressBar/ProgressBar.js +10 -6
- package/dist/src/ProgressBar/ProgressBarInner.js +3 -12
- package/dist/src/ProgressBar/ProgressBarStepped.js +7 -2
- package/dist/src/StatusIndicator/StatusIndicator.js +11 -0
- package/dist/src/StatusIndicator/StatusIndicator.style.js +12 -0
- package/dist/src/StatusIndicator/StatusIndicator.type.js +1 -0
- package/dist/src/StatusIndicator/index.js +1 -0
- package/dist/src/StatusLabel/StatusLabel.js +6 -12
- package/dist/src/StatusLabel/StatusLabel.style.js +10 -13
- package/dist/src/hooks/useFormController.js +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/utils/meta/meta.json +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/BottomSheet/BottomSheet.d.ts +7 -3
- package/dist/types/src/BottomSheet/BottomSheet.style.d.ts +7 -14
- package/dist/types/src/BottomSheet/hooks/useBottomSheetBackHandler.d.ts +8 -0
- package/dist/types/src/Content/Content.d.ts +1 -1
- package/dist/types/src/Content/ContentHorizontal.style.d.ts +15 -0
- package/dist/types/src/Content/ContentSpaceAround.style.d.ts +15 -0
- package/dist/types/src/Content/ContentVertical.style.d.ts +15 -0
- package/dist/types/src/Form/context/types.d.ts +2 -2
- package/dist/types/src/Form/hooks/useInternalForm.d.ts +2 -2
- package/dist/types/src/InputText/InputText.d.ts +1 -1
- package/dist/types/src/Menu/types.d.ts +1 -1
- package/dist/types/src/ProgressBar/ProgressBar.d.ts +1 -1
- package/dist/types/src/ProgressBar/ProgressBarInner.d.ts +4 -1
- package/dist/types/src/ProgressBar/ProgressBarStepped.d.ts +3 -1
- package/dist/types/src/ProgressBar/types.d.ts +20 -0
- package/dist/types/src/StatusIndicator/StatusIndicator.d.ts +6 -0
- package/dist/types/src/StatusIndicator/StatusIndicator.style.d.ts +8 -0
- package/dist/types/src/StatusIndicator/StatusIndicator.type.d.ts +1 -0
- package/dist/types/src/StatusIndicator/index.d.ts +2 -0
- package/dist/types/src/StatusLabel/StatusLabel.d.ts +3 -3
- package/dist/types/src/StatusLabel/StatusLabel.style.d.ts +10 -10
- package/dist/types/src/index.d.ts +1 -0
- package/package.json +5 -3
- package/src/BottomSheet/BottomSheet.stories.tsx +128 -0
- package/src/BottomSheet/BottomSheet.style.ts +7 -14
- package/src/BottomSheet/BottomSheet.test.tsx +19 -24
- package/src/BottomSheet/BottomSheet.tsx +112 -93
- package/src/BottomSheet/hooks/useBottomSheetBackHandler.test.ts +90 -0
- package/src/BottomSheet/hooks/useBottomSheetBackHandler.ts +41 -0
- package/src/Checkbox/Checkbox.test.tsx +118 -1
- package/src/Chip/Chip.style.ts +1 -1
- package/src/Chip/Chip.tsx +19 -1
- package/src/Content/Content.tsx +6 -1
- package/src/Content/ContentHorizontal.style.ts +20 -0
- package/src/Content/ContentSpaceAround.style.ts +20 -0
- package/src/Content/ContentVertical.style.ts +20 -0
- package/src/Form/context/types.ts +2 -2
- package/src/Form/hooks/useInternalForm.ts +2 -2
- package/src/InputText/InputText.test.tsx +115 -1
- package/src/InputText/InputText.tsx +3 -3
- package/src/Menu/types.ts +1 -1
- package/src/ProgressBar/ProgressBar.test.tsx +109 -0
- package/src/ProgressBar/ProgressBar.tsx +17 -1
- package/src/ProgressBar/ProgressBarInner.tsx +7 -10
- package/src/ProgressBar/ProgressBarStepped.tsx +12 -1
- package/src/ProgressBar/__snapshots__/ProgressBar.test.tsx.snap +14 -0
- package/src/ProgressBar/types.ts +22 -0
- package/src/StatusIndicator/StatusIndicator.style.ts +14 -0
- package/src/StatusIndicator/StatusIndicator.test.tsx +42 -0
- package/src/StatusIndicator/StatusIndicator.tsx +23 -0
- package/src/StatusIndicator/StatusIndicator.type.ts +6 -0
- package/src/StatusIndicator/__snapshots__/StatusIndicator.test.tsx.snap +96 -0
- package/src/StatusIndicator/index.ts +2 -0
- package/src/StatusLabel/StatusLabel.style.ts +10 -16
- package/src/StatusLabel/StatusLabel.tsx +15 -28
- package/src/StatusLabel/__snapshots__/StatusLabel.test.tsx.snap +133 -105
- package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +199 -1
- package/src/hooks/useFormController.ts +1 -1
- package/src/index.ts +1 -0
- package/src/utils/meta/meta.json +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RefObject } from "react";
|
|
2
2
|
import type { DeepPartial, FieldValues, UseFormReturn } from "react-hook-form";
|
|
3
3
|
|
|
4
4
|
export interface UseConfirmBeforeBackProps {
|
|
@@ -21,7 +21,7 @@ interface LocalCacheOptions {
|
|
|
21
21
|
export interface AtlantisFormContextProps {
|
|
22
22
|
useConfirmBeforeBack: (
|
|
23
23
|
props: UseConfirmBeforeBackProps,
|
|
24
|
-
) =>
|
|
24
|
+
) => RefObject<() => void>;
|
|
25
25
|
useInternalFormLocalCache: <TData extends FieldValues>(
|
|
26
26
|
formMethods: UseFormReturn<TData>,
|
|
27
27
|
cacheKey?: string,
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
UseFormReturn,
|
|
6
6
|
} from "react-hook-form";
|
|
7
7
|
import { useForm } from "react-hook-form";
|
|
8
|
-
import type {
|
|
8
|
+
import type { RefObject } from "react";
|
|
9
9
|
import type { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
|
10
10
|
import { useAtlantisContext } from "../../AtlantisContext";
|
|
11
11
|
import { useAtlantisFormContext } from "../context/AtlantisFormContext";
|
|
@@ -32,7 +32,7 @@ interface UseInternalForm<T extends FieldValues> {
|
|
|
32
32
|
readonly handleSubmit: UseFormHandleSubmit<T>;
|
|
33
33
|
readonly isSubmitting: boolean;
|
|
34
34
|
readonly isDirty: boolean;
|
|
35
|
-
readonly removeListenerRef:
|
|
35
|
+
readonly removeListenerRef: RefObject<() => void>;
|
|
36
36
|
readonly setLocalCache: (data: DeepPartial<T>) => void;
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
waitFor,
|
|
9
9
|
} from "@testing-library/react-native";
|
|
10
10
|
import type { TextStyle } from "react-native";
|
|
11
|
-
import { Platform } from "react-native";
|
|
11
|
+
import { Button, Platform } from "react-native";
|
|
12
12
|
import { FormProvider, useForm } from "react-hook-form";
|
|
13
13
|
import type { InputTextProps } from "./InputText";
|
|
14
14
|
import { InputText } from "./InputText";
|
|
@@ -623,6 +623,120 @@ describe("InputText", () => {
|
|
|
623
623
|
});
|
|
624
624
|
});
|
|
625
625
|
});
|
|
626
|
+
|
|
627
|
+
describe("with FormProvider", () => {
|
|
628
|
+
const mockOnSubmit = jest.fn();
|
|
629
|
+
const inputName = "testInput";
|
|
630
|
+
const inputAccessibilityLabel = "Test Input";
|
|
631
|
+
const saveButtonText = "Save";
|
|
632
|
+
|
|
633
|
+
function FormWithProvider({
|
|
634
|
+
defaultValue,
|
|
635
|
+
}: {
|
|
636
|
+
readonly defaultValue?: string;
|
|
637
|
+
}) {
|
|
638
|
+
const formMethods = useForm();
|
|
639
|
+
|
|
640
|
+
return (
|
|
641
|
+
<FormProvider {...formMethods}>
|
|
642
|
+
<InputText
|
|
643
|
+
name={inputName}
|
|
644
|
+
defaultValue={defaultValue}
|
|
645
|
+
accessibilityLabel={inputAccessibilityLabel}
|
|
646
|
+
/>
|
|
647
|
+
<Button
|
|
648
|
+
onPress={formMethods.handleSubmit(values => mockOnSubmit(values))}
|
|
649
|
+
title={saveButtonText}
|
|
650
|
+
accessibilityLabel={saveButtonText}
|
|
651
|
+
/>
|
|
652
|
+
</FormProvider>
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
beforeEach(() => {
|
|
657
|
+
mockOnSubmit.mockClear();
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
describe("defaultValue prop sets form value", () => {
|
|
661
|
+
it("sets form value to string when defaultValue is provided", async () => {
|
|
662
|
+
const testValue = "test value";
|
|
663
|
+
const { getByLabelText } = render(
|
|
664
|
+
<FormWithProvider defaultValue={testValue} />,
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
668
|
+
await waitFor(() => {
|
|
669
|
+
fireEvent.press(saveButton);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
673
|
+
[inputName]: testValue,
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it("sets form value to undefined when defaultValue is undefined", async () => {
|
|
678
|
+
const { getByLabelText } = render(<FormWithProvider />);
|
|
679
|
+
|
|
680
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
681
|
+
await waitFor(() => {
|
|
682
|
+
fireEvent.press(saveButton);
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
686
|
+
[inputName]: undefined,
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it("sets form value to undefined when defaultValue is empty string", async () => {
|
|
691
|
+
const { getByLabelText } = render(<FormWithProvider defaultValue="" />);
|
|
692
|
+
|
|
693
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
694
|
+
await waitFor(() => {
|
|
695
|
+
fireEvent.press(saveButton);
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
699
|
+
[inputName]: undefined,
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
describe("input value updates form value", () => {
|
|
705
|
+
it("updates form value when input text is changed", async () => {
|
|
706
|
+
const { getByLabelText } = render(
|
|
707
|
+
<FormWithProvider defaultValue="initial" />,
|
|
708
|
+
);
|
|
709
|
+
|
|
710
|
+
const input = getByLabelText(inputAccessibilityLabel);
|
|
711
|
+
fireEvent.changeText(input, "new value");
|
|
712
|
+
|
|
713
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
714
|
+
await waitFor(() => {
|
|
715
|
+
fireEvent.press(saveButton);
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
719
|
+
[inputName]: "new value",
|
|
720
|
+
});
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
it("preserves defaultValue when input is not interacted with", async () => {
|
|
724
|
+
const testValue = "preserved value";
|
|
725
|
+
const { getByLabelText } = render(
|
|
726
|
+
<FormWithProvider defaultValue={testValue} />,
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
730
|
+
await waitFor(() => {
|
|
731
|
+
fireEvent.press(saveButton);
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
735
|
+
[inputName]: testValue,
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
});
|
|
739
|
+
});
|
|
626
740
|
});
|
|
627
741
|
|
|
628
742
|
describe("Transform", () => {
|
|
@@ -126,7 +126,7 @@ export interface InputTextProps
|
|
|
126
126
|
/**
|
|
127
127
|
* Callback that is called when the text input is blurred
|
|
128
128
|
*/
|
|
129
|
-
readonly onBlur?: () => void;
|
|
129
|
+
readonly onBlur?: (event?: FocusEvent) => void;
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
132
|
* VoiceOver will read this string when a user selects the associated element
|
|
@@ -436,10 +436,10 @@ function InputTextInternal(
|
|
|
436
436
|
setFocused(true);
|
|
437
437
|
onFocus?.(event);
|
|
438
438
|
}}
|
|
439
|
-
onBlur={
|
|
439
|
+
onBlur={event => {
|
|
440
440
|
_name && setFocusedInput("");
|
|
441
441
|
setFocused(false);
|
|
442
|
-
onBlur?.();
|
|
442
|
+
onBlur?.(event);
|
|
443
443
|
field.onBlur();
|
|
444
444
|
trimWhitespace(inputTransform(field.value), updateFormAndState);
|
|
445
445
|
}}
|
package/src/Menu/types.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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>
|
package/src/ProgressBar/types.ts
CHANGED
|
@@ -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
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { buildThemedStyles } from "../AtlantisThemeContext";
|
|
2
|
+
|
|
3
|
+
export const useStyles = buildThemedStyles(tokens => {
|
|
4
|
+
const statusIndicatorDiameter = 8;
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
statusIndicator: {
|
|
8
|
+
borderRadius: tokens["radius-circle"],
|
|
9
|
+
backgroundColor: tokens["color-success"],
|
|
10
|
+
width: statusIndicatorDiameter,
|
|
11
|
+
height: statusIndicatorDiameter,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { StatusIndicator } from "./StatusIndicator";
|
|
4
|
+
|
|
5
|
+
describe("StatusIndicator", () => {
|
|
6
|
+
describe("status", () => {
|
|
7
|
+
describe('when status prop set to default ("success")', () => {
|
|
8
|
+
it("should match snapshot", () => {
|
|
9
|
+
const view = render(<StatusIndicator status="success" />).toJSON();
|
|
10
|
+
expect(view).toMatchSnapshot();
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('when status prop set to "warning"', () => {
|
|
15
|
+
it("should match snapshot", () => {
|
|
16
|
+
const view = render(<StatusIndicator status="warning" />).toJSON();
|
|
17
|
+
expect(view).toMatchSnapshot();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('when status prop set to "critical"', () => {
|
|
22
|
+
it("should match snapshot", () => {
|
|
23
|
+
const view = render(<StatusIndicator status="critical" />).toJSON();
|
|
24
|
+
expect(view).toMatchSnapshot();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('when status prop set to "inactive"', () => {
|
|
29
|
+
it("should match snapshot", () => {
|
|
30
|
+
const view = render(<StatusIndicator status="inactive" />).toJSON();
|
|
31
|
+
expect(view).toMatchSnapshot();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('when status prop set to "informative"', () => {
|
|
36
|
+
it("should match snapshot", () => {
|
|
37
|
+
const view = render(<StatusIndicator status="informative" />).toJSON();
|
|
38
|
+
expect(view).toMatchSnapshot();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { useStyles } from "./StatusIndicator.style";
|
|
4
|
+
import type { StatusIndicatorType } from "./StatusIndicator.type";
|
|
5
|
+
import { tokens } from "../utils/design";
|
|
6
|
+
|
|
7
|
+
export interface StatusIndicatorProps {
|
|
8
|
+
readonly status: StatusIndicatorType;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function StatusIndicator({ status }: StatusIndicatorProps) {
|
|
12
|
+
const styles = useStyles();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<View
|
|
16
|
+
testID={`${status}Indicator`}
|
|
17
|
+
style={[
|
|
18
|
+
styles.statusIndicator,
|
|
19
|
+
{ backgroundColor: tokens[`color-${status}`] },
|
|
20
|
+
]}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
}
|