@jobber/components-native 0.89.5-JOB-139254-4e3c64d.7 → 0.90.1-JOB-140976-20bb6ae.11
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 +2 -2
- package/dist/src/Form/Form.js +2 -2
- package/dist/src/Form/hooks/useInternalForm.js +2 -2
- package/dist/src/InputFieldWrapper/InputFieldWrapper.js +14 -12
- package/dist/src/InputFieldWrapper/components/Prefix/Prefix.js +5 -2
- package/dist/src/InputFieldWrapper/components/Suffix/Suffix.js +5 -2
- package/dist/src/InputPressable/InputPressable.js +20 -8
- package/dist/src/InputText/InputText.js +19 -11
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/Form/hooks/useInternalForm.d.ts +2 -2
- package/dist/types/src/Form/types.d.ts +1 -1
- package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +9 -2
- package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +2 -3
- package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +2 -3
- package/dist/types/src/InputPressable/InputPressable.d.ts +9 -1
- package/dist/types/src/InputText/InputText.d.ts +8 -0
- package/package.json +2 -2
- package/src/Form/Form.test.tsx +143 -0
- package/src/Form/Form.tsx +2 -2
- package/src/Form/hooks/useInternalForm.ts +3 -3
- package/src/Form/types.ts +1 -1
- package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +48 -1
- package/src/InputFieldWrapper/InputFieldWrapper.tsx +38 -28
- package/src/InputFieldWrapper/components/Prefix/Prefix.test.tsx +3 -5
- package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +6 -4
- package/src/InputFieldWrapper/components/Suffix/Suffix.test.tsx +2 -4
- package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +6 -4
- package/src/InputPressable/InputPressable.test.tsx +75 -1
- package/src/InputPressable/InputPressable.tsx +33 -7
- package/src/InputText/InputText.test.tsx +75 -0
- package/src/InputText/InputText.tsx +29 -12
|
@@ -2,7 +2,7 @@ import type { DeepPartial, FieldValues, UseFormHandleSubmit, UseFormReturn } fro
|
|
|
2
2
|
import type { MutableRefObject, RefObject } from "react";
|
|
3
3
|
import type { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
|
4
4
|
import type { InternalFormProps } from "../types";
|
|
5
|
-
type UseInternalFormProps<T extends FieldValues, SubmitResponseType> = Pick<InternalFormProps<T, SubmitResponseType>, "mode" | "reValidateMode" | "initialValues" | "formRef" | "localCacheKey" | "localCacheExclude" | "localCacheId" | "
|
|
5
|
+
type UseInternalFormProps<T extends FieldValues, SubmitResponseType> = Pick<InternalFormProps<T, SubmitResponseType>, "mode" | "reValidateMode" | "initialValues" | "formRef" | "localCacheKey" | "localCacheExclude" | "localCacheId" | "UNSAFE_allowDiscardLocalCacheWhenOffline"> & {
|
|
6
6
|
scrollViewRef?: RefObject<KeyboardAwareScrollView>;
|
|
7
7
|
readonly saveButtonHeight: number;
|
|
8
8
|
readonly messageBannerHeight: number;
|
|
@@ -15,5 +15,5 @@ interface UseInternalForm<T extends FieldValues> {
|
|
|
15
15
|
readonly removeListenerRef: MutableRefObject<() => void>;
|
|
16
16
|
readonly setLocalCache: (data: DeepPartial<T>) => void;
|
|
17
17
|
}
|
|
18
|
-
export declare function useInternalForm<T extends FieldValues, SubmitResponseType>({ mode, reValidateMode, initialValues, formRef, localCacheKey, localCacheId, scrollViewRef, saveButtonHeight, messageBannerHeight,
|
|
18
|
+
export declare function useInternalForm<T extends FieldValues, SubmitResponseType>({ mode, reValidateMode, initialValues, formRef, localCacheKey, localCacheId, scrollViewRef, saveButtonHeight, messageBannerHeight, UNSAFE_allowDiscardLocalCacheWhenOffline, }: UseInternalFormProps<T, SubmitResponseType>): UseInternalForm<T>;
|
|
19
19
|
export {};
|
|
@@ -136,7 +136,7 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
|
|
|
136
136
|
* the dirty form even when offline. By default, cache is only removed on back when online.
|
|
137
137
|
* Defaults to false.
|
|
138
138
|
*/
|
|
139
|
-
|
|
139
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline?: boolean;
|
|
140
140
|
/**
|
|
141
141
|
* Secondary Action for ButtonGroup
|
|
142
142
|
*/
|
|
@@ -26,7 +26,14 @@ export interface InputFieldWrapperProps {
|
|
|
26
26
|
* Text that goes below the input to help the user understand the input
|
|
27
27
|
*/
|
|
28
28
|
readonly assistiveText?: string;
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Controls how the placeholder text is displayed.
|
|
31
|
+
* - normal: the placeholder text will be displayed in the normal placeholder position
|
|
32
|
+
* - mini: the placeholder text will float above the input value
|
|
33
|
+
* - hidden: the placeholder text will not be displayed
|
|
34
|
+
* @default "normal"
|
|
35
|
+
*/
|
|
36
|
+
readonly placeholderMode?: "normal" | "mini" | "hidden";
|
|
30
37
|
readonly hasValue?: boolean;
|
|
31
38
|
/**
|
|
32
39
|
* Symbol to display before the text input
|
|
@@ -78,4 +85,4 @@ export interface InputFieldWrapperProps {
|
|
|
78
85
|
}
|
|
79
86
|
export declare const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID = "ATL-InputFieldWrapper-Glimmers";
|
|
80
87
|
export declare const INPUT_FIELD_WRAPPER_SPINNER_TEST_ID = "ATL-InputFieldWrapper-Spinner";
|
|
81
|
-
export declare function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix,
|
|
88
|
+
export declare function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, placeholderMode, hasValue, error, focused, children, onClear, showClearAction, styleOverride, toolbar, toolbarVisibility, loading, loadingType, }: InputFieldWrapperProps): JSX.Element;
|
|
@@ -3,18 +3,17 @@ import type { IconNames } from "@jobber/design";
|
|
|
3
3
|
export interface PrefixLabelProps {
|
|
4
4
|
readonly focused: boolean;
|
|
5
5
|
readonly disabled?: boolean;
|
|
6
|
-
readonly
|
|
6
|
+
readonly miniLabelActive: boolean;
|
|
7
7
|
readonly inputInvalid: boolean;
|
|
8
8
|
readonly label: string;
|
|
9
9
|
readonly styleOverride?: StyleProp<TextStyle>;
|
|
10
10
|
}
|
|
11
11
|
export declare const prefixLabelTestId = "ATL-InputFieldWrapper-PrefixLabel";
|
|
12
12
|
export declare const prefixIconTestId = "ATL-InputFieldWrapper-PrefixIcon";
|
|
13
|
-
export declare function PrefixLabel({ focused, disabled,
|
|
13
|
+
export declare function PrefixLabel({ focused, disabled, miniLabelActive, inputInvalid, label, styleOverride, }: PrefixLabelProps): JSX.Element;
|
|
14
14
|
export interface PrefixIconProps {
|
|
15
15
|
readonly focused: boolean;
|
|
16
16
|
readonly disabled?: boolean;
|
|
17
|
-
readonly hasMiniLabel: boolean;
|
|
18
17
|
readonly inputInvalid?: boolean;
|
|
19
18
|
readonly icon: IconNames;
|
|
20
19
|
readonly styleOverride?: StyleProp<ViewStyle>;
|
|
@@ -3,7 +3,7 @@ import type { IconNames } from "@jobber/design";
|
|
|
3
3
|
export interface SuffixLabelProps {
|
|
4
4
|
readonly focused: boolean;
|
|
5
5
|
readonly disabled?: boolean;
|
|
6
|
-
readonly
|
|
6
|
+
readonly miniLabelActive: boolean;
|
|
7
7
|
readonly inputInvalid?: boolean;
|
|
8
8
|
readonly label: string;
|
|
9
9
|
readonly hasLeftMargin?: boolean;
|
|
@@ -11,11 +11,10 @@ export interface SuffixLabelProps {
|
|
|
11
11
|
}
|
|
12
12
|
export declare const suffixLabelTestId = "ATL-InputFieldWrapper-SuffixLabel";
|
|
13
13
|
export declare const suffixIconTestId = "ATL-InputFieldWrapper-SuffixIcon";
|
|
14
|
-
export declare function SuffixLabel({ focused, disabled,
|
|
14
|
+
export declare function SuffixLabel({ focused, disabled, miniLabelActive, inputInvalid, label, hasLeftMargin, styleOverride, }: SuffixLabelProps): JSX.Element;
|
|
15
15
|
export interface SuffixIconProps {
|
|
16
16
|
readonly focused: boolean;
|
|
17
17
|
readonly disabled?: boolean;
|
|
18
|
-
readonly hasMiniLabel: boolean;
|
|
19
18
|
readonly inputInvalid?: boolean;
|
|
20
19
|
readonly icon: IconNames;
|
|
21
20
|
readonly hasLeftMargin?: boolean;
|
|
@@ -39,6 +39,14 @@ export interface InputPressableProps {
|
|
|
39
39
|
* Indicates the current selection is invalid
|
|
40
40
|
*/
|
|
41
41
|
readonly invalid?: boolean | string;
|
|
42
|
+
/**
|
|
43
|
+
* Controls the visibility of the mini label that appears inside the input
|
|
44
|
+
* when a value is entered. By default, the placeholder text moves up to
|
|
45
|
+
* become a mini label. Set to false to disable this behavior.
|
|
46
|
+
*
|
|
47
|
+
* @default true
|
|
48
|
+
*/
|
|
49
|
+
readonly showMiniLabel?: boolean;
|
|
42
50
|
/**
|
|
43
51
|
* Callback that is called when the text input is focused
|
|
44
52
|
* @param event
|
|
@@ -79,5 +87,5 @@ export interface InputPressableProps {
|
|
|
79
87
|
}
|
|
80
88
|
export type InputPressableRef = NativeText;
|
|
81
89
|
export declare const InputPressable: React.ForwardRefExoticComponent<InputPressableProps & React.RefAttributes<NativeText>>;
|
|
82
|
-
export declare function InputPressableInternal({ value, placeholder, disabled, invalid, error, onPress, accessibilityLabel, accessibilityHint, prefix, suffix, clearable, onClear, focused, }: InputPressableProps, ref: Ref<InputPressableRef>): JSX.Element;
|
|
90
|
+
export declare function InputPressableInternal({ value, placeholder, disabled, invalid, error, showMiniLabel, onPress, accessibilityLabel, accessibilityHint, prefix, suffix, clearable, onClear, focused, }: InputPressableProps, ref: Ref<InputPressableRef>): JSX.Element;
|
|
83
91
|
export {};
|
|
@@ -31,6 +31,14 @@ export interface InputTextProps extends Pick<InputFieldWrapperProps, "toolbar" |
|
|
|
31
31
|
* Text that helps the user understand the input
|
|
32
32
|
*/
|
|
33
33
|
readonly assistiveText?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Controls the visibility of the mini label that appears inside the input
|
|
36
|
+
* when a value is entered. By default, the placeholder text moves up to
|
|
37
|
+
* become a mini label. Set to false to disable this behavior.
|
|
38
|
+
*
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
readonly showMiniLabel?: boolean;
|
|
34
42
|
/**
|
|
35
43
|
* Determines what keyboard is shown
|
|
36
44
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.90.1-JOB-140976-20bb6ae.11+20bb6ae6a",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native implementation of Atlantis",
|
|
6
6
|
"repository": {
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"react-native-safe-area-context": "^5.4.0",
|
|
95
95
|
"react-native-svg": ">=12.0.0"
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "20bb6ae6a51997e8306d4f4ddf12295b33ca16be"
|
|
98
98
|
}
|
package/src/Form/Form.test.tsx
CHANGED
|
@@ -104,6 +104,7 @@ interface FormTestProps {
|
|
|
104
104
|
readonly onBeforeSubmit?: jest.Mock;
|
|
105
105
|
readonly renderFooter?: React.ReactNode;
|
|
106
106
|
readonly saveButtonOffset?: number;
|
|
107
|
+
readonly UNSAFE_allowDiscardLocalCacheWhenOffline?: boolean;
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
function FormTest(props: FormTestProps) {
|
|
@@ -125,6 +126,7 @@ function MockForm({
|
|
|
125
126
|
localCacheId,
|
|
126
127
|
renderFooter,
|
|
127
128
|
saveButtonOffset,
|
|
129
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline = false,
|
|
128
130
|
}: FormTestProps) {
|
|
129
131
|
const formErrors: FormBannerErrors = {};
|
|
130
132
|
|
|
@@ -154,6 +156,9 @@ function MockForm({
|
|
|
154
156
|
onBeforeSubmit={onBeforeSubmit}
|
|
155
157
|
renderFooter={renderFooter}
|
|
156
158
|
saveButtonOffset={saveButtonOffset}
|
|
159
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline={
|
|
160
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline
|
|
161
|
+
}
|
|
157
162
|
>
|
|
158
163
|
<InputText
|
|
159
164
|
name={testInputTextName}
|
|
@@ -561,4 +566,142 @@ describe("Form", () => {
|
|
|
561
566
|
expect(queryByTestId("ATL-FormSafeArea")).toBeNull();
|
|
562
567
|
});
|
|
563
568
|
});
|
|
569
|
+
|
|
570
|
+
describe("Leaving the form", () => {
|
|
571
|
+
let mockUseConfirmBeforeBack: jest.Mock;
|
|
572
|
+
let mockRemoveLocalCache: jest.Mock;
|
|
573
|
+
const atlantisContextSpy = jest.spyOn(
|
|
574
|
+
atlantisContext,
|
|
575
|
+
"useAtlantisContext",
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
beforeEach(() => {
|
|
579
|
+
mockUseConfirmBeforeBack = jest
|
|
580
|
+
.fn()
|
|
581
|
+
.mockReturnValue({ current: jest.fn() });
|
|
582
|
+
mockRemoveLocalCache = jest.fn();
|
|
583
|
+
|
|
584
|
+
jest
|
|
585
|
+
.spyOn(
|
|
586
|
+
require("../Form/context/AtlantisFormContext"),
|
|
587
|
+
"useAtlantisFormContext",
|
|
588
|
+
)
|
|
589
|
+
.mockReturnValue({
|
|
590
|
+
useConfirmBeforeBack: mockUseConfirmBeforeBack,
|
|
591
|
+
useInternalFormLocalCache: () => ({
|
|
592
|
+
setLocalCache: jest.fn(),
|
|
593
|
+
removeLocalCache: mockRemoveLocalCache,
|
|
594
|
+
}),
|
|
595
|
+
edgeToEdgeEnabled: false,
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
afterEach(() => {
|
|
600
|
+
jest.restoreAllMocks();
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
describe("when UNSAFE_allowDiscardLocalCacheWhenOffline is false", () => {
|
|
604
|
+
it("should NOT pass onAcceptEvent when offline", () => {
|
|
605
|
+
atlantisContextSpy.mockReturnValue({
|
|
606
|
+
...atlantisContextDefaultValues,
|
|
607
|
+
isOnline: false,
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
render(
|
|
611
|
+
<FormTest
|
|
612
|
+
onSubmit={onSubmitMock}
|
|
613
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline={false}
|
|
614
|
+
localCacheKey="testCacheKey"
|
|
615
|
+
/>,
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
|
|
619
|
+
const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
|
|
620
|
+
expect(callArgs.onAcceptEvent).toBeUndefined();
|
|
621
|
+
expect(callArgs.showLostProgressMessage).toBe(false);
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it("should pass onAcceptEvent when online", () => {
|
|
625
|
+
atlantisContextSpy.mockReturnValue({
|
|
626
|
+
...atlantisContextDefaultValues,
|
|
627
|
+
isOnline: true,
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
render(
|
|
631
|
+
<FormTest
|
|
632
|
+
onSubmit={onSubmitMock}
|
|
633
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline={false}
|
|
634
|
+
localCacheKey="testCacheKey"
|
|
635
|
+
/>,
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
|
|
639
|
+
const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
|
|
640
|
+
expect(callArgs.onAcceptEvent).toBe(mockRemoveLocalCache);
|
|
641
|
+
expect(callArgs.showLostProgressMessage).toBe(true);
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
describe("when UNSAFE_allowDiscardLocalCacheWhenOffline is true", () => {
|
|
646
|
+
it("should pass onAcceptEvent when offline", () => {
|
|
647
|
+
atlantisContextSpy.mockReturnValue({
|
|
648
|
+
...atlantisContextDefaultValues,
|
|
649
|
+
isOnline: false,
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
render(
|
|
653
|
+
<FormTest
|
|
654
|
+
onSubmit={onSubmitMock}
|
|
655
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline={true}
|
|
656
|
+
localCacheKey="testCacheKey"
|
|
657
|
+
/>,
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
|
|
661
|
+
const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
|
|
662
|
+
expect(callArgs.onAcceptEvent).toBe(mockRemoveLocalCache);
|
|
663
|
+
expect(callArgs.showLostProgressMessage).toBe(true);
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it("should pass onAcceptEvent when online", () => {
|
|
667
|
+
atlantisContextSpy.mockReturnValue({
|
|
668
|
+
...atlantisContextDefaultValues,
|
|
669
|
+
isOnline: true,
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
render(
|
|
673
|
+
<FormTest
|
|
674
|
+
onSubmit={onSubmitMock}
|
|
675
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline={true}
|
|
676
|
+
localCacheKey="testCacheKey"
|
|
677
|
+
/>,
|
|
678
|
+
);
|
|
679
|
+
|
|
680
|
+
expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
|
|
681
|
+
const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
|
|
682
|
+
expect(callArgs.onAcceptEvent).toBe(mockRemoveLocalCache);
|
|
683
|
+
expect(callArgs.showLostProgressMessage).toBe(true);
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
describe("without localCacheKey", () => {
|
|
688
|
+
it("should always show lost progress message when no cache key is provided", () => {
|
|
689
|
+
atlantisContextSpy.mockReturnValue({
|
|
690
|
+
...atlantisContextDefaultValues,
|
|
691
|
+
isOnline: false,
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
render(
|
|
695
|
+
<FormTest
|
|
696
|
+
onSubmit={onSubmitMock}
|
|
697
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline={false}
|
|
698
|
+
/>,
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
|
|
702
|
+
const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
|
|
703
|
+
expect(callArgs.showLostProgressMessage).toBe(true);
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
});
|
|
564
707
|
});
|
package/src/Form/Form.tsx
CHANGED
|
@@ -66,7 +66,7 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
66
66
|
saveButtonOffset,
|
|
67
67
|
showStickySaveButton = false,
|
|
68
68
|
renderFooter,
|
|
69
|
-
|
|
69
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline,
|
|
70
70
|
}: InternalFormProps<T, S>) {
|
|
71
71
|
const { scrollViewRef, bottomViewRef, scrollToTop } = useFormViewRefs();
|
|
72
72
|
const [saveButtonHeight, setSaveButtonHeight] = useState(0);
|
|
@@ -88,7 +88,7 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
88
88
|
scrollViewRef,
|
|
89
89
|
saveButtonHeight,
|
|
90
90
|
messageBannerHeight,
|
|
91
|
-
|
|
91
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline,
|
|
92
92
|
});
|
|
93
93
|
const { windowHeight, headerHeight } = useScreenInformation();
|
|
94
94
|
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
@@ -20,7 +20,7 @@ type UseInternalFormProps<T extends FieldValues, SubmitResponseType> = Pick<
|
|
|
20
20
|
| "localCacheKey"
|
|
21
21
|
| "localCacheExclude"
|
|
22
22
|
| "localCacheId"
|
|
23
|
-
| "
|
|
23
|
+
| "UNSAFE_allowDiscardLocalCacheWhenOffline"
|
|
24
24
|
> & {
|
|
25
25
|
scrollViewRef?: RefObject<KeyboardAwareScrollView>;
|
|
26
26
|
readonly saveButtonHeight: number;
|
|
@@ -46,7 +46,7 @@ export function useInternalForm<T extends FieldValues, SubmitResponseType>({
|
|
|
46
46
|
scrollViewRef,
|
|
47
47
|
saveButtonHeight,
|
|
48
48
|
messageBannerHeight,
|
|
49
|
-
|
|
49
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline = false,
|
|
50
50
|
}: UseInternalFormProps<T, SubmitResponseType>): UseInternalForm<T> {
|
|
51
51
|
const { useConfirmBeforeBack, useInternalFormLocalCache } =
|
|
52
52
|
useAtlantisFormContext();
|
|
@@ -84,7 +84,7 @@ export function useInternalForm<T extends FieldValues, SubmitResponseType>({
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
const shouldRemoveCacheOnBack =
|
|
87
|
+
const shouldRemoveCacheOnBack = UNSAFE_allowDiscardLocalCacheWhenOffline
|
|
88
88
|
? true
|
|
89
89
|
: isOnline;
|
|
90
90
|
|
package/src/Form/types.ts
CHANGED
|
@@ -176,7 +176,7 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
|
|
|
176
176
|
* the dirty form even when offline. By default, cache is only removed on back when online.
|
|
177
177
|
* Defaults to false.
|
|
178
178
|
*/
|
|
179
|
-
|
|
179
|
+
UNSAFE_allowDiscardLocalCacheWhenOffline?: boolean;
|
|
180
180
|
|
|
181
181
|
/**
|
|
182
182
|
* Secondary Action for ButtonGroup
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { RenderAPI } from "@testing-library/react-native";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
fireEvent,
|
|
5
|
+
render,
|
|
6
|
+
renderHook,
|
|
7
|
+
screen,
|
|
8
|
+
} from "@testing-library/react-native";
|
|
4
9
|
import type { ViewStyle } from "react-native";
|
|
5
10
|
import { Text } from "react-native";
|
|
6
11
|
import type { InputFieldWrapperProps } from ".";
|
|
@@ -311,4 +316,46 @@ describe("InputFieldWrapper", () => {
|
|
|
311
316
|
expect(queryByTestId(INPUT_FIELD_WRAPPER_SPINNER_TEST_ID)).toBeNull();
|
|
312
317
|
});
|
|
313
318
|
});
|
|
319
|
+
|
|
320
|
+
describe("placeholderMode", () => {
|
|
321
|
+
it("renders the placeholder in its normal position", () => {
|
|
322
|
+
renderInputFieldWrapper({
|
|
323
|
+
placeholder: "placeholder",
|
|
324
|
+
placeholderMode: "normal",
|
|
325
|
+
});
|
|
326
|
+
const placeholder = screen.getByText("placeholder", {
|
|
327
|
+
includeHiddenElements: true,
|
|
328
|
+
});
|
|
329
|
+
expect(placeholder).toBeDefined();
|
|
330
|
+
expect(placeholder.props.style).toContainEqual(
|
|
331
|
+
typographyStyles.defaultSize,
|
|
332
|
+
);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("renders the placeholder in its mini label position", () => {
|
|
336
|
+
renderInputFieldWrapper({
|
|
337
|
+
placeholder: "placeholder",
|
|
338
|
+
placeholderMode: "mini",
|
|
339
|
+
});
|
|
340
|
+
const placeholder = screen.getByText("placeholder", {
|
|
341
|
+
includeHiddenElements: true,
|
|
342
|
+
});
|
|
343
|
+
expect(placeholder).toBeDefined();
|
|
344
|
+
expect(placeholder.props.style).toContainEqual(
|
|
345
|
+
typographyStyles.smallSize,
|
|
346
|
+
);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("does not render the placeholder", () => {
|
|
350
|
+
renderInputFieldWrapper({
|
|
351
|
+
placeholder: "placeholder",
|
|
352
|
+
placeholderMode: "hidden",
|
|
353
|
+
});
|
|
354
|
+
expect(
|
|
355
|
+
screen.queryByText("placeholder", {
|
|
356
|
+
includeHiddenElements: true,
|
|
357
|
+
}),
|
|
358
|
+
).toBeNull();
|
|
359
|
+
});
|
|
360
|
+
});
|
|
314
361
|
});
|
|
@@ -43,7 +43,14 @@ export interface InputFieldWrapperProps {
|
|
|
43
43
|
*/
|
|
44
44
|
readonly assistiveText?: string;
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Controls how the placeholder text is displayed.
|
|
48
|
+
* - normal: the placeholder text will be displayed in the normal placeholder position
|
|
49
|
+
* - mini: the placeholder text will float above the input value
|
|
50
|
+
* - hidden: the placeholder text will not be displayed
|
|
51
|
+
* @default "normal"
|
|
52
|
+
*/
|
|
53
|
+
readonly placeholderMode?: "normal" | "mini" | "hidden";
|
|
47
54
|
|
|
48
55
|
readonly hasValue?: boolean;
|
|
49
56
|
|
|
@@ -119,7 +126,7 @@ export function InputFieldWrapper({
|
|
|
119
126
|
assistiveText,
|
|
120
127
|
prefix,
|
|
121
128
|
suffix,
|
|
122
|
-
|
|
129
|
+
placeholderMode = "normal",
|
|
123
130
|
hasValue = false,
|
|
124
131
|
error,
|
|
125
132
|
focused = false,
|
|
@@ -143,6 +150,9 @@ export function InputFieldWrapper({
|
|
|
143
150
|
const showLoadingGlimmer = loading && loadingType === "glimmer";
|
|
144
151
|
const styles = useStyles();
|
|
145
152
|
|
|
153
|
+
const placeholderVisible = placeholderMode !== "hidden";
|
|
154
|
+
const miniLabelActive = placeholderMode === "mini";
|
|
155
|
+
|
|
146
156
|
return (
|
|
147
157
|
<ErrorMessageWrapper message={getMessage({ invalid, error })}>
|
|
148
158
|
<View
|
|
@@ -160,35 +170,36 @@ export function InputFieldWrapper({
|
|
|
160
170
|
<PrefixIcon
|
|
161
171
|
disabled={disabled}
|
|
162
172
|
focused={focused}
|
|
163
|
-
hasMiniLabel={hasMiniLabel}
|
|
164
173
|
inputInvalid={inputInvalid}
|
|
165
174
|
icon={prefix.icon}
|
|
166
175
|
/>
|
|
167
176
|
)}
|
|
168
177
|
<View style={[styles.inputContainer]}>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
178
|
+
{placeholderVisible && (
|
|
179
|
+
<View
|
|
180
|
+
style={[
|
|
181
|
+
!!placeholder && styles.label,
|
|
182
|
+
miniLabelActive && styles.miniLabel,
|
|
183
|
+
disabled && styles.disabled,
|
|
184
|
+
miniLabelActive &&
|
|
185
|
+
showClearAction &&
|
|
186
|
+
styles.miniLabelShowClearAction,
|
|
187
|
+
]}
|
|
188
|
+
pointerEvents="none"
|
|
189
|
+
>
|
|
190
|
+
<Placeholder
|
|
191
|
+
placeholder={placeholder}
|
|
192
|
+
labelVariation={getLabelVariation(error, invalid, disabled)}
|
|
193
|
+
miniLabelActive={miniLabelActive}
|
|
194
|
+
styleOverride={styleOverride?.placeholderText}
|
|
195
|
+
/>
|
|
196
|
+
</View>
|
|
197
|
+
)}
|
|
187
198
|
{prefix?.label && hasValue && (
|
|
188
199
|
<PrefixLabel
|
|
189
200
|
disabled={disabled}
|
|
190
201
|
focused={focused}
|
|
191
|
-
|
|
202
|
+
miniLabelActive={miniLabelActive}
|
|
192
203
|
inputInvalid={inputInvalid}
|
|
193
204
|
label={prefix.label}
|
|
194
205
|
styleOverride={styleOverride?.prefixLabel}
|
|
@@ -225,7 +236,7 @@ export function InputFieldWrapper({
|
|
|
225
236
|
<SuffixLabel
|
|
226
237
|
disabled={disabled}
|
|
227
238
|
focused={focused}
|
|
228
|
-
|
|
239
|
+
miniLabelActive={miniLabelActive}
|
|
229
240
|
inputInvalid={inputInvalid}
|
|
230
241
|
label={suffix.label}
|
|
231
242
|
hasLeftMargin={!showClearAction}
|
|
@@ -245,7 +256,6 @@ export function InputFieldWrapper({
|
|
|
245
256
|
<SuffixIcon
|
|
246
257
|
disabled={disabled}
|
|
247
258
|
focused={focused}
|
|
248
|
-
hasMiniLabel={hasMiniLabel}
|
|
249
259
|
hasLeftMargin={!!(!showClearAction || suffix?.label)}
|
|
250
260
|
inputInvalid={inputInvalid}
|
|
251
261
|
icon={suffix.icon}
|
|
@@ -332,12 +342,12 @@ function Placeholder({
|
|
|
332
342
|
placeholder,
|
|
333
343
|
styleOverride,
|
|
334
344
|
labelVariation,
|
|
335
|
-
|
|
345
|
+
miniLabelActive,
|
|
336
346
|
}: {
|
|
337
347
|
readonly placeholder?: string;
|
|
338
348
|
readonly styleOverride: StyleProp<TextStyle>;
|
|
339
349
|
readonly labelVariation: TextVariation;
|
|
340
|
-
readonly
|
|
350
|
+
readonly miniLabelActive: boolean;
|
|
341
351
|
}) {
|
|
342
352
|
const typographyStyles = useTypographyStyles();
|
|
343
353
|
|
|
@@ -348,7 +358,7 @@ function Placeholder({
|
|
|
348
358
|
hideFromScreenReader={true}
|
|
349
359
|
maxLines="single"
|
|
350
360
|
variation={labelVariation}
|
|
351
|
-
level={
|
|
361
|
+
level={miniLabelActive ? "textSupporting" : "text"}
|
|
352
362
|
>
|
|
353
363
|
{placeholder}
|
|
354
364
|
</Text>
|
|
@@ -361,7 +371,7 @@ function Placeholder({
|
|
|
361
371
|
style={[
|
|
362
372
|
typographyStyles[labelVariation],
|
|
363
373
|
typographyStyles.baseRegularRegular,
|
|
364
|
-
|
|
374
|
+
miniLabelActive
|
|
365
375
|
? typographyStyles.smallSize
|
|
366
376
|
: typographyStyles.defaultSize,
|
|
367
377
|
styleOverride,
|
|
@@ -30,7 +30,7 @@ beforeAll(() => {
|
|
|
30
30
|
function setupLabel({
|
|
31
31
|
disabled = false,
|
|
32
32
|
focused = false,
|
|
33
|
-
|
|
33
|
+
miniLabelActive = false,
|
|
34
34
|
inputInvalid = false,
|
|
35
35
|
label = mockLabel,
|
|
36
36
|
styleOverride,
|
|
@@ -39,7 +39,7 @@ function setupLabel({
|
|
|
39
39
|
<PrefixLabel
|
|
40
40
|
disabled={disabled}
|
|
41
41
|
focused={focused}
|
|
42
|
-
|
|
42
|
+
miniLabelActive={miniLabelActive}
|
|
43
43
|
inputInvalid={inputInvalid}
|
|
44
44
|
label={label}
|
|
45
45
|
styleOverride={styleOverride}
|
|
@@ -50,7 +50,6 @@ function setupLabel({
|
|
|
50
50
|
function setupIcon({
|
|
51
51
|
disabled = false,
|
|
52
52
|
focused = false,
|
|
53
|
-
hasMiniLabel = false,
|
|
54
53
|
inputInvalid = false,
|
|
55
54
|
icon = "invoice",
|
|
56
55
|
}: Partial<PrefixIconProps>) {
|
|
@@ -58,7 +57,6 @@ function setupIcon({
|
|
|
58
57
|
<PrefixIcon
|
|
59
58
|
disabled={disabled}
|
|
60
59
|
focused={focused}
|
|
61
|
-
hasMiniLabel={hasMiniLabel}
|
|
62
60
|
inputInvalid={inputInvalid}
|
|
63
61
|
icon={icon}
|
|
64
62
|
/>,
|
|
@@ -150,7 +148,7 @@ describe("Prefix", () => {
|
|
|
150
148
|
|
|
151
149
|
it("updates the position of the label when a value is entered", () => {
|
|
152
150
|
const tree = setupLabel({
|
|
153
|
-
|
|
151
|
+
miniLabelActive: true,
|
|
154
152
|
});
|
|
155
153
|
const prefixLabel = tree.getByTestId(prefixLabelTestId);
|
|
156
154
|
const labelWrapper = prefixLabel.children[0] as ReactTestInstance;
|
|
@@ -11,7 +11,7 @@ import { useStyles } from "../../InputFieldWrapper.style";
|
|
|
11
11
|
export interface PrefixLabelProps {
|
|
12
12
|
readonly focused: boolean;
|
|
13
13
|
readonly disabled?: boolean;
|
|
14
|
-
readonly
|
|
14
|
+
readonly miniLabelActive: boolean;
|
|
15
15
|
readonly inputInvalid: boolean;
|
|
16
16
|
readonly label: string;
|
|
17
17
|
readonly styleOverride?: StyleProp<TextStyle>;
|
|
@@ -23,7 +23,7 @@ export const prefixIconTestId = "ATL-InputFieldWrapper-PrefixIcon";
|
|
|
23
23
|
export function PrefixLabel({
|
|
24
24
|
focused,
|
|
25
25
|
disabled,
|
|
26
|
-
|
|
26
|
+
miniLabelActive,
|
|
27
27
|
inputInvalid,
|
|
28
28
|
label,
|
|
29
29
|
styleOverride,
|
|
@@ -41,7 +41,10 @@ export function PrefixLabel({
|
|
|
41
41
|
testID={prefixLabelTestId}
|
|
42
42
|
>
|
|
43
43
|
<View
|
|
44
|
-
style={[
|
|
44
|
+
style={[
|
|
45
|
+
styles.prefixLabel,
|
|
46
|
+
miniLabelActive && styles.fieldAffixMiniLabel,
|
|
47
|
+
]}
|
|
45
48
|
>
|
|
46
49
|
{!styleOverride ? (
|
|
47
50
|
<Text variation={disabled ? "disabled" : "base"}>{label}</Text>
|
|
@@ -67,7 +70,6 @@ export function PrefixLabel({
|
|
|
67
70
|
export interface PrefixIconProps {
|
|
68
71
|
readonly focused: boolean;
|
|
69
72
|
readonly disabled?: boolean;
|
|
70
|
-
readonly hasMiniLabel: boolean;
|
|
71
73
|
readonly inputInvalid?: boolean;
|
|
72
74
|
readonly icon: IconNames;
|
|
73
75
|
readonly styleOverride?: StyleProp<ViewStyle>;
|