@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.
Files changed (31) hide show
  1. package/dist/package.json +2 -2
  2. package/dist/src/Form/Form.js +2 -2
  3. package/dist/src/Form/hooks/useInternalForm.js +2 -2
  4. package/dist/src/InputFieldWrapper/InputFieldWrapper.js +14 -12
  5. package/dist/src/InputFieldWrapper/components/Prefix/Prefix.js +5 -2
  6. package/dist/src/InputFieldWrapper/components/Suffix/Suffix.js +5 -2
  7. package/dist/src/InputPressable/InputPressable.js +20 -8
  8. package/dist/src/InputText/InputText.js +19 -11
  9. package/dist/tsconfig.build.tsbuildinfo +1 -1
  10. package/dist/types/src/Form/hooks/useInternalForm.d.ts +2 -2
  11. package/dist/types/src/Form/types.d.ts +1 -1
  12. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +9 -2
  13. package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +2 -3
  14. package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +2 -3
  15. package/dist/types/src/InputPressable/InputPressable.d.ts +9 -1
  16. package/dist/types/src/InputText/InputText.d.ts +8 -0
  17. package/package.json +2 -2
  18. package/src/Form/Form.test.tsx +143 -0
  19. package/src/Form/Form.tsx +2 -2
  20. package/src/Form/hooks/useInternalForm.ts +3 -3
  21. package/src/Form/types.ts +1 -1
  22. package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +48 -1
  23. package/src/InputFieldWrapper/InputFieldWrapper.tsx +38 -28
  24. package/src/InputFieldWrapper/components/Prefix/Prefix.test.tsx +3 -5
  25. package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +6 -4
  26. package/src/InputFieldWrapper/components/Suffix/Suffix.test.tsx +2 -4
  27. package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +6 -4
  28. package/src/InputPressable/InputPressable.test.tsx +75 -1
  29. package/src/InputPressable/InputPressable.tsx +33 -7
  30. package/src/InputText/InputText.test.tsx +75 -0
  31. 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" | "removeLocalCacheOnBackOffline"> & {
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, removeLocalCacheOnBackOffline, }: UseInternalFormProps<T, SubmitResponseType>): UseInternalForm<T>;
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
- removeLocalCacheOnBackOffline?: boolean;
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
- readonly hasMiniLabel?: boolean;
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, hasMiniLabel, hasValue, error, focused, children, onClear, showClearAction, styleOverride, toolbar, toolbarVisibility, loading, loadingType, }: InputFieldWrapperProps): JSX.Element;
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 hasMiniLabel: boolean;
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, hasMiniLabel, inputInvalid, label, styleOverride, }: PrefixLabelProps): JSX.Element;
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 hasMiniLabel: boolean;
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, hasMiniLabel, inputInvalid, label, hasLeftMargin, styleOverride, }: SuffixLabelProps): JSX.Element;
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.89.5-JOB-139254-4e3c64d.7+4e3c64d19",
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": "4e3c64d1982cb68580827ad583b4812b3a240e7c"
97
+ "gitHead": "20bb6ae6a51997e8306d4f4ddf12295b33ca16be"
98
98
  }
@@ -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
- removeLocalCacheOnBackOffline,
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
- removeLocalCacheOnBackOffline,
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
- | "removeLocalCacheOnBackOffline"
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
- removeLocalCacheOnBackOffline = false,
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 = removeLocalCacheOnBackOffline
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
- removeLocalCacheOnBackOffline?: boolean;
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 { fireEvent, render, renderHook } from "@testing-library/react-native";
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
- readonly hasMiniLabel?: boolean;
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
- hasMiniLabel = false,
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
- <View
170
- style={[
171
- !!placeholder && styles.label,
172
- hasMiniLabel && styles.miniLabel,
173
- disabled && styles.disabled,
174
- hasMiniLabel &&
175
- showClearAction &&
176
- styles.miniLabelShowClearAction,
177
- ]}
178
- pointerEvents="none"
179
- >
180
- <Placeholder
181
- placeholder={placeholder}
182
- labelVariation={getLabelVariation(error, invalid, disabled)}
183
- hasMiniLabel={hasMiniLabel}
184
- styleOverride={styleOverride?.placeholderText}
185
- />
186
- </View>
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
- hasMiniLabel={hasMiniLabel}
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
- hasMiniLabel={hasMiniLabel}
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
- hasMiniLabel,
345
+ miniLabelActive,
336
346
  }: {
337
347
  readonly placeholder?: string;
338
348
  readonly styleOverride: StyleProp<TextStyle>;
339
349
  readonly labelVariation: TextVariation;
340
- readonly hasMiniLabel: boolean;
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={hasMiniLabel ? "textSupporting" : "text"}
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
- hasMiniLabel
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
- hasMiniLabel = false,
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
- hasMiniLabel={hasMiniLabel}
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
- hasMiniLabel: true,
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 hasMiniLabel: boolean;
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
- hasMiniLabel,
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={[styles.prefixLabel, hasMiniLabel && styles.fieldAffixMiniLabel]}
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>;