@jobber/components-native 0.44.2 → 0.45.1

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 (159) hide show
  1. package/dist/package.json +88 -0
  2. package/dist/src/AtlantisContext/AtlantisContext.js +1 -0
  3. package/dist/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.js +3 -3
  4. package/dist/src/AutoLink/utils.js +14 -3
  5. package/dist/src/BottomSheet/BottomSheet.js +3 -4
  6. package/dist/src/ButtonGroup/ButtonGroup.js +3 -4
  7. package/dist/src/ButtonGroup/utils.js +4 -5
  8. package/dist/src/ContentOverlay/ContentOverlay.js +3 -4
  9. package/dist/src/Form/components/FormErrorBanner/FormErrorBanner.js +5 -13
  10. package/dist/src/Form/components/FormMask/FormMask.js +3 -4
  11. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.js +4 -5
  12. package/dist/src/Form/components/FormSaveButton/FormSaveButton.js +4 -5
  13. package/dist/src/Form/hooks/useOfflineHandler.js +6 -7
  14. package/dist/src/FormatFile/FormatFile.js +3 -4
  15. package/dist/src/FormatFile/components/FileView/FileView.js +3 -3
  16. package/dist/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.js +6 -7
  17. package/dist/src/FormatFile/components/MediaView/MediaView.js +3 -3
  18. package/dist/src/FormatFile/utils/computeA11yLabel.js +4 -5
  19. package/dist/src/InputCurrency/InputCurrency.js +3 -3
  20. package/dist/src/InputDate/InputDate.js +4 -5
  21. package/dist/src/InputFieldWrapper/components/ClearAction/ClearAction.js +3 -4
  22. package/dist/src/InputFieldWrapper/components/ClearAction/index.js +0 -1
  23. package/dist/src/InputNumber/InputNumber.js +3 -4
  24. package/dist/src/InputPassword/InputPassword.js +3 -4
  25. package/dist/src/InputTime/InputTime.js +3 -4
  26. package/dist/src/Menu/Menu.js +3 -4
  27. package/dist/src/ProgressBar/ProgressBar.js +14 -8
  28. package/dist/src/Select/Select.js +5 -6
  29. package/dist/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.js +3 -4
  30. package/dist/src/Toast/Toast.js +4 -5
  31. package/dist/src/hooks/useAtlantisI18n/index.js +1 -0
  32. package/dist/src/hooks/useAtlantisI18n/locales/en.json +35 -0
  33. package/dist/src/hooks/useAtlantisI18n/locales/es.json +35 -0
  34. package/dist/src/hooks/useAtlantisI18n/useAtlantisI18n.js +22 -0
  35. package/dist/tsconfig.json +38 -0
  36. package/dist/tsconfig.tsbuildinfo +1 -1
  37. package/dist/types/src/AtlantisContext/AtlantisContext.d.ts +8 -0
  38. package/dist/types/src/AutoLink/utils.d.ts +2 -2
  39. package/dist/types/src/FormatFile/utils/computeA11yLabel.d.ts +6 -6
  40. package/dist/types/src/InputFieldWrapper/components/ClearAction/index.d.ts +0 -1
  41. package/dist/types/src/hooks/useAtlantisI18n/index.d.ts +1 -0
  42. package/dist/types/src/hooks/useAtlantisI18n/useAtlantisI18n.d.ts +7 -0
  43. package/package.json +2 -2
  44. package/src/AtlantisContext/AtlantisContext.tsx +10 -0
  45. package/src/AutoLink/AutoLink.test.tsx +3 -4
  46. package/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.tsx +3 -3
  47. package/src/AutoLink/utils.ts +16 -5
  48. package/src/BottomSheet/BottomSheet.test.tsx +3 -4
  49. package/src/BottomSheet/BottomSheet.tsx +3 -4
  50. package/src/ButtonGroup/ButtonGroup.test.tsx +8 -9
  51. package/src/ButtonGroup/ButtonGroup.tsx +3 -4
  52. package/src/ButtonGroup/utils.ts +5 -6
  53. package/src/ContentOverlay/ContentOverlay.test.tsx +1 -11
  54. package/src/ContentOverlay/ContentOverlay.tsx +5 -9
  55. package/src/Form/Form.test.tsx +9 -40
  56. package/src/Form/components/FormErrorBanner/FormErrorBanner.test.tsx +9 -72
  57. package/src/Form/components/FormErrorBanner/FormErrorBanner.tsx +6 -15
  58. package/src/Form/components/FormMask/FormMask.tsx +3 -7
  59. package/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.tsx +4 -5
  60. package/src/Form/components/FormSaveButton/FormSaveButton.test.tsx +3 -7
  61. package/src/Form/components/FormSaveButton/FormSaveButton.tsx +4 -5
  62. package/src/Form/hooks/useOfflineHandler.ts +7 -8
  63. package/src/FormatFile/FormatFile.test.tsx +7 -31
  64. package/src/FormatFile/FormatFile.tsx +3 -7
  65. package/src/FormatFile/components/FileView/FileView.tsx +3 -3
  66. package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.test.tsx +2 -9
  67. package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.tsx +6 -7
  68. package/src/FormatFile/components/MediaView/MediaView.tsx +3 -3
  69. package/src/FormatFile/utils/computeA11yLabel.ts +9 -12
  70. package/src/InputCurrency/InputCurrency.test.tsx +6 -1
  71. package/src/InputCurrency/InputCurrency.tsx +3 -3
  72. package/src/InputDate/InputDate.tsx +6 -5
  73. package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +4 -15
  74. package/src/InputFieldWrapper/components/ClearAction/ClearAction.test.tsx +1 -5
  75. package/src/InputFieldWrapper/components/ClearAction/ClearAction.tsx +3 -4
  76. package/src/InputFieldWrapper/components/ClearAction/index.ts +0 -1
  77. package/src/InputNumber/InputNumber.test.tsx +10 -18
  78. package/src/InputNumber/InputNumber.tsx +3 -4
  79. package/src/InputPassword/InputPassword.test.tsx +1 -2
  80. package/src/InputPassword/InputPassword.tsx +3 -4
  81. package/src/InputSearch/InputSearch.test.tsx +1 -6
  82. package/src/InputText/InputText.test.tsx +10 -38
  83. package/src/InputTime/InputTime.tsx +3 -4
  84. package/src/Menu/Menu.test.tsx +10 -9
  85. package/src/Menu/Menu.tsx +3 -4
  86. package/src/ProgressBar/ProgressBar.tsx +17 -8
  87. package/src/Select/Select.test.tsx +4 -5
  88. package/src/Select/Select.tsx +5 -8
  89. package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.tsx +3 -4
  90. package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.test.tsx +1 -2
  91. package/src/Toast/Toast.tsx +4 -9
  92. package/src/hooks/useAtlantisI18n/index.ts +1 -0
  93. package/src/hooks/useAtlantisI18n/locales/en.json +35 -0
  94. package/src/hooks/useAtlantisI18n/locales/es.json +35 -0
  95. package/src/hooks/useAtlantisI18n/useAtlantisI18n.test.ts +53 -0
  96. package/src/hooks/useAtlantisI18n/useAtlantisI18n.ts +42 -0
  97. package/dist/src/AutoLink/messages.js +0 -18
  98. package/dist/src/BottomSheet/messages.js +0 -8
  99. package/dist/src/ButtonGroup/messages.js +0 -18
  100. package/dist/src/ContentOverlay/messages.js +0 -8
  101. package/dist/src/Form/components/FormErrorBanner/messages.js +0 -13
  102. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/messages.js +0 -8
  103. package/dist/src/Form/components/FormSaveButton/messages.js +0 -8
  104. package/dist/src/Form/messages.js +0 -28
  105. package/dist/src/FormatFile/components/FormatFileBottomSheet/messages.js +0 -13
  106. package/dist/src/FormatFile/messages.js +0 -23
  107. package/dist/src/InputCurrency/messages.js +0 -8
  108. package/dist/src/InputDate/messages.js +0 -8
  109. package/dist/src/InputFieldWrapper/components/ClearAction/messages.js +0 -8
  110. package/dist/src/InputNumber/messages.js +0 -8
  111. package/dist/src/InputPassword/messages.js +0 -8
  112. package/dist/src/InputTime/messages.js +0 -8
  113. package/dist/src/Menu/messages.js +0 -8
  114. package/dist/src/ProgressBar/messages.js +0 -13
  115. package/dist/src/Select/components/SelectDefaultPicker/messages.js +0 -8
  116. package/dist/src/Select/messages.js +0 -13
  117. package/dist/src/Toast/messages.js +0 -13
  118. package/dist/types/src/AutoLink/messages.d.ts +0 -17
  119. package/dist/types/src/BottomSheet/messages.d.ts +0 -7
  120. package/dist/types/src/ButtonGroup/messages.d.ts +0 -17
  121. package/dist/types/src/ContentOverlay/messages.d.ts +0 -7
  122. package/dist/types/src/Form/components/FormErrorBanner/messages.d.ts +0 -12
  123. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/messages.d.ts +0 -7
  124. package/dist/types/src/Form/components/FormSaveButton/messages.d.ts +0 -7
  125. package/dist/types/src/Form/messages.d.ts +0 -27
  126. package/dist/types/src/FormatFile/components/FormatFileBottomSheet/messages.d.ts +0 -12
  127. package/dist/types/src/FormatFile/messages.d.ts +0 -22
  128. package/dist/types/src/InputCurrency/messages.d.ts +0 -7
  129. package/dist/types/src/InputDate/messages.d.ts +0 -7
  130. package/dist/types/src/InputFieldWrapper/components/ClearAction/messages.d.ts +0 -7
  131. package/dist/types/src/InputNumber/messages.d.ts +0 -7
  132. package/dist/types/src/InputPassword/messages.d.ts +0 -7
  133. package/dist/types/src/InputTime/messages.d.ts +0 -7
  134. package/dist/types/src/Menu/messages.d.ts +0 -7
  135. package/dist/types/src/ProgressBar/messages.d.ts +0 -12
  136. package/dist/types/src/Select/components/SelectDefaultPicker/messages.d.ts +0 -7
  137. package/dist/types/src/Select/messages.d.ts +0 -12
  138. package/dist/types/src/Toast/messages.d.ts +0 -12
  139. package/src/AutoLink/messages.ts +0 -19
  140. package/src/BottomSheet/messages.ts +0 -9
  141. package/src/ButtonGroup/messages.ts +0 -19
  142. package/src/ContentOverlay/messages.ts +0 -9
  143. package/src/Form/components/FormErrorBanner/messages.ts +0 -14
  144. package/src/Form/components/FormMessage/components/InternalFormMessage/messages.ts +0 -10
  145. package/src/Form/components/FormSaveButton/messages.ts +0 -9
  146. package/src/Form/messages.ts +0 -33
  147. package/src/FormatFile/components/FormatFileBottomSheet/messages.ts +0 -14
  148. package/src/FormatFile/messages.ts +0 -24
  149. package/src/InputCurrency/messages.ts +0 -10
  150. package/src/InputDate/messages.ts +0 -9
  151. package/src/InputFieldWrapper/components/ClearAction/messages.ts +0 -9
  152. package/src/InputNumber/messages.ts +0 -10
  153. package/src/InputPassword/messages.ts +0 -9
  154. package/src/InputTime/messages.ts +0 -9
  155. package/src/Menu/messages.ts +0 -9
  156. package/src/ProgressBar/messages.ts +0 -14
  157. package/src/Select/components/SelectDefaultPicker/messages.ts +0 -9
  158. package/src/Select/messages.ts +0 -14
  159. package/src/Toast/messages.ts +0 -14
@@ -4,7 +4,6 @@ import { Host } from "react-native-portalize";
4
4
  import { View } from "react-native";
5
5
  import { tokens } from "@jobber/design/foundation";
6
6
  import { Menu, MenuOptionProps, MenuProps } from ".";
7
- import { messages } from "./messages";
8
7
  import { Icon } from "../Icon";
9
8
  import { Button } from "../Button";
10
9
 
@@ -24,6 +23,8 @@ const setup = (props?: MenuProps) => {
24
23
  );
25
24
  };
26
25
 
26
+ const menuLabel = "Menu";
27
+
27
28
  describe("Menu", () => {
28
29
  beforeEach(() => {
29
30
  mockOnPress.mockClear();
@@ -35,7 +36,7 @@ describe("Menu", () => {
35
36
  });
36
37
 
37
38
  expect(getByTestId("more")).toBeDefined();
38
- expect(getByLabelText(messages.more.defaultMessage)).toBeDefined();
39
+ expect(getByLabelText(menuLabel)).toBeDefined();
39
40
  });
40
41
 
41
42
  it("renders every menu option when menu is opened", () => {
@@ -48,7 +49,7 @@ describe("Menu", () => {
48
49
  menuOptions,
49
50
  });
50
51
 
51
- fireEvent.press(getByLabelText(messages.more.defaultMessage));
52
+ fireEvent.press(getByLabelText(menuLabel));
52
53
  expect(getByLabelText(menuOptions[0].label)).toBeDefined();
53
54
  expect(getByLabelText(menuOptions[1].label)).toBeDefined();
54
55
  expect(getByLabelText(menuOptions[2].label)).toBeDefined();
@@ -116,7 +117,7 @@ describe("Menu", () => {
116
117
  ],
117
118
  });
118
119
 
119
- fireEvent.press(getByLabelText(messages.more.defaultMessage));
120
+ fireEvent.press(getByLabelText(menuLabel));
120
121
  fireEvent.press(getByLabelText("hi"));
121
122
  expect(mockOnPress).toHaveBeenCalled();
122
123
  });
@@ -128,7 +129,7 @@ describe("Menu", () => {
128
129
  ],
129
130
  });
130
131
 
131
- fireEvent.press(getByLabelText(messages.more.defaultMessage));
132
+ fireEvent.press(getByLabelText(menuLabel));
132
133
  expect(getByTestId("add").props.style).toContainEqual({
133
134
  display: "flex",
134
135
  fill: tokens["color-critical"],
@@ -150,7 +151,7 @@ describe("Menu", () => {
150
151
  ],
151
152
  });
152
153
 
153
- fireEvent.press(getByLabelText(messages.more.defaultMessage));
154
+ fireEvent.press(getByLabelText(menuLabel));
154
155
  fireEvent.press(getByLabelText("hi"));
155
156
  expect(mockOnPress).toHaveBeenCalled();
156
157
  expect(queryByLabelText("hi")).toBeNull();
@@ -167,7 +168,7 @@ describe("Menu", () => {
167
168
  },
168
169
  ],
169
170
  });
170
- fireEvent.press(getByLabelText(messages.more.defaultMessage));
171
+ fireEvent.press(getByLabelText(menuLabel));
171
172
  expect(getByText("Hi")).toBeDefined();
172
173
  });
173
174
 
@@ -182,7 +183,7 @@ describe("Menu", () => {
182
183
  },
183
184
  ],
184
185
  });
185
- fireEvent.press(getByLabelText(messages.more.defaultMessage));
186
+ fireEvent.press(getByLabelText(menuLabel));
186
187
  expect(getByText("hi")).toBeDefined();
187
188
  });
188
189
  });
@@ -192,7 +193,7 @@ describe("Menu", () => {
192
193
  menuOptions: [{ label: "hi", icon: "add", onPress: mockOnPress }],
193
194
  });
194
195
 
195
- fireEvent.press(getByLabelText(messages.more.defaultMessage));
196
+ fireEvent.press(getByLabelText(menuLabel));
196
197
 
197
198
  expect(getByTestId("ATL-MENU-OPTIONS")).toBeDefined();
198
199
  expect(getByTestId("add")).toBeDefined();
package/src/Menu/Menu.tsx CHANGED
@@ -6,10 +6,8 @@ import {
6
6
  useWindowDimensions,
7
7
  } from "react-native";
8
8
  import { Portal } from "react-native-portalize";
9
- import { useIntl } from "react-intl";
10
9
  import { useSafeAreaFrame } from "react-native-safe-area-context";
11
10
  import { styles } from "./Menu.style";
12
- import { messages } from "./messages";
13
11
  import { findViewpoint } from "./utils";
14
12
  import { MenuProps } from "./types";
15
13
  import { MenuOption } from "./components/MenuOption";
@@ -18,6 +16,7 @@ import { tokens } from "../utils/design";
18
16
  import { Button } from "../Button";
19
17
  import { Content } from "../Content";
20
18
  import { useAtlantisContext } from "../AtlantisContext";
19
+ import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
21
20
 
22
21
  export function Menu({ menuOptions, customActivator }: MenuProps): JSX.Element {
23
22
  const [open, setOpen] = useState<boolean>(false);
@@ -26,7 +25,7 @@ export function Menu({ menuOptions, customActivator }: MenuProps): JSX.Element {
26
25
  const menuButtonRef = useRef<View | null>();
27
26
  const screenInfo = useScreenInformation();
28
27
 
29
- const { formatMessage } = useIntl();
28
+ const { t } = useAtlantisI18n();
30
29
 
31
30
  const findMenuLayout = useCallback(() => {
32
31
  if (activatorLayout.current) {
@@ -78,7 +77,7 @@ export function Menu({ menuOptions, customActivator }: MenuProps): JSX.Element {
78
77
  {!customActivator && (
79
78
  <Button
80
79
  icon="more"
81
- accessibilityLabel={formatMessage(messages.more)}
80
+ accessibilityLabel={t("menu")}
82
81
  variation="cancel"
83
82
  type="tertiary"
84
83
  onPress={() => {
@@ -1,11 +1,10 @@
1
1
  import React from "react";
2
2
  import { View } from "react-native";
3
- import { useIntl } from "react-intl";
4
3
  import { ProgressBarProps } from "./types";
5
4
  import { styles } from "./ProgressBar.style";
6
5
  import { ProgressBarInner, calculateWidth } from "./ProgressBarInner";
7
- import { messages } from "./messages";
8
6
  import { tokens } from "../utils/design";
7
+ import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
9
8
 
10
9
  export function ProgressBar({
11
10
  loading,
@@ -15,17 +14,13 @@ export function ProgressBar({
15
14
  reverseTheme = false,
16
15
  header,
17
16
  }: ProgressBarProps): JSX.Element {
18
- const { formatMessage } = useIntl();
19
- const accessibilityLabel = [];
20
- accessibilityLabel.push(formatMessage(messages.complete, { current, total }));
21
- inProgress &&
22
- accessibilityLabel.push(formatMessage(messages.inProgress, { inProgress }));
17
+ const { t } = useAtlantisI18n();
23
18
 
24
19
  return (
25
20
  <View
26
21
  accessible
27
22
  accessibilityRole="progressbar"
28
- accessibilityLabel={accessibilityLabel.join(", ")}
23
+ accessibilityLabel={getA11yLabel()}
29
24
  >
30
25
  {header}
31
26
  <View style={styles.progressBarContainer}>
@@ -55,4 +50,18 @@ export function ProgressBar({
55
50
  </View>
56
51
  </View>
57
52
  );
53
+
54
+ function getA11yLabel(): string {
55
+ const a11yLabelValues = {
56
+ current: String(current),
57
+ total: String(total),
58
+ inProgress: String(inProgress),
59
+ };
60
+
61
+ if (inProgress) {
62
+ return t("ProgressBar.inProgress", a11yLabelValues);
63
+ }
64
+
65
+ return t("ProgressBar.complete", a11yLabelValues);
66
+ }
58
67
  }
@@ -8,7 +8,6 @@ import {
8
8
  import { tokens } from "@jobber/design/foundation";
9
9
  import { AccessibilityInfo } from "react-native";
10
10
  import { Option, Select } from ".";
11
- import { messages } from "./messages";
12
11
  import { SelectInternalPicker } from "./components/SelectInternalPicker";
13
12
 
14
13
  const A11yInfoSpy = jest.spyOn(AccessibilityInfo, "isScreenReaderEnabled");
@@ -24,6 +23,8 @@ afterEach(() => {
24
23
  jest.resetAllMocks();
25
24
  });
26
25
 
26
+ const defaultPlaceholder = "Select an option";
27
+
27
28
  describe("Select", () => {
28
29
  it("renders a Select", () => {
29
30
  const component = render(
@@ -34,7 +35,7 @@ describe("Select", () => {
34
35
  );
35
36
  expect(component.getByTestId("arrowDown")).toBeDefined();
36
37
  expect(
37
- component.getByText(messages.emptyValue.defaultMessage, {
38
+ component.getByText(defaultPlaceholder, {
38
39
  includeHiddenElements: true,
39
40
  }),
40
41
  ).toBeDefined();
@@ -191,9 +192,7 @@ describe("Select", () => {
191
192
  );
192
193
 
193
194
  expect(
194
- getByText(messages.emptyValue.defaultMessage, {
195
- includeHiddenElements: true,
196
- }),
195
+ getByText(defaultPlaceholder, { includeHiddenElements: true }),
197
196
  ).toBeDefined();
198
197
  });
199
198
 
@@ -1,15 +1,14 @@
1
1
  import React, { ReactElement } from "react";
2
2
  import { View } from "react-native";
3
- import { useIntl } from "react-intl";
4
3
  import { RegisterOptions } from "react-hook-form";
5
4
  import { styles } from "./Select.style";
6
5
  import { SelectInternalPicker } from "./components/SelectInternalPicker";
7
- import { messages } from "./messages";
8
6
  import { InputFieldWrapper } from "../InputFieldWrapper";
9
7
  import { Icon } from "../Icon";
10
8
  import { TextVariation } from "../Typography";
11
9
  import { Text } from "../Text";
12
10
  import { useFormController } from "../hooks";
11
+ import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
13
12
 
14
13
  export interface SelectOption {
15
14
  /**
@@ -111,7 +110,7 @@ export function Select({
111
110
  value: value ?? defaultValue,
112
111
  });
113
112
 
114
- const { formatMessage } = useIntl();
113
+ const { t } = useAtlantisI18n();
115
114
  const internalValue = value ?? field.value;
116
115
  const textVariation = getTextVariation({
117
116
  disabled,
@@ -134,7 +133,7 @@ export function Select({
134
133
  accessible={true}
135
134
  accessibilityLabel={getA11yLabel()}
136
135
  accessibilityValue={{ text: getValue() }}
137
- accessibilityHint={formatMessage(messages.a11yHint)}
136
+ accessibilityHint={t("Select.a11yHint")}
138
137
  accessibilityRole="button"
139
138
  accessibilityState={{ disabled: disabled }}
140
139
  >
@@ -207,7 +206,7 @@ export function Select({
207
206
 
208
207
  if (!internalValue || placeholder) {
209
208
  options.unshift({
210
- label: placeholder || formatMessage(messages.emptyValue),
209
+ label: placeholder || t("Select.emptyValue"),
211
210
  value: "",
212
211
  isActive: !internalValue,
213
212
  });
@@ -220,9 +219,7 @@ export function Select({
220
219
  const options = getOptions();
221
220
 
222
221
  const activeValue = options.find(option => option.isActive);
223
- return (
224
- activeValue?.label || placeholder || formatMessage(messages.emptyValue)
225
- );
222
+ return activeValue?.label || placeholder || t("Select.emptyValue");
226
223
  }
227
224
  }
228
225
 
@@ -8,11 +8,10 @@ import {
8
8
  } from "react-native";
9
9
  import { Picker } from "@react-native-picker/picker";
10
10
  import { SafeAreaView } from "react-native-safe-area-context";
11
- import { useIntl } from "react-intl";
12
11
  import { styles } from "./SelectDefaultPicker.style";
13
- import { messages } from "./messages";
14
12
  import { SelectInternalPickerProps } from "../../types";
15
13
  import { SelectPressable } from "../SelectPressable/SelectPressable";
14
+ import { useAtlantisI18n } from "../../../hooks/useAtlantisI18n";
16
15
 
17
16
  type SelectDefaultPickerProps = SelectInternalPickerProps;
18
17
 
@@ -22,7 +21,7 @@ export function SelectDefaultPicker({
22
21
  onChange,
23
22
  }: SelectDefaultPickerProps): JSX.Element {
24
23
  const [show, setShow] = useState(false);
25
- const { formatMessage } = useIntl();
24
+ const { t } = useAtlantisI18n();
26
25
  const selectedLanguage = options.find(option => option.isActive);
27
26
 
28
27
  return (
@@ -36,7 +35,7 @@ export function SelectDefaultPicker({
36
35
  >
37
36
  <TouchableOpacity style={styles.overlay} onPress={hidePicker} />
38
37
  <View style={styles.actionBar}>
39
- <Button title={formatMessage(messages.done)} onPress={hidePicker} />
38
+ <Button title={t("done")} onPress={hidePicker} />
40
39
  </View>
41
40
  <View style={styles.pickerContainer} testID="select-wheel-picker">
42
41
  <SafeAreaView edges={["bottom"]}>
@@ -2,7 +2,6 @@ import React from "react";
2
2
  import { cleanup, fireEvent, render } from "@testing-library/react-native";
3
3
  import { AccessibilityInfo, View } from "react-native";
4
4
  import { SelectDefaultPicker } from "./SelectDefaultPicker";
5
- import { messages } from "./messages";
6
5
  import { Text } from "../../../Text";
7
6
 
8
7
  const A11yInfoSpy = jest.spyOn(AccessibilityInfo, "isScreenReaderEnabled");
@@ -55,7 +54,7 @@ describe("SelectDefaultPicker", () => {
55
54
  const screen = setup();
56
55
 
57
56
  fireEvent.press(screen.getByText(childText));
58
- fireEvent.press(screen.getByText(messages.done.defaultMessage));
57
+ fireEvent.press(screen.getByText("Done"));
59
58
  expect(
60
59
  screen.getByTestId("SelectDefaultPicker").findAllByType(MockPicker),
61
60
  ).toEqual([]);
@@ -6,28 +6,23 @@ import Toast, {
6
6
  } from "react-native-toast-message";
7
7
  import { AccessibilityInfo, TouchableOpacity, View } from "react-native";
8
8
  import { useSafeAreaInsets } from "react-native-safe-area-context";
9
- import { useIntl } from "react-intl";
10
9
  import { styles } from "./Toast.styles";
11
- import { messages } from "./messages";
12
10
  import { tokens } from "../utils/design";
13
11
  import { Text } from "../Text";
14
12
  import { IconButton } from "../IconButton";
13
+ import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
15
14
 
16
15
  const MAX_TOAST_MESSAGE_LENGTH = 60;
17
16
  const ANNOUNCEMENT_DELAY = 100;
18
17
 
19
18
  function DefaultToast({ text1 }: ToastConfigParams<string>): JSX.Element {
20
19
  const { bottom } = useSafeAreaInsets();
21
- const { formatMessage } = useIntl();
20
+ const { t } = useAtlantisI18n();
22
21
  const toastContainerStyles = [styles.container, { paddingBottom: bottom }];
23
22
  return (
24
23
  <View style={toastContainerStyles}>
25
24
  <View style={styles.toast}>
26
- <TouchableOpacity
27
- style={styles.toastMessage}
28
- accessibilityRole="alert"
29
- accessibilityLabel={formatMessage(messages.toastNotificationLabel)}
30
- >
25
+ <TouchableOpacity style={styles.toastMessage} accessibilityRole="alert">
31
26
  <Text reverseTheme>{text1}</Text>
32
27
  </TouchableOpacity>
33
28
  <View style={styles.toastIcon}>
@@ -35,7 +30,7 @@ function DefaultToast({ text1 }: ToastConfigParams<string>): JSX.Element {
35
30
  onPress={Toast.hide}
36
31
  name="remove"
37
32
  customColor={tokens["color-greyBlue--light"]}
38
- accessibilityLabel={formatMessage(messages.dismissA11yLabel)}
33
+ accessibilityLabel={t("dismiss")}
39
34
  />
40
35
  </View>
41
36
  </View>
@@ -0,0 +1 @@
1
+ export * from "./useAtlantisI18n";
@@ -0,0 +1,35 @@
1
+ {
2
+ "AutoLink.emailCopied": "Email copied",
3
+ "AutoLink.phoneCopied": "Phone number copied",
4
+ "AutoLink.urlCopied": "URL copied",
5
+ "cancel": "Cancel",
6
+ "confirm": "Confirm",
7
+ "copied": "Copied",
8
+ "date": "Date",
9
+ "dismiss": "Dismiss",
10
+ "done": "Done",
11
+ "ContentOverlay.close": "Close {title} modal",
12
+ "errors.couldNotSave": "Could not save changes",
13
+ "errors.notANumber": "Enter a number",
14
+ "FormatFile.label": "Attachment Preview",
15
+ "FormatFile.hint": "Select for more options",
16
+ "FormatFile.preview": "Preview {item}",
17
+ "FormatFile.remove": "Remove {item}",
18
+ "goBack": "Go back",
19
+ "InputFieldWrapper.clear": "Clear input",
20
+ "InputPassword.enterPassword": "Enter a password",
21
+ "loading": "Loading",
22
+ "menu": "Menu",
23
+ "more": "More",
24
+ "networkUnavailableTitle": "Network Unavailable",
25
+ "networkUnavailableDescription": "Check your internet connection and try again later.",
26
+ "ProgressBar.complete": "{current} of {total} complete",
27
+ "ProgressBar.inProgress": "{current} of {total} complete, {inProgress} in progress",
28
+ "save": "Save",
29
+ "Select.a11yHint": "Select to open the picker",
30
+ "Select.emptyValue": "Select an option",
31
+ "time": "Time",
32
+ "tryAgain": "Try again",
33
+ "upload.failed": "Failed to upload.",
34
+ "upload.inProgress": "Upload in progress."
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "AutoLink.emailCopied": "Correo electrónico copiado",
3
+ "AutoLink.phoneCopied": "Número de teléfono copiado",
4
+ "AutoLink.urlCopied": "URL copiada",
5
+ "cancel": "Cancelar",
6
+ "confirm": "Confirmar",
7
+ "copied": "Copiado",
8
+ "date": "Fecha",
9
+ "dismiss": "Descartar",
10
+ "done": "Listo",
11
+ "ContentOverlay.close": "Close {title} modal",
12
+ "errors.couldNotSave": "No se pudieron guardar los cambios",
13
+ "errors.notANumber": "Escriba un número",
14
+ "FormatFile.label": "Attachment Preview",
15
+ "FormatFile.hint": "Select for more options",
16
+ "FormatFile.preview": "Vista previa de {item}",
17
+ "FormatFile.remove": "Eliminar {item}",
18
+ "goBack": "Volver",
19
+ "InputFieldWrapper.clear": "Borrar",
20
+ "InputPassword.enterPassword": "Escriba una contraseña",
21
+ "loading": "Cargando",
22
+ "menu": "Menú",
23
+ "more": "Más",
24
+ "networkUnavailableTitle": "Red no disponible",
25
+ "networkUnavailableDescription": "Compruebe su conexión a Internet e inténtelo de nuevo más adelante.",
26
+ "ProgressBar.complete": "{current} de {total} completo",
27
+ "ProgressBar.inProgress": "{current} de {total} completo, {inProgress} en progreso",
28
+ "save": "Guardar",
29
+ "Select.a11yHint": "Seleccionar para abrir el selector",
30
+ "Select.emptyValue": "Seleccione una opción",
31
+ "time": "Hora",
32
+ "tryAgain": "Intentar de nuevo",
33
+ "upload.failed": "No se pudo adjuntar el archivo.",
34
+ "upload.inProgress": "Carga de archivo en curso."
35
+ }
@@ -0,0 +1,53 @@
1
+ import { renderHook } from "@testing-library/react-native";
2
+ import { useAtlantisI18n } from ".";
3
+ import en from "./locales/en.json";
4
+ import es from "./locales/es.json";
5
+ import * as context from "../../AtlantisContext";
6
+
7
+ jest.mock("../../AtlantisContext", () => ({
8
+ // need to mark this as a module so that we can spy on it
9
+ __esModule: true,
10
+ ...jest.requireActual("../../AtlantisContext"),
11
+ }));
12
+
13
+ describe("useAtlantisI18n", () => {
14
+ it("should return english by default", () => {
15
+ const { result } = renderHook(useAtlantisI18n);
16
+
17
+ expect(result.current.t("cancel")).toBe("Cancel");
18
+ });
19
+
20
+ it("should interpolate the strings wrapped in {}", () => {
21
+ const { result } = renderHook(useAtlantisI18n);
22
+
23
+ expect(result.current.t("FormatFile.preview", { item: "🔱" })).toBe(
24
+ "Preview 🔱",
25
+ );
26
+ });
27
+
28
+ describe("Español", () => {
29
+ it("should return español", () => {
30
+ const spy = jest.spyOn(context, "useAtlantisContext");
31
+ spy.mockReturnValueOnce({ ...context.defaultValues, locale: "es" });
32
+ const { result } = renderHook(useAtlantisI18n);
33
+
34
+ expect(result.current.t("cancel")).toBe("Cancelar");
35
+ });
36
+ });
37
+
38
+ describe("Unsupported language", () => {
39
+ it("should return the english translation", () => {
40
+ const spy = jest.spyOn(context, "useAtlantisContext");
41
+ spy.mockReturnValueOnce({ ...context.defaultValues, locale: "fr" });
42
+ const { result } = renderHook(useAtlantisI18n);
43
+
44
+ expect(result.current.t("cancel")).toBe("Cancel");
45
+ });
46
+ });
47
+
48
+ describe("Translation files", () => {
49
+ it("should have the same keys for en and es", () => {
50
+ expect(Object.keys(en)).toEqual(Object.keys(es));
51
+ });
52
+ });
53
+ });
@@ -0,0 +1,42 @@
1
+ import en from "./locales/en.json";
2
+ import es from "./locales/es.json";
3
+ import { useAtlantisContext } from "../../AtlantisContext";
4
+
5
+ export type I18nKeys = keyof typeof en;
6
+
7
+ export interface useAtlantisI18nValue {
8
+ readonly locale: string;
9
+ readonly t: (message: I18nKeys, values?: Record<string, string>) => string;
10
+ }
11
+
12
+ export function useAtlantisI18n(): useAtlantisI18nValue {
13
+ const { locale } = useAtlantisContext();
14
+ const t = (messageKey: keyof typeof en, values?: Record<string, string>) =>
15
+ formatMessage(messageKey, values, locale);
16
+
17
+ return { locale, t };
18
+ }
19
+
20
+ function getLocalizedStrings(locale: string): typeof en {
21
+ switch (locale) {
22
+ case "es":
23
+ return es;
24
+ default:
25
+ return en;
26
+ }
27
+ }
28
+
29
+ function formatMessage(
30
+ messageKey: keyof typeof en,
31
+ values?: Record<string, string>,
32
+ locale = "en",
33
+ ) {
34
+ const message = getLocalizedStrings(locale)[messageKey];
35
+
36
+ if (!values) return message;
37
+
38
+ return Object.entries(values).reduce(
39
+ (acc, [key, value]) => acc.replace(`{${key}}`, value),
40
+ message,
41
+ );
42
+ }
@@ -1,18 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- phoneCopied: {
4
- id: "phoneCopied",
5
- defaultMessage: "Phone number copied",
6
- description: "Message shown after copying a phone number",
7
- },
8
- emailCopied: {
9
- id: "emailCopied",
10
- defaultMessage: "Email copied",
11
- description: "Message shown after copying an email",
12
- },
13
- urlCopied: {
14
- id: "urlCopied",
15
- defaultMessage: "URL copied",
16
- description: "Message shown after copying a URL",
17
- },
18
- });
@@ -1,8 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- cancel: {
4
- id: "cancel",
5
- defaultMessage: "Cancel",
6
- description: "Text for the cancel action",
7
- },
8
- });
@@ -1,18 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- more: {
4
- id: "more",
5
- defaultMessage: "More",
6
- description: "Accessibility label for the More button",
7
- },
8
- unavailableNetworkTitle: {
9
- id: "unavailableNetworkTitle",
10
- defaultMessage: "Network unavailable",
11
- description: "The title for alert about network unavailable",
12
- },
13
- unavailableNetworkMessage: {
14
- id: "unavailableNetworkMessage",
15
- defaultMessage: "Check your internet connection and try again later.",
16
- description: "The message for alert about network unavailable",
17
- },
18
- });
@@ -1,8 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- closeOverlayA11YLabel: {
4
- id: "closeOverlayA11yLabel",
5
- defaultMessage: "Close {title} modal",
6
- description: "Accessibility label for button to close the overlay modal",
7
- },
8
- });
@@ -1,13 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- networkError: {
4
- id: "networkError",
5
- defaultMessage: "Could not save changes",
6
- description: "Displayed when a general server error occurs during save",
7
- },
8
- offlineError: {
9
- id: "offlineError",
10
- defaultMessage: "Currently offline. Check your internet connection.",
11
- description: "Error message to be shown when the app is offline",
12
- },
13
- });
@@ -1,8 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- goBackButton: {
4
- id: "goBackButton",
5
- defaultMessage: "Go Back",
6
- description: "The label for the fallback secondary button when it doesn't exist",
7
- },
8
- });
@@ -1,8 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- saveButton: {
4
- id: "saveButton",
5
- defaultMessage: "Save",
6
- description: "The label for the save button",
7
- },
8
- });
@@ -1,28 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- loadingA11YLabel: {
4
- id: "loadingA11yLabel",
5
- defaultMessage: "Loading",
6
- description: "Accessibility label for the loading indicator",
7
- },
8
- dismissAlertButton: {
9
- id: "dismiss",
10
- defaultMessage: "Dismiss",
11
- description: "The label for the button to dismiss the alert ",
12
- },
13
- retryAlertButton: {
14
- id: "retry",
15
- defaultMessage: "Try Again",
16
- description: "The label for the alert button to try action again",
17
- },
18
- unavailableNetworkTitle: {
19
- id: "unavailableNetworkTitle",
20
- defaultMessage: "Network unavailable",
21
- description: "The title for alert about network unavailable",
22
- },
23
- unavailableNetworkMessage: {
24
- id: "unavailableNetworkMessage",
25
- defaultMessage: "Check your internet connection and try again later.",
26
- description: "The message for alert about network unavailable",
27
- },
28
- });
@@ -1,13 +0,0 @@
1
- import { defineMessages } from "react-intl";
2
- export const messages = defineMessages({
3
- lightBoxPreviewButton: {
4
- id: "lightBoxPreviewButton",
5
- defaultMessage: "Preview {bottomSheetOptionsSuffix}",
6
- description: "Label for button when preview the file",
7
- },
8
- removeButton: {
9
- id: "removeButton",
10
- defaultMessage: "Remove {bottomSheetOptionsSuffix}",
11
- description: "Label for button when remove the file",
12
- },
13
- });