@jobber/components-native 0.54.4-JOB-88641.9 → 0.54.4-fix-inline.6

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 (152) hide show
  1. package/dist/package.json +7 -7
  2. package/dist/src/ActionItem/ActionItem.js +1 -1
  3. package/dist/src/Banner/Banner.js +1 -1
  4. package/dist/src/Button/Button.js +2 -2
  5. package/dist/src/Button/Button.style.js +2 -2
  6. package/dist/src/Button/components/InternalButtonLoading/InternalButtonLoading.js +3 -0
  7. package/dist/src/ButtonGroup/ButtonGroup.js +1 -1
  8. package/dist/src/ButtonGroup/ButtonGroup.style.js +1 -1
  9. package/dist/src/Card/Card.js +7 -8
  10. package/dist/src/Disclosure/Disclosure.js +3 -3
  11. package/dist/src/Glimmer/Glimmer.js +42 -0
  12. package/dist/src/Glimmer/Glimmer.shape.style.js +16 -0
  13. package/dist/src/Glimmer/Glimmer.size.style.js +9 -0
  14. package/dist/src/Glimmer/Glimmer.style.js +20 -0
  15. package/dist/src/Glimmer/index.js +1 -0
  16. package/dist/src/InputCurrency/InputCurrency.js +16 -17
  17. package/dist/src/InputFieldWrapper/InputFieldWrapper.js +39 -18
  18. package/dist/src/InputFieldWrapper/InputFieldWrapper.style.js +38 -1
  19. package/dist/src/InputText/InputText.js +6 -3
  20. package/dist/src/Menu/Menu.js +20 -3
  21. package/dist/src/Menu/Menu.style.js +1 -1
  22. package/dist/src/Menu/utils.js +2 -7
  23. package/dist/src/index.js +6 -5
  24. package/dist/tsconfig.tsbuildinfo +1 -1
  25. package/dist/types/src/BottomSheet/components/BottomSheetOption/BottomSheetOption.d.ts +1 -1
  26. package/dist/types/src/ButtonGroup/ButtonGroup.style.d.ts +1 -1
  27. package/dist/types/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.d.ts +6 -6
  28. package/dist/types/src/Checkbox/CheckboxGroup.d.ts +2 -2
  29. package/dist/types/src/Form/components/FormBody/FormBody.d.ts +3 -3
  30. package/dist/types/src/Form/components/FormCache/FormCache.d.ts +4 -4
  31. package/dist/types/src/Form/components/FormMessageBanner/FormMessageBanner.d.ts +1 -1
  32. package/dist/types/src/FormField/FormField.d.ts +2 -2
  33. package/dist/types/src/FormatFile/FormatFile.d.ts +6 -6
  34. package/dist/types/src/FormatFile/components/FileView/FileView.d.ts +6 -6
  35. package/dist/types/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.d.ts +4 -4
  36. package/dist/types/src/FormatFile/components/MediaView/MediaView.d.ts +6 -6
  37. package/dist/types/src/FormatFile/components/ProgressBar/ProgressBar.d.ts +3 -3
  38. package/dist/types/src/Glimmer/Glimmer.d.ts +31 -0
  39. package/dist/types/src/Glimmer/Glimmer.shape.style.d.ts +14 -0
  40. package/dist/types/src/Glimmer/Glimmer.size.style.d.ts +17 -0
  41. package/dist/types/src/Glimmer/Glimmer.style.d.ts +18 -0
  42. package/dist/types/src/Glimmer/index.d.ts +1 -0
  43. package/dist/types/src/InputCurrency/InputCurrency.d.ts +3 -3
  44. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +19 -1
  45. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.style.d.ts +34 -0
  46. package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +12 -12
  47. package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +14 -14
  48. package/dist/types/src/InputPassword/InputPassword.d.ts +1 -1
  49. package/dist/types/src/InputText/InputText.d.ts +6 -2
  50. package/dist/types/src/InputText/context/InputAccessoriesProvider.d.ts +1 -1
  51. package/dist/types/src/Toast/Toast.d.ts +1 -1
  52. package/dist/types/src/index.d.ts +6 -5
  53. package/dist/types/src/utils/test/MockSafeAreaProvider.d.ts +3 -3
  54. package/jestSetup.js +2 -0
  55. package/package.json +7 -7
  56. package/src/ActionItem/ActionItem.test.tsx +2 -2
  57. package/src/ActionItem/ActionItem.tsx +3 -1
  58. package/src/ActionItem/ActionItemGroup.tsx +1 -0
  59. package/src/AutoLink/hooks/useCreateLinkedText.ts +1 -0
  60. package/src/AutoLink/hooks/useTokenGenerator.ts +1 -0
  61. package/src/Banner/Banner.tsx +1 -1
  62. package/src/BottomSheet/BottomSheet.tsx +3 -3
  63. package/src/BottomSheet/components/BottomSheetOption/BottomSheetOption.tsx +3 -1
  64. package/src/Button/Button.style.ts +2 -2
  65. package/src/Button/Button.test.tsx +2 -2
  66. package/src/Button/Button.tsx +2 -2
  67. package/src/Button/components/InternalButtonLoading/InternalButtonLoading.tsx +4 -0
  68. package/src/ButtonGroup/ButtonGroup.style.ts +1 -1
  69. package/src/ButtonGroup/ButtonGroup.tsx +1 -0
  70. package/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.tsx +7 -6
  71. package/src/Card/Card.tsx +31 -32
  72. package/src/Card/components/InternalCardHeader.tsx +1 -0
  73. package/src/Checkbox/CheckboxGroup.tsx +12 -2
  74. package/src/Chip/Chip.tsx +2 -0
  75. package/src/Content/Content.test.tsx +1 -0
  76. package/src/Content/Content.tsx +1 -0
  77. package/src/ContentOverlay/ContentOverlay.test.tsx +1 -0
  78. package/src/Disclosure/Disclosure.tsx +3 -8
  79. package/src/Disclosure/__snapshots__/Disclosure.test.tsx.snap +18 -24
  80. package/src/Divider/Divider.tsx +1 -0
  81. package/src/EmptyState/EmptyState.tsx +2 -2
  82. package/src/Flex/Flex.styles.tsx +1 -0
  83. package/src/Flex/Flex.test.tsx +2 -0
  84. package/src/Form/Form.test.tsx +19 -14
  85. package/src/Form/components/FormBody/FormBody.tsx +4 -3
  86. package/src/Form/components/FormCache/FormCache.tsx +5 -4
  87. package/src/Form/components/FormMessage/FormMessage.tsx +1 -0
  88. package/src/Form/components/FormMessageBanner/FormMessageBanner.tsx +1 -1
  89. package/src/Form/components/FormSaveButton/FormSaveButton.test.tsx +5 -5
  90. package/src/Form/context/AtlantisFormContext.tsx +1 -0
  91. package/src/Form/hooks/useFormViewRefs.ts +1 -0
  92. package/src/Form/hooks/useOfflineHandler.ts +1 -0
  93. package/src/Form/hooks/useScrollToError/useScrollToError.ts +2 -0
  94. package/src/FormField/FormField.test.tsx +6 -8
  95. package/src/FormField/FormField.tsx +2 -2
  96. package/src/FormatFile/FormatFile.tsx +15 -14
  97. package/src/FormatFile/components/FileView/FileView.tsx +9 -6
  98. package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.tsx +4 -4
  99. package/src/FormatFile/components/MediaView/MediaView.tsx +15 -14
  100. package/src/FormatFile/components/ProgressBar/ProgressBar.tsx +3 -3
  101. package/src/FormatFile/utils/createUseCreateThumbnail.ts +1 -0
  102. package/src/Glimmer/Glimmer.shape.style.ts +17 -0
  103. package/src/Glimmer/Glimmer.size.style.ts +10 -0
  104. package/src/Glimmer/Glimmer.style.ts +23 -0
  105. package/src/Glimmer/Glimmer.test.tsx +73 -0
  106. package/src/Glimmer/Glimmer.tsx +106 -0
  107. package/src/Glimmer/index.ts +1 -0
  108. package/src/Heading/__snapshots__/Heading.test.tsx.snap +7 -7
  109. package/src/Icon/Icon.tsx +1 -0
  110. package/src/Icon/__snapshots__/Icon.test.tsx.snap +96 -31
  111. package/src/InputCurrency/InputCurrency.tsx +40 -38
  112. package/src/InputCurrency/utils.ts +9 -0
  113. package/src/InputDate/InputDate.test.tsx +1 -0
  114. package/src/InputFieldWrapper/InputFieldWrapper.style.ts +46 -1
  115. package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +74 -0
  116. package/src/InputFieldWrapper/InputFieldWrapper.tsx +131 -64
  117. package/src/InputFieldWrapper/components/ClearAction/ClearAction.tsx +1 -0
  118. package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +12 -12
  119. package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +14 -14
  120. package/src/InputNumber/InputNumber.tsx +8 -0
  121. package/src/InputPassword/InputPassword.test.tsx +1 -0
  122. package/src/InputPassword/InputPassword.tsx +2 -1
  123. package/src/InputPressable/InputPressable.test.tsx +1 -0
  124. package/src/InputSearch/InputSearch.tsx +1 -0
  125. package/src/InputText/InputText.test.tsx +11 -0
  126. package/src/InputText/InputText.tsx +27 -2
  127. package/src/InputText/context/InputAccessoriesProvider.test.tsx +6 -1
  128. package/src/InputText/context/InputAccessoriesProvider.tsx +1 -1
  129. package/src/InputTime/utils/index.ts +1 -0
  130. package/src/Menu/Menu.style.ts +1 -1
  131. package/src/Menu/Menu.tsx +23 -2
  132. package/src/Menu/components/MenuOption/MenuOption.tsx +1 -0
  133. package/src/Menu/utils.ts +3 -7
  134. package/src/ProgressBar/ProgressBarInner.tsx +1 -0
  135. package/src/ProgressBar/__snapshots__/ProgressBar.test.tsx.snap +4 -4
  136. package/src/Select/components/SelectInternalPicker/SelectInternalPicker.tsx +1 -0
  137. package/src/Select/components/SelectInternalPicker/utils.ts +1 -0
  138. package/src/StatusLabel/StatusLabel.tsx +1 -1
  139. package/src/StatusLabel/__snapshots__/StatusLabel.test.tsx.snap +20 -20
  140. package/src/Switch/components/BaseSwitch/BaseSwitch.tsx +1 -0
  141. package/src/Switch/components/BaseSwitch/__snapshots__/BaseSwitch.test.tsx.snap +18 -18
  142. package/src/Text/Text.tsx +1 -0
  143. package/src/Text/__snapshots__/Text.test.tsx.snap +9 -9
  144. package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +3 -3
  145. package/src/Toast/Toast.tsx +2 -1
  146. package/src/Typography/Typography.tsx +5 -0
  147. package/src/Typography/__snapshots__/Typography.test.tsx.snap +3 -3
  148. package/src/hooks/useAtlantisI18n/utils/dateFormatter.ts +1 -0
  149. package/src/hooks/useFormController.ts +2 -0
  150. package/src/index.ts +6 -5
  151. package/src/utils/intl/capitalize.ts +1 -0
  152. package/src/utils/test/MockSafeAreaProvider.tsx +3 -3
@@ -13,9 +13,11 @@ import { styles } from "./InputFieldWrapper.style";
13
13
  import { PrefixIcon, PrefixLabel } from "./components/Prefix/Prefix";
14
14
  import { SuffixIcon, SuffixLabel } from "./components/Suffix/Suffix";
15
15
  import { ClearAction } from "./components/ClearAction";
16
+ import { Glimmer } from "../Glimmer/Glimmer";
16
17
  import { ErrorMessageWrapper } from "../ErrorMessageWrapper";
17
18
  import { TextVariation, typographyStyles } from "../Typography";
18
19
  import { Text } from "../Text";
20
+ import { ActivityIndicator } from "../ActivityIndicator";
19
21
 
20
22
  export type Clearable = "never" | "while-editing" | "always";
21
23
 
@@ -88,8 +90,33 @@ export interface InputFieldWrapperProps {
88
90
  * Custom styling to override default style of the input field
89
91
  */
90
92
  readonly styleOverride?: InputFieldStyleOverride;
93
+
94
+ /**
95
+ * Add a toolbar below the input field for actions like rewriting the text.
96
+ */
97
+ readonly toolbar?: React.ReactNode;
98
+
99
+ /**
100
+ * Change the behaviour of when the toolbar becomes visible.
101
+ */
102
+ readonly toolbarVisibility?: "always" | "while-editing";
103
+
104
+ /**
105
+ * Show loading indicator.
106
+ */
107
+ readonly loading?: boolean;
108
+
109
+ /**
110
+ * Change the type of loading indicator to spinner or glimmer.
111
+ */
112
+ readonly loadingType?: "spinner" | "glimmer";
91
113
  }
92
114
 
115
+ export const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID =
116
+ "ATL-InputFieldWrapper-Glimmers";
117
+ export const INPUT_FIELD_WRAPPER_SPINNER_TEST_ID =
118
+ "ATL-InputFieldWrapper-Spinner";
119
+
93
120
  export function InputFieldWrapper({
94
121
  invalid,
95
122
  disabled,
@@ -105,11 +132,20 @@ export function InputFieldWrapper({
105
132
  onClear,
106
133
  showClearAction = false,
107
134
  styleOverride,
135
+ toolbar,
136
+ toolbarVisibility = "while-editing",
137
+ loading = false,
138
+ loadingType = "spinner",
108
139
  }: InputFieldWrapperProps): JSX.Element {
109
140
  fieldAffixRequiredPropsCheck([prefix, suffix]);
110
141
  const handleClear = onClear ?? noopClear;
111
142
  warnIfClearActionWithNoOnClear(onClear, showClearAction);
112
143
  const inputInvalid = Boolean(invalid) || Boolean(error);
144
+ const isToolbarVisible =
145
+ toolbar && (toolbarVisibility === "always" || focused);
146
+
147
+ const showLoadingSpinner = loading && loadingType === "spinner";
148
+ const showLoadingGlimmer = loading && loadingType === "glimmer";
113
149
 
114
150
  return (
115
151
  <ErrorMessageWrapper message={getMessage({ invalid, error })}>
@@ -123,78 +159,109 @@ export function InputFieldWrapper({
123
159
  styleOverride?.container,
124
160
  ]}
125
161
  >
126
- {prefix?.icon && (
127
- <PrefixIcon
128
- disabled={disabled}
129
- focused={focused}
130
- hasMiniLabel={hasMiniLabel}
131
- inputInvalid={inputInvalid}
132
- icon={prefix.icon}
133
- />
134
- )}
135
- <View style={[styles.inputContainer]}>
136
- <View
137
- style={[
138
- !!placeholder && styles.label,
139
- hasMiniLabel && styles.miniLabel,
140
- disabled && styles.disabled,
141
- hasMiniLabel &&
142
- showClearAction &&
143
- styles.miniLabelShowClearAction,
144
- ]}
145
- pointerEvents="none"
146
- >
147
- <Placeholder
148
- placeholder={placeholder}
149
- labelVariation={getLabelVariation(error, invalid, disabled)}
150
- hasMiniLabel={hasMiniLabel}
151
- styleOverride={styleOverride?.placeholderText}
152
- />
153
- </View>
154
- {prefix?.label && hasValue && (
155
- <PrefixLabel
162
+ <View style={styles.field}>
163
+ {prefix?.icon && (
164
+ <PrefixIcon
156
165
  disabled={disabled}
157
166
  focused={focused}
158
167
  hasMiniLabel={hasMiniLabel}
159
168
  inputInvalid={inputInvalid}
160
- label={prefix.label}
161
- styleOverride={styleOverride?.prefixLabel}
169
+ icon={prefix.icon}
162
170
  />
163
171
  )}
164
- {children}
165
- {(showClearAction || suffix?.label || suffix?.icon) && (
166
- <View style={styles.inputEndContainer}>
167
- {showClearAction && (
168
- <ClearAction
169
- hasMarginRight={!!suffix?.icon || !!suffix?.label}
170
- onPress={handleClear}
171
- />
172
- )}
173
- {suffix?.label && hasValue && (
174
- <SuffixLabel
175
- disabled={disabled}
176
- focused={focused}
177
- hasMiniLabel={hasMiniLabel}
178
- inputInvalid={inputInvalid}
179
- label={suffix.label}
180
- hasLeftMargin={!showClearAction}
181
- styleOverride={styleOverride?.suffixLabel}
182
- />
183
- )}
184
- {suffix?.icon && (
185
- <SuffixIcon
186
- disabled={disabled}
187
- focused={focused}
188
- hasMiniLabel={hasMiniLabel}
189
- hasLeftMargin={!!(!showClearAction || suffix?.label)}
190
- inputInvalid={inputInvalid}
191
- icon={suffix.icon}
192
- onPress={suffix.onPress}
193
- />
194
- )}
172
+ <View style={[styles.inputContainer]}>
173
+ <View
174
+ style={[
175
+ !!placeholder && styles.label,
176
+ hasMiniLabel && styles.miniLabel,
177
+ disabled && styles.disabled,
178
+ hasMiniLabel &&
179
+ showClearAction &&
180
+ styles.miniLabelShowClearAction,
181
+ ]}
182
+ pointerEvents="none"
183
+ >
184
+ <Placeholder
185
+ placeholder={placeholder}
186
+ labelVariation={getLabelVariation(error, invalid, disabled)}
187
+ hasMiniLabel={hasMiniLabel}
188
+ styleOverride={styleOverride?.placeholderText}
189
+ />
195
190
  </View>
196
- )}
191
+ {prefix?.label && hasValue && (
192
+ <PrefixLabel
193
+ disabled={disabled}
194
+ focused={focused}
195
+ hasMiniLabel={hasMiniLabel}
196
+ inputInvalid={inputInvalid}
197
+ label={prefix.label}
198
+ styleOverride={styleOverride?.prefixLabel}
199
+ />
200
+ )}
201
+ {children}
202
+
203
+ {showLoadingGlimmer && (
204
+ <View
205
+ testID={INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID}
206
+ style={[
207
+ styles.loadingGlimmers,
208
+ hasValue && styles.loadingGlimmersHasValue,
209
+ ]}
210
+ >
211
+ <Glimmer size="small" width="80%" />
212
+ <Glimmer size="small" />
213
+ <Glimmer size="small" width="70%" />
214
+ </View>
215
+ )}
216
+
217
+ {(showClearAction ||
218
+ suffix?.label ||
219
+ suffix?.icon ||
220
+ showLoadingSpinner) && (
221
+ <View style={styles.inputEndContainer}>
222
+ {showClearAction && (
223
+ <ClearAction
224
+ hasMarginRight={!!suffix?.icon || !!suffix?.label}
225
+ onPress={handleClear}
226
+ />
227
+ )}
228
+ {suffix?.label && hasValue && (
229
+ <SuffixLabel
230
+ disabled={disabled}
231
+ focused={focused}
232
+ hasMiniLabel={hasMiniLabel}
233
+ inputInvalid={inputInvalid}
234
+ label={suffix.label}
235
+ hasLeftMargin={!showClearAction}
236
+ styleOverride={styleOverride?.suffixLabel}
237
+ />
238
+ )}
239
+
240
+ {showLoadingSpinner && (
241
+ <View style={styles.loadingSpinner}>
242
+ <ActivityIndicator
243
+ testID={INPUT_FIELD_WRAPPER_SPINNER_TEST_ID}
244
+ />
245
+ </View>
246
+ )}
247
+
248
+ {suffix?.icon && (
249
+ <SuffixIcon
250
+ disabled={disabled}
251
+ focused={focused}
252
+ hasMiniLabel={hasMiniLabel}
253
+ hasLeftMargin={!!(!showClearAction || suffix?.label)}
254
+ inputInvalid={inputInvalid}
255
+ icon={suffix.icon}
256
+ onPress={suffix.onPress}
257
+ />
258
+ )}
259
+ </View>
260
+ )}
261
+ </View>
197
262
  </View>
263
+
264
+ {isToolbarVisible && <View style={styles.toolbar}>{toolbar}</View>}
198
265
  </View>
199
266
  {assistiveText && !error && !invalid && (
200
267
  <Text
@@ -17,6 +17,7 @@ export function ClearAction({
17
17
  hasMarginRight = false,
18
18
  }: ClearActionProps): JSX.Element {
19
19
  const { t } = useAtlantisI18n();
20
+
20
21
  return (
21
22
  <Pressable
22
23
  style={[styles.container, hasMarginRight && styles.addedMargin]}
@@ -15,12 +15,12 @@ import { typographyStyles } from "../../../Typography";
15
15
  import { styles } from "../../InputFieldWrapper.style";
16
16
 
17
17
  export interface PrefixLabelProps {
18
- focused: boolean;
19
- disabled?: boolean;
20
- hasMiniLabel: boolean;
21
- inputInvalid: boolean;
22
- label: string;
23
- styleOverride?: StyleProp<TextStyle>;
18
+ readonly focused: boolean;
19
+ readonly disabled?: boolean;
20
+ readonly hasMiniLabel: boolean;
21
+ readonly inputInvalid: boolean;
22
+ readonly label: string;
23
+ readonly styleOverride?: StyleProp<TextStyle>;
24
24
  }
25
25
 
26
26
  export const prefixLabelTestId = "ATL-InputFieldWrapper-PrefixLabel";
@@ -68,12 +68,12 @@ export function PrefixLabel({
68
68
  }
69
69
 
70
70
  export interface PrefixIconProps {
71
- focused: boolean;
72
- disabled?: boolean;
73
- hasMiniLabel: boolean;
74
- inputInvalid?: boolean;
75
- icon: IconNames;
76
- styleOverride?: StyleProp<ViewStyle>;
71
+ readonly focused: boolean;
72
+ readonly disabled?: boolean;
73
+ readonly hasMiniLabel: boolean;
74
+ readonly inputInvalid?: boolean;
75
+ readonly icon: IconNames;
76
+ readonly styleOverride?: StyleProp<ViewStyle>;
77
77
  }
78
78
 
79
79
  export function PrefixIcon({
@@ -15,13 +15,13 @@ import { typographyStyles } from "../../../Typography";
15
15
  import { styles } from "../../InputFieldWrapper.style";
16
16
 
17
17
  export interface SuffixLabelProps {
18
- focused: boolean;
19
- disabled?: boolean;
20
- hasMiniLabel: boolean;
21
- inputInvalid?: boolean;
22
- label: string;
23
- hasLeftMargin?: boolean;
24
- styleOverride?: StyleProp<TextStyle>;
18
+ readonly focused: boolean;
19
+ readonly disabled?: boolean;
20
+ readonly hasMiniLabel: boolean;
21
+ readonly inputInvalid?: boolean;
22
+ readonly label: string;
23
+ readonly hasLeftMargin?: boolean;
24
+ readonly styleOverride?: StyleProp<TextStyle>;
25
25
  }
26
26
 
27
27
  export const suffixLabelTestId = "ATL-InputFieldWrapper-SuffixLabel";
@@ -71,13 +71,13 @@ export function SuffixLabel({
71
71
  }
72
72
 
73
73
  export interface SuffixIconProps {
74
- focused: boolean;
75
- disabled?: boolean;
76
- hasMiniLabel: boolean;
77
- inputInvalid?: boolean;
78
- icon: IconNames;
79
- hasLeftMargin?: boolean;
80
- onPress?: () => void;
74
+ readonly focused: boolean;
75
+ readonly disabled?: boolean;
76
+ readonly hasMiniLabel: boolean;
77
+ readonly inputInvalid?: boolean;
78
+ readonly icon: IconNames;
79
+ readonly hasLeftMargin?: boolean;
80
+ readonly onPress?: () => void;
81
81
  }
82
82
 
83
83
  export function SuffixIcon({
@@ -36,12 +36,14 @@ function InputNumberInternal(props: InputNumberProps, ref: Ref<InputTextRef>) {
36
36
  }
37
37
  };
38
38
  const { t } = useAtlantisI18n();
39
+
39
40
  const handleChange = (newValue: number | string | undefined) => {
40
41
  props.onChange?.(newValue);
41
42
  };
42
43
 
43
44
  const { inputTransform: convertToString, outputTransform: convertToNumber } =
44
45
  useNumberTransform(props.value);
46
+
45
47
  return (
46
48
  <InputText
47
49
  {...props}
@@ -69,10 +71,12 @@ function hasPeriodAtEnd(value: string) {
69
71
  // matches patterns like ".", "0.", "12.", "+1.", and "-0."
70
72
  return !!value?.match(/^[-+]?[0-9]*\.$/);
71
73
  }
74
+
72
75
  function hasScientificNotationAtEnd(value: string) {
73
76
  // matches patterns like "1e", "+2e", "1.2e" and "-3e"
74
77
  return !!value?.match(/^[-+]?[0-9]+(\.?[0-9]+)?e$/);
75
78
  }
79
+
76
80
  function hasPlusMinusAtEnd(value: string) {
77
81
  // matches "+" and "-"
78
82
  return !!value?.match(/^[-+]+$/);
@@ -92,6 +96,7 @@ export function shouldShowUserValue(value: string): boolean {
92
96
  ];
93
97
  const isSpecial = (v: string) =>
94
98
  specialCasesFn.reduce((acc, fn) => acc || fn(v), false);
99
+
95
100
  return isSpecial(value);
96
101
  }
97
102
 
@@ -105,9 +110,11 @@ export function useNumberTransform(controlledValue: number | undefined): {
105
110
 
106
111
  const convertToNumber = (newValue: string) => {
107
112
  setTypedValue(newValue);
113
+
108
114
  if (newValue?.match?.(NUMBER_VALIDATION_REGEX)) {
109
115
  return parseFloat(newValue);
110
116
  }
117
+
111
118
  return newValue;
112
119
  };
113
120
 
@@ -115,6 +122,7 @@ export function useNumberTransform(controlledValue: number | undefined): {
115
122
  if (shouldShowUserValue(typedValue)) {
116
123
  return typedValue;
117
124
  }
125
+
118
126
  return internalValue?.toString() || undefined;
119
127
  };
120
128
 
@@ -8,6 +8,7 @@ jest.mock("../InputFieldWrapper", () => ({
8
8
  ...jest.requireActual("../InputFieldWrapper"),
9
9
  InputFieldWrapper: function Mock(props: InputFieldWrapperProps) {
10
10
  MockInputFieldWrapper(props);
11
+
11
12
  return jest.requireActual("../InputFieldWrapper").InputFieldWrapper(props);
12
13
  },
13
14
  }));
@@ -15,7 +15,7 @@ export interface InputPasswordProps
15
15
  *
16
16
  * @default true
17
17
  */
18
- usePrivacyEye?: boolean;
18
+ readonly usePrivacyEye?: boolean;
19
19
  }
20
20
 
21
21
  function InputPasswordInternal(
@@ -43,6 +43,7 @@ function InputPasswordInternal(
43
43
  onPress: handleOnPress,
44
44
  };
45
45
  }
46
+
46
47
  return undefined;
47
48
  };
48
49
 
@@ -8,6 +8,7 @@ jest.mock("../InputFieldWrapper", () => ({
8
8
  ...jest.requireActual("../InputFieldWrapper"),
9
9
  InputFieldWrapper: function Mock(props: InputFieldWrapperProps) {
10
10
  MockInputFieldWrapper(props);
11
+
11
12
  return jest.requireActual("../InputFieldWrapper").InputFieldWrapper(props);
12
13
  },
13
14
  }));
@@ -54,6 +54,7 @@ function SearchInputInternal(
54
54
 
55
55
  useEffect(() => {
56
56
  delayedSearch(value);
57
+
57
58
  return delayedSearch.cancel;
58
59
  }, [value, delayedSearch]);
59
60
 
@@ -75,6 +75,17 @@ describe("InputText", () => {
75
75
  );
76
76
  });
77
77
 
78
+ describe("readonly", () => {
79
+ it.each([[false], [true]])("sets the readOnly to %s", expected => {
80
+ const { getByTestId } = renderInputText({
81
+ testID: "InputText",
82
+ readonly: expected,
83
+ });
84
+
85
+ expect(getByTestId("InputText").props.readOnly).toBe(expected);
86
+ });
87
+ });
88
+
78
89
  it("renders a InputText with placeholder", () => {
79
90
  const props = { placeholder: "Foobar" };
80
91
  renderInputText(props);
@@ -25,11 +25,18 @@ import { Clearable, useShowClear } from "@jobber/hooks";
25
25
  import { styles } from "./InputText.style";
26
26
  import { useInputAccessoriesContext } from "./context";
27
27
  import { useFormController } from "../hooks";
28
- import { InputFieldStyleOverride } from "../InputFieldWrapper/InputFieldWrapper";
28
+ import {
29
+ InputFieldStyleOverride,
30
+ InputFieldWrapperProps,
31
+ } from "../InputFieldWrapper/InputFieldWrapper";
29
32
  import { InputFieldWrapper } from "../InputFieldWrapper";
30
33
  import { commonInputStyles } from "../InputFieldWrapper/CommonInputStyles.style";
31
34
 
32
- export interface InputTextProps {
35
+ export interface InputTextProps
36
+ extends Pick<
37
+ InputFieldWrapperProps,
38
+ "toolbar" | "toolbarVisibility" | "loading" | "loadingType"
39
+ > {
33
40
  /**
34
41
  * Highlights the field red and shows message below (if string) to indicate an error
35
42
  */
@@ -40,6 +47,11 @@ export interface InputTextProps {
40
47
  */
41
48
  readonly disabled?: boolean;
42
49
 
50
+ /**
51
+ * Makes the input read-only
52
+ */
53
+ readonly readonly?: boolean;
54
+
43
55
  /**
44
56
  * Name of the input.
45
57
  */
@@ -233,6 +245,7 @@ function InputTextInternal(
233
245
  {
234
246
  invalid,
235
247
  disabled,
248
+ readonly = false,
236
249
  name,
237
250
  placeholder,
238
251
  assistiveText,
@@ -260,6 +273,10 @@ function InputTextInternal(
260
273
  testID,
261
274
  secureTextEntry,
262
275
  styleOverride,
276
+ toolbar,
277
+ toolbarVisibility,
278
+ loading,
279
+ loadingType,
263
280
  }: InputTextProps,
264
281
  ref: Ref<InputTextRef>,
265
282
  ) {
@@ -361,6 +378,10 @@ function InputTextInternal(
361
378
  onClear={handleClear}
362
379
  showClearAction={showClear}
363
380
  styleOverride={styleOverride}
381
+ toolbar={toolbar}
382
+ toolbarVisibility={toolbarVisibility}
383
+ loading={loading}
384
+ loadingType={loadingType}
364
385
  >
365
386
  <TextInput
366
387
  inputAccessoryViewID={inputAccessoryID || undefined}
@@ -377,7 +398,10 @@ function InputTextInternal(
377
398
  multiline && Platform.OS === "ios" && styles.multilineInputiOS,
378
399
  multiline && hasMiniLabel && styles.multiLineInputWithMini,
379
400
  styleOverride?.inputText,
401
+ loading && loadingType === "glimmer" && { color: "transparent" },
380
402
  ]}
403
+ // @ts-expect-error - does exist on 0.71 and up https://github.com/facebook/react-native/pull/39281
404
+ readOnly={readonly}
381
405
  editable={!disabled}
382
406
  keyboardType={keyboard}
383
407
  value={inputTransform(internalValue)}
@@ -392,6 +416,7 @@ function InputTextInternal(
392
416
  blurOnSubmit={shouldBlurOnSubmit}
393
417
  accessibilityLabel={accessibilityLabel || placeholder}
394
418
  accessibilityHint={accessibilityHint}
419
+ accessibilityState={{ busy: loading }}
395
420
  secureTextEntry={secureTextEntry}
396
421
  {...androidA11yProps}
397
422
  onFocus={event => {
@@ -31,7 +31,7 @@ describe("InputAccessories", () => {
31
31
  const mockInputOneFocus = jest.fn();
32
32
  const mockInputTwoFocus = jest.fn();
33
33
 
34
- function InputWrapper({ focusedInput }: { focusedInput: string }) {
34
+ function InputWrapper({ focusedInput }: { readonly focusedInput: string }) {
35
35
  const { register, setFocusedInput, unregister } = useContext(
36
36
  InputAccessoriesContext,
37
37
  );
@@ -40,12 +40,14 @@ describe("InputAccessories", () => {
40
40
  register(inputOneName, () => mockInputOneFocus());
41
41
  register(inputTwoName, () => mockInputTwoFocus());
42
42
  setFocusedInput(focusedInput);
43
+
43
44
  return () => {
44
45
  unregister(inputOneName);
45
46
  unregister(inputTwoName);
46
47
  setFocusedInput("");
47
48
  };
48
49
  }, [register, setFocusedInput, unregister, focusedInput]);
50
+
49
51
  return (
50
52
  <>
51
53
  <InputText
@@ -58,6 +60,7 @@ describe("InputAccessories", () => {
58
60
  </>
59
61
  );
60
62
  }
63
+
61
64
  function SetupInputAccessoriesTest(focusedInput: string) {
62
65
  mockUseFormController.mockImplementation(({ name, value, validations }) => {
63
66
  const { field, error } = actualUseFormController({
@@ -65,8 +68,10 @@ describe("InputAccessories", () => {
65
68
  value,
66
69
  validations,
67
70
  });
71
+
68
72
  return { field, error };
69
73
  });
74
+
70
75
  return render(<InputWrapper focusedInput={focusedInput} />, {
71
76
  wrapper: InputAccessoriesProvider,
72
77
  });
@@ -15,7 +15,7 @@ import { styles } from "./InputAccessoriesProvider.style";
15
15
  export function InputAccessoriesProvider({
16
16
  children,
17
17
  }: {
18
- children: ReactNode;
18
+ readonly children: ReactNode;
19
19
  }): JSX.Element {
20
20
  const inputAccessoryID = useRef(v4()).current;
21
21
  const {
@@ -15,6 +15,7 @@ export function roundUpToNearestMinutes(
15
15
  minutes: MinutesIncrement,
16
16
  ): Date {
17
17
  const ms = 1000 * 60 * minutes;
18
+
18
19
  return new Date(Math.ceil(date.getTime() / ms) * ms);
19
20
  }
20
21
 
@@ -9,7 +9,7 @@ export const styles = StyleSheet.create({
9
9
  backgroundColor: tokens["color-surface"],
10
10
  paddingHorizontal: tokens["space-small"],
11
11
  paddingVertical: tokens["space-small"] + tokens["space-smallest"],
12
- borderRadius: tokens["radius-larger"],
12
+ borderRadius: tokens["radius-base"],
13
13
  width: menuWidth,
14
14
  ...tokens["shadow-high"],
15
15
  },
package/src/Menu/Menu.tsx CHANGED
@@ -1,6 +1,8 @@
1
1
  import React, { useCallback, useRef, useState } from "react";
2
2
  import {
3
+ Keyboard,
3
4
  LayoutRectangle,
5
+ Platform,
4
6
  Pressable,
5
7
  View,
6
8
  useWindowDimensions,
@@ -33,7 +35,7 @@ export function Menu({ menuOptions, customActivator }: MenuProps): JSX.Element {
33
35
  }
34
36
  }, [screenInfo, activatorLayout]);
35
37
 
36
- const activatorOnPress = (onPress?: () => void) => {
38
+ const openMenu = () => {
37
39
  menuButtonRef.current?.measureInWindow(
38
40
  (x: number, y: number, width: number, height: number) => {
39
41
  activatorLayout.current = {
@@ -44,11 +46,23 @@ export function Menu({ menuOptions, customActivator }: MenuProps): JSX.Element {
44
46
  };
45
47
  findMenuLayout();
46
48
  setOpen(true);
47
- onPress && onPress();
48
49
  },
49
50
  );
50
51
  };
51
52
 
53
+ const activatorOnPress = (onPress?: () => void) => {
54
+ onPress && onPress();
55
+
56
+ if (Platform.OS === "ios" && Keyboard.isVisible()) {
57
+ // On iOS, the keyboard height causes problems with the menu positioning logic.
58
+ // Wait until the keyboard is fully hidden before we show the menu.
59
+ onKeyboardDidHide(openMenu);
60
+ Keyboard.dismiss();
61
+ } else {
62
+ openMenu();
63
+ }
64
+ };
65
+
52
66
  return (
53
67
  <>
54
68
  <View
@@ -113,3 +127,10 @@ function useScreenInformation() {
113
127
 
114
128
  return { headerHeight, windowWidth, windowHeight };
115
129
  }
130
+
131
+ function onKeyboardDidHide(callback: () => void) {
132
+ const listener = Keyboard.addListener("keyboardDidHide", () => {
133
+ listener.remove();
134
+ callback();
135
+ });
136
+ }
@@ -20,6 +20,7 @@ export function MenuOption({
20
20
  }: MenuOptionInternalProps): JSX.Element {
21
21
  const destructiveColor = "critical";
22
22
  const textVariation = destructive ? destructiveColor : "heading";
23
+
23
24
  return (
24
25
  <View testID="ATL-MENU-OPTIONS">
25
26
  <Pressable