@jobber/components-native 0.84.4-JOB-134681-8086ad4.25 → 0.84.4-JOB-138679-b1552ab.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +23 -14
- package/dist/src/ActionItem/ActionItemGroup.js +1 -1
- package/dist/src/AutoLink/components/Link/Link.js +1 -1
- package/dist/src/Banner/Banner.js +2 -0
- package/dist/src/Banner/components/BannerIcon/BannerIcon.style.js +3 -0
- package/dist/src/BottomSheet/BottomSheet.js +2 -2
- package/dist/src/Button/components/InternalButtonLoading/InternalButtonLoading.js +0 -1
- package/dist/src/ButtonGroup/ButtonGroup.js +1 -1
- package/dist/src/ContentOverlay/ContentOverlay.js +5 -7
- package/dist/src/ContentOverlay/UNSAFE_WrappedModalize.js +23 -0
- package/dist/src/ErrorMessageWrapper/context/ErrorMessageProvider.js +1 -1
- package/dist/src/Form/Form.js +5 -2
- package/dist/src/Form/components/FormCache/FormCache.js +1 -0
- package/dist/src/Form/components/FormMessageBanner/FormMessageBanner.js +1 -1
- package/dist/src/Form/context/AtlantisFormContext.js +1 -0
- package/dist/src/Form/hooks/useInternalForm.js +6 -3
- package/dist/src/FormatFile/components/MediaView/MediaView.js +22 -5
- package/dist/src/FormatFile/utils/createUseCreateThumbnail.js +1 -1
- package/dist/src/InputCurrency/InputCurrency.js +42 -30
- package/dist/src/InputDate/InputDate.js +2 -2
- package/dist/src/InputEmail/InputEmail.js +12 -4
- package/dist/src/InputFieldWrapper/InputFieldWrapper.js +14 -12
- package/dist/src/InputFieldWrapper/InputFieldWrapper.style.js +1 -1
- package/dist/src/InputFieldWrapper/components/Prefix/Prefix.js +5 -2
- package/dist/src/InputFieldWrapper/components/Suffix/Suffix.js +5 -2
- package/dist/src/InputNumber/InputNumber.js +10 -4
- package/dist/src/InputPressable/InputPressable.js +20 -8
- package/dist/src/InputPressable/InputPressable.style.js +3 -0
- package/dist/src/InputSearch/InputSearch.js +1 -1
- package/dist/src/InputText/InputText.js +22 -11
- package/dist/src/InputText/InputText.style.js +4 -0
- package/dist/src/InputTime/InputTime.js +2 -2
- package/dist/src/Menu/Menu.js +2 -2
- package/dist/src/Select/Select.style.js +1 -0
- package/dist/src/Select/components/SelectPressable/SelectPressable.js +1 -1
- package/dist/src/Switch/components/BaseSwitch/BaseSwitch.js +7 -1
- package/dist/src/Typography/Typography.js +16 -5
- package/dist/src/hooks/useAtlantisI18n/locales/en.json +1 -0
- package/dist/src/hooks/useAtlantisI18n/locales/es.json +1 -0
- package/dist/src/hooks/useFormController.js +5 -14
- package/dist/tsconfig.build.json +7 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/tsconfig.json +4 -6
- package/dist/types/src/ActionItem/ActionItem.d.ts +2 -1
- package/dist/types/src/ActionItem/ActionItemGroup.d.ts +2 -1
- package/dist/types/src/ActionItem/components/ActionItemContainer.d.ts +2 -1
- package/dist/types/src/ActionLabel/ActionLabel.d.ts +4 -3
- package/dist/types/src/ActivityIndicator/ActivityIndicator.d.ts +2 -1
- package/dist/types/src/AutoLink/AutoLink.d.ts +2 -1
- package/dist/types/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.d.ts +2 -1
- package/dist/types/src/AutoLink/components/Link/Link.d.ts +2 -1
- package/dist/types/src/Banner/Banner.d.ts +2 -1
- package/dist/types/src/Banner/components/BannerIcon/BannerIcon.d.ts +2 -1
- package/dist/types/src/Banner/components/BannerIcon/BannerIcon.style.d.ts +3 -0
- package/dist/types/src/Banner/types.d.ts +1 -1
- package/dist/types/src/BottomSheet/BottomSheet.d.ts +1 -1
- package/dist/types/src/BottomSheet/components/BottomSheetOption/BottomSheetOption.d.ts +2 -1
- package/dist/types/src/Button/Button.d.ts +2 -1
- package/dist/types/src/Button/components/InternalButtonLoading/InternalButtonLoading.d.ts +1 -1
- package/dist/types/src/ButtonGroup/ButtonGroup.d.ts +2 -1
- package/dist/types/src/ButtonGroup/ButtonGroupAction.d.ts +4 -3
- package/dist/types/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.d.ts +3 -2
- package/dist/types/src/Card/Card.d.ts +2 -1
- package/dist/types/src/Card/components/InternalCardHeader.d.ts +2 -1
- package/dist/types/src/Checkbox/Checkbox.d.ts +2 -1
- package/dist/types/src/Checkbox/CheckboxGroup.d.ts +2 -1
- package/dist/types/src/Chip/Chip.d.ts +2 -1
- package/dist/types/src/Content/Content.d.ts +2 -1
- package/dist/types/src/ContentOverlay/ContentOverlay.d.ts +1 -1
- package/dist/types/src/ContentOverlay/UNSAFE_WrappedModalize.d.ts +3 -0
- package/dist/types/src/Disclosure/Disclosure.d.ts +1 -1
- package/dist/types/src/Divider/Divider.d.ts +2 -1
- package/dist/types/src/EmptyState/EmptyState.d.ts +2 -1
- package/dist/types/src/ErrorMessageWrapper/ErrorMessageWrapper.d.ts +2 -1
- package/dist/types/src/ErrorMessageWrapper/context/ErrorMessageProvider.d.ts +2 -1
- package/dist/types/src/ErrorMessageWrapper/context/types.d.ts +1 -1
- package/dist/types/src/Flex/Flex.d.ts +2 -1
- package/dist/types/src/Form/Form.d.ts +2 -1
- package/dist/types/src/Form/components/FormActionBar/FormActionBar.d.ts +3 -2
- package/dist/types/src/Form/components/FormBody/FormBody.d.ts +3 -2
- package/dist/types/src/Form/components/FormCache/FormCache.d.ts +4 -3
- package/dist/types/src/Form/components/FormErrorBanner/FormErrorBanner.d.ts +2 -1
- package/dist/types/src/Form/components/FormMask/FormMask.d.ts +2 -1
- package/dist/types/src/Form/components/FormMessage/FormMessage.d.ts +2 -1
- package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.d.ts +2 -1
- package/dist/types/src/Form/components/FormMessageBanner/FormMessageBanner.d.ts +2 -1
- package/dist/types/src/Form/components/FormSaveButton/FormSaveButton.d.ts +2 -1
- package/dist/types/src/Form/context/AtlantisFormContext.d.ts +2 -1
- package/dist/types/src/Form/context/types.d.ts +3 -2
- package/dist/types/src/Form/hooks/useFormViewRefs.d.ts +2 -2
- package/dist/types/src/Form/hooks/useInternalForm.d.ts +5 -5
- package/dist/types/src/Form/types.d.ts +13 -7
- package/dist/types/src/FormField/FormField.d.ts +1 -1
- package/dist/types/src/FormatFile/FormatFile.d.ts +2 -1
- package/dist/types/src/FormatFile/components/ErrorIcon/ErrorIcon.d.ts +2 -1
- package/dist/types/src/FormatFile/components/FileView/FileView.d.ts +2 -1
- package/dist/types/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.d.ts +3 -2
- package/dist/types/src/FormatFile/components/MediaView/MediaView.d.ts +2 -1
- package/dist/types/src/FormatFile/components/ProgressBar/ProgressBar.d.ts +2 -1
- package/dist/types/src/Heading/Heading.d.ts +4 -3
- package/dist/types/src/Icon/Icon.d.ts +2 -1
- package/dist/types/src/IconButton/IconButton.d.ts +1 -1
- package/dist/types/src/InputCurrency/InputCurrency.d.ts +2 -1
- package/dist/types/src/InputDate/InputDate.d.ts +4 -2
- package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +9 -2
- package/dist/types/src/InputFieldWrapper/InputFieldWrapper.style.d.ts +1 -1
- package/dist/types/src/InputFieldWrapper/components/ClearAction/ClearAction.d.ts +2 -1
- package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +4 -4
- package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +4 -4
- package/dist/types/src/InputPressable/InputPressable.d.ts +9 -1
- package/dist/types/src/InputPressable/InputPressable.style.d.ts +3 -0
- package/dist/types/src/InputSearch/InputSearch.d.ts +1 -1
- package/dist/types/src/InputSearch/components/FilterButton.d.ts +2 -1
- package/dist/types/src/InputText/InputText.d.ts +10 -2
- package/dist/types/src/InputText/InputText.style.d.ts +4 -0
- package/dist/types/src/InputText/context/InputAccessoriesProvider.d.ts +2 -1
- package/dist/types/src/InputTime/InputTime.d.ts +4 -2
- package/dist/types/src/Menu/Menu.d.ts +2 -1
- package/dist/types/src/Menu/components/MenuOption/MenuOption.d.ts +2 -1
- package/dist/types/src/Menu/components/Overlay/Overlay.d.ts +2 -1
- package/dist/types/src/Menu/types.d.ts +6 -1
- package/dist/types/src/ProgressBar/ProgressBar.d.ts +2 -1
- package/dist/types/src/ProgressBar/ProgressBarInner.d.ts +2 -1
- package/dist/types/src/Select/Select.d.ts +3 -2
- package/dist/types/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.d.ts +2 -1
- package/dist/types/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.d.ts +2 -1
- package/dist/types/src/Select/components/SelectInternalPicker/SelectInternalPicker.d.ts +2 -1
- package/dist/types/src/Select/components/SelectPressable/SelectPressable.d.ts +2 -1
- package/dist/types/src/StatusLabel/StatusLabel.d.ts +2 -1
- package/dist/types/src/Switch/Switch.d.ts +2 -1
- package/dist/types/src/Switch/components/BaseSwitch/BaseSwitch.d.ts +2 -1
- package/dist/types/src/Text/Text.d.ts +4 -3
- package/dist/types/src/TextList/TextList.d.ts +2 -1
- package/dist/types/src/ThumbnailList/ThumbnailList.d.ts +2 -1
- package/dist/types/src/Toast/Toast.d.ts +2 -1
- package/dist/types/src/Typography/Typography.d.ts +3 -3
- package/dist/types/src/Typography/TypographyGestureDetector.d.ts +2 -1
- package/dist/types/src/utils/test/MockSafeAreaProvider.d.ts +1 -1
- package/package.json +23 -14
- package/src/ActionItem/ActionItem.tsx +1 -1
- package/src/ActionItem/ActionItemGroup.tsx +1 -3
- package/src/ActionItem/components/ActionItemContainer.tsx +1 -1
- package/src/ActionLabel/ActionLabel.test.tsx +12 -0
- package/src/ActionLabel/ActionLabel.tsx +3 -3
- package/src/ActionLabel/__snapshots__/ActionLabel.test.tsx.snap +66 -0
- package/src/ActivityIndicator/ActivityIndicator.tsx +1 -3
- package/src/AtlantisContext/AtlantisContext.test.tsx +1 -1
- package/src/AtlantisThemeContext/AtlantisThemeContext.test.tsx +5 -5
- package/src/AutoLink/AutoLink.test.tsx +2 -4
- package/src/AutoLink/AutoLink.tsx +1 -1
- package/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.tsx +1 -1
- package/src/AutoLink/components/Link/Link.tsx +1 -5
- package/src/Banner/Banner.test.tsx +12 -0
- package/src/Banner/Banner.tsx +4 -2
- package/src/Banner/components/BannerIcon/BannerIcon.style.ts +3 -0
- package/src/Banner/components/BannerIcon/BannerIcon.tsx +1 -1
- package/src/Banner/types.ts +1 -1
- package/src/BottomSheet/BottomSheet.test.tsx +17 -17
- package/src/BottomSheet/BottomSheet.tsx +4 -3
- package/src/BottomSheet/components/BottomSheetOption/BottomSheetOption.tsx +1 -1
- package/src/Button/Button.tsx +1 -1
- package/src/Button/components/InternalButtonLoading/InternalButtonLoading.tsx +1 -2
- package/src/ButtonGroup/ButtonGroup.test.tsx +13 -11
- package/src/ButtonGroup/ButtonGroup.tsx +2 -2
- package/src/ButtonGroup/ButtonGroupAction.tsx +4 -4
- package/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.tsx +2 -2
- package/src/Card/Card.tsx +1 -1
- package/src/Card/components/InternalCardHeader.tsx +1 -1
- package/src/Checkbox/Checkbox.tsx +2 -2
- package/src/Checkbox/CheckboxGroup.test.tsx +3 -7
- package/src/Checkbox/CheckboxGroup.tsx +2 -2
- package/src/Chip/Chip.tsx +1 -1
- package/src/Content/Content.tsx +1 -1
- package/src/ContentOverlay/ContentOverlay.test.tsx +34 -27
- package/src/ContentOverlay/ContentOverlay.tsx +7 -7
- package/src/ContentOverlay/UNSAFE_WrappedModalize.tsx +41 -0
- package/src/ContentOverlay/hooks/useKeyboardVisibility.test.ts +7 -7
- package/src/ContentOverlay/hooks/useViewLayoutHeight.test.ts +1 -1
- package/src/Disclosure/Disclosure.tsx +1 -1
- package/src/Divider/Divider.tsx +1 -1
- package/src/EmptyState/EmptyState.test.tsx +29 -42
- package/src/EmptyState/EmptyState.tsx +1 -1
- package/src/ErrorMessageWrapper/ErrorMessageWrapper.tsx +1 -1
- package/src/ErrorMessageWrapper/context/ErrorMessageProvider.tsx +3 -5
- package/src/ErrorMessageWrapper/context/types.ts +1 -1
- package/src/Flex/Flex.tsx +2 -2
- package/src/Form/Form.test.tsx +145 -2
- package/src/Form/Form.tsx +8 -1
- package/src/Form/components/FormActionBar/FormActionBar.tsx +3 -3
- package/src/Form/components/FormBody/FormBody.tsx +3 -3
- package/src/Form/components/FormCache/FormCache.tsx +5 -4
- package/src/Form/components/FormErrorBanner/FormErrorBanner.tsx +1 -1
- package/src/Form/components/FormMask/FormMask.tsx +1 -1
- package/src/Form/components/FormMessage/FormMessage.test.tsx +40 -27
- package/src/Form/components/FormMessage/FormMessage.tsx +1 -1
- package/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.tsx +1 -1
- package/src/Form/components/FormMessageBanner/FormMessageBanner.tsx +1 -3
- package/src/Form/components/FormSaveButton/FormSaveButton.test.tsx +2 -2
- package/src/Form/components/FormSaveButton/FormSaveButton.tsx +1 -1
- package/src/Form/context/AtlantisFormContext.test.tsx +1 -1
- package/src/Form/context/AtlantisFormContext.tsx +1 -0
- package/src/Form/context/types.ts +3 -2
- package/src/Form/hooks/useFormViewRefs.ts +4 -5
- package/src/Form/hooks/useInternalForm.ts +12 -4
- package/src/Form/hooks/useScrollToError/useScrollToError.test.tsx +2 -1
- package/src/Form/types.ts +14 -8
- package/src/FormField/FormField.tsx +1 -1
- package/src/FormatFile/FormatFile.test.tsx +21 -21
- package/src/FormatFile/FormatFile.tsx +3 -3
- package/src/FormatFile/components/ErrorIcon/ErrorIcon.tsx +1 -1
- package/src/FormatFile/components/FileView/FileView.tsx +1 -1
- package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.test.tsx +14 -14
- package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.tsx +2 -2
- package/src/FormatFile/components/MediaView/MediaView.test.tsx +283 -0
- package/src/FormatFile/components/MediaView/MediaView.tsx +28 -7
- package/src/FormatFile/components/ProgressBar/ProgressBar.tsx +1 -1
- package/src/FormatFile/utils/createUseCreateThumbnail.ts +1 -1
- package/src/Glimmer/Glimmer.test.tsx +2 -2
- package/src/Heading/Heading.test.tsx +13 -0
- package/src/Heading/Heading.tsx +3 -3
- package/src/Heading/__snapshots__/Heading.test.tsx.snap +65 -0
- package/src/Icon/Icon.tsx +1 -1
- package/src/Icon/__snapshots__/Icon.test.tsx.snap +7 -0
- package/src/IconButton/IconButton.tsx +1 -1
- package/src/InputCurrency/InputCurrency.tsx +72 -58
- package/src/InputDate/InputDate.tsx +7 -3
- package/src/InputEmail/InputEmail.tsx +15 -8
- package/src/InputFieldWrapper/InputFieldWrapper.style.ts +1 -1
- package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +48 -1
- package/src/InputFieldWrapper/InputFieldWrapper.tsx +39 -29
- package/src/InputFieldWrapper/components/ClearAction/ClearAction.tsx +1 -1
- package/src/InputFieldWrapper/components/Prefix/Prefix.test.tsx +8 -16
- package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +8 -6
- package/src/InputFieldWrapper/components/Suffix/Suffix.test.tsx +2 -4
- package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +8 -6
- package/src/InputNumber/InputNumber.tsx +11 -7
- package/src/InputPassword/InputPassword.tsx +1 -1
- package/src/InputPressable/InputPressable.style.ts +4 -0
- package/src/InputPressable/InputPressable.test.tsx +75 -1
- package/src/InputPressable/InputPressable.tsx +34 -8
- package/src/InputSearch/InputSearch.tsx +2 -1
- package/src/InputSearch/components/FilterButton.tsx +1 -1
- package/src/InputText/InputText.style.ts +5 -0
- package/src/InputText/InputText.test.tsx +75 -0
- package/src/InputText/InputText.tsx +34 -17
- package/src/InputText/context/InputAccessoriesProvider.tsx +1 -1
- package/src/InputTime/InputTime.tsx +7 -3
- package/src/Menu/Menu.tsx +3 -3
- package/src/Menu/components/MenuOption/MenuOption.tsx +1 -1
- package/src/Menu/components/Overlay/Overlay.tsx +1 -1
- package/src/Menu/types.ts +7 -1
- package/src/ProgressBar/ProgressBar.tsx +1 -1
- package/src/ProgressBar/ProgressBarInner.tsx +1 -1
- package/src/Select/Select.style.ts +1 -0
- package/src/Select/Select.tsx +2 -2
- package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.tsx +1 -1
- package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.tsx +1 -1
- package/src/Select/components/SelectInternalPicker/SelectInternalPicker.tsx +1 -1
- package/src/Select/components/SelectPressable/SelectPressable.tsx +1 -4
- package/src/StatusLabel/StatusLabel.tsx +1 -1
- package/src/Switch/Switch.tsx +1 -1
- package/src/Switch/components/BaseSwitch/BaseSwitch.tsx +8 -2
- package/src/Text/Text.test.tsx +10 -0
- package/src/Text/Text.tsx +3 -3
- package/src/Text/__snapshots__/Text.test.tsx.snap +66 -0
- package/src/TextList/TextList.tsx +1 -1
- package/src/ThumbnailList/ThumbnailList.test.tsx +5 -5
- package/src/ThumbnailList/ThumbnailList.tsx +1 -1
- package/src/Toast/Toast.tsx +2 -2
- package/src/Typography/Typography.test.tsx +61 -0
- package/src/Typography/Typography.tsx +25 -9
- package/src/Typography/TypographyGestureDetector.tsx +1 -3
- package/src/Typography/__snapshots__/Typography.test.tsx.snap +222 -0
- package/src/hooks/useAtlantisI18n/locales/en.json +1 -0
- package/src/hooks/useAtlantisI18n/locales/es.json +1 -0
- package/src/hooks/useFormController.ts +6 -13
- package/src/utils/test/MockSafeAreaProvider.tsx +1 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { fireEvent, render, waitFor } from "@testing-library/react-native";
|
|
3
|
+
import { MediaView } from "./MediaView";
|
|
4
|
+
import type { FormattedFile } from "../../types";
|
|
5
|
+
import { StatusCode } from "../../types";
|
|
6
|
+
import { AtlantisFormatFileContext } from "../../context/FormatFileContext";
|
|
7
|
+
|
|
8
|
+
jest.mock("../../../hooks/useAtlantisI18n", () => ({
|
|
9
|
+
useAtlantisI18n: () => ({ t: (key: string) => key }),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
describe("MediaView", () => {
|
|
13
|
+
const mockFile: FormattedFile = {
|
|
14
|
+
showPreview: true,
|
|
15
|
+
source: "https://example.com/image1.jpg",
|
|
16
|
+
thumbnailUrl: undefined,
|
|
17
|
+
name: "test.jpg",
|
|
18
|
+
size: 1024,
|
|
19
|
+
external: false,
|
|
20
|
+
progress: 0,
|
|
21
|
+
status: StatusCode.Completed,
|
|
22
|
+
error: false,
|
|
23
|
+
type: "image/jpeg",
|
|
24
|
+
isMedia: true,
|
|
25
|
+
showFileTypeIndicator: false,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const defaultProps = {
|
|
29
|
+
accessibilityLabel: "Test image",
|
|
30
|
+
showOverlay: false,
|
|
31
|
+
showError: false,
|
|
32
|
+
file: mockFile,
|
|
33
|
+
styleInGrid: false,
|
|
34
|
+
onUploadComplete: jest.fn(),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const mockContextValue = {
|
|
38
|
+
useCreateThumbnail: () => ({ thumbnail: undefined, error: false }),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const renderWithContext = (props = defaultProps) => {
|
|
42
|
+
return render(
|
|
43
|
+
<AtlantisFormatFileContext.Provider value={mockContextValue}>
|
|
44
|
+
<MediaView {...props} />
|
|
45
|
+
</AtlantisFormatFileContext.Provider>,
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
describe("Normal loading flow", () => {
|
|
50
|
+
it("shows loading indicator when onLoadStart fires", () => {
|
|
51
|
+
const { getByTestId, queryByTestId } = renderWithContext();
|
|
52
|
+
const image = getByTestId("test-image");
|
|
53
|
+
|
|
54
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
55
|
+
|
|
56
|
+
fireEvent(image, "loadStart");
|
|
57
|
+
|
|
58
|
+
expect(queryByTestId("ActivityIndicator")).toBeTruthy();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("hides loading indicator when onLoadEnd fires", () => {
|
|
62
|
+
const { getByTestId, queryByTestId } = renderWithContext();
|
|
63
|
+
const image = getByTestId("test-image");
|
|
64
|
+
|
|
65
|
+
fireEvent(image, "loadStart");
|
|
66
|
+
expect(queryByTestId("ActivityIndicator")).toBeTruthy();
|
|
67
|
+
|
|
68
|
+
fireEvent(image, "loadEnd");
|
|
69
|
+
|
|
70
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("Race condition handling (cached images)", () => {
|
|
75
|
+
it("does not get stuck loading when onLoadEnd fires before onLoadStart", () => {
|
|
76
|
+
const { getByTestId, queryByTestId } = renderWithContext();
|
|
77
|
+
const image = getByTestId("test-image");
|
|
78
|
+
|
|
79
|
+
// Simulate cached image: LoadEnd fires BEFORE LoadStart
|
|
80
|
+
fireEvent(image, "loadEnd");
|
|
81
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
82
|
+
|
|
83
|
+
fireEvent(image, "loadStart");
|
|
84
|
+
|
|
85
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("does not show infinite spinner when load events fire out of order", () => {
|
|
89
|
+
const { getByTestId, queryByTestId } = renderWithContext();
|
|
90
|
+
const image = getByTestId("test-image");
|
|
91
|
+
|
|
92
|
+
// Race condition scenario
|
|
93
|
+
fireEvent(image, "loadEnd");
|
|
94
|
+
fireEvent(image, "loadStart");
|
|
95
|
+
fireEvent(image, "loadEnd");
|
|
96
|
+
|
|
97
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("URI changes", () => {
|
|
102
|
+
it("shows loading indicator when URI changes to a new image", async () => {
|
|
103
|
+
const { getByTestId, queryByTestId, rerender } = renderWithContext();
|
|
104
|
+
const image = getByTestId("test-image");
|
|
105
|
+
|
|
106
|
+
// First image: simulate cached load (LoadEnd before LoadStart)
|
|
107
|
+
fireEvent(image, "loadEnd");
|
|
108
|
+
fireEvent(image, "loadStart");
|
|
109
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
110
|
+
|
|
111
|
+
// Change URI to a new image
|
|
112
|
+
const newFile = {
|
|
113
|
+
...mockFile,
|
|
114
|
+
source: "https://example.com/image2.jpg",
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
rerender(
|
|
118
|
+
<AtlantisFormatFileContext.Provider value={mockContextValue}>
|
|
119
|
+
<MediaView {...defaultProps} file={newFile} />
|
|
120
|
+
</AtlantisFormatFileContext.Provider>,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
await waitFor(() => {
|
|
124
|
+
const updatedImage = getByTestId("test-image");
|
|
125
|
+
|
|
126
|
+
// New image starts loading
|
|
127
|
+
fireEvent(updatedImage, "loadStart");
|
|
128
|
+
|
|
129
|
+
expect(queryByTestId("ActivityIndicator")).toBeTruthy();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("shows loading indicator on second URI change", async () => {
|
|
134
|
+
const { getByTestId, queryByTestId, rerender } = renderWithContext();
|
|
135
|
+
|
|
136
|
+
const image1 = getByTestId("test-image");
|
|
137
|
+
fireEvent(image1, "loadStart");
|
|
138
|
+
fireEvent(image1, "loadEnd");
|
|
139
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
140
|
+
|
|
141
|
+
const file2 = { ...mockFile, source: "https://example.com/image2.jpg" };
|
|
142
|
+
rerender(
|
|
143
|
+
<AtlantisFormatFileContext.Provider value={mockContextValue}>
|
|
144
|
+
<MediaView {...defaultProps} file={file2} />
|
|
145
|
+
</AtlantisFormatFileContext.Provider>,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
await waitFor(() => {
|
|
149
|
+
const image2 = getByTestId("test-image");
|
|
150
|
+
fireEvent(image2, "loadStart");
|
|
151
|
+
|
|
152
|
+
expect(queryByTestId("ActivityIndicator")).toBeTruthy();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("shows loading indicator on third URI change", async () => {
|
|
157
|
+
const { getByTestId, queryByTestId, rerender } = renderWithContext();
|
|
158
|
+
|
|
159
|
+
const image1 = getByTestId("test-image");
|
|
160
|
+
fireEvent(image1, "loadStart");
|
|
161
|
+
fireEvent(image1, "loadEnd");
|
|
162
|
+
|
|
163
|
+
const file2 = { ...mockFile, source: "https://example.com/image2.jpg" };
|
|
164
|
+
rerender(
|
|
165
|
+
<AtlantisFormatFileContext.Provider value={mockContextValue}>
|
|
166
|
+
<MediaView {...defaultProps} file={file2} />
|
|
167
|
+
</AtlantisFormatFileContext.Provider>,
|
|
168
|
+
);
|
|
169
|
+
const image2 = getByTestId("test-image");
|
|
170
|
+
fireEvent(image2, "loadEnd");
|
|
171
|
+
|
|
172
|
+
const file3 = { ...mockFile, source: "https://example.com/image3.jpg" };
|
|
173
|
+
rerender(
|
|
174
|
+
<AtlantisFormatFileContext.Provider value={mockContextValue}>
|
|
175
|
+
<MediaView {...defaultProps} file={file3} />
|
|
176
|
+
</AtlantisFormatFileContext.Provider>,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
await waitFor(() => {
|
|
180
|
+
const image3 = getByTestId("test-image");
|
|
181
|
+
fireEvent(image3, "loadStart");
|
|
182
|
+
|
|
183
|
+
expect(queryByTestId("ActivityIndicator")).toBeTruthy();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("handles URI change from cached to uncached image correctly", async () => {
|
|
188
|
+
const { getByTestId, queryByTestId, rerender } = renderWithContext();
|
|
189
|
+
|
|
190
|
+
// First image: cached (LoadEnd before LoadStart)
|
|
191
|
+
const image1 = getByTestId("test-image");
|
|
192
|
+
fireEvent(image1, "loadEnd");
|
|
193
|
+
fireEvent(image1, "loadStart");
|
|
194
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
195
|
+
|
|
196
|
+
// Second image: uncached (normal LoadStart then LoadEnd)
|
|
197
|
+
const file2 = { ...mockFile, source: "https://example.com/image2.jpg" };
|
|
198
|
+
rerender(
|
|
199
|
+
<AtlantisFormatFileContext.Provider value={mockContextValue}>
|
|
200
|
+
<MediaView {...defaultProps} file={file2} />
|
|
201
|
+
</AtlantisFormatFileContext.Provider>,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
await waitFor(() => {
|
|
205
|
+
const image2 = getByTestId("test-image");
|
|
206
|
+
fireEvent(image2, "loadStart");
|
|
207
|
+
|
|
208
|
+
expect(queryByTestId("ActivityIndicator")).toBeTruthy();
|
|
209
|
+
|
|
210
|
+
fireEvent(image2, "loadEnd");
|
|
211
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("thumbnailUrl changes", () => {
|
|
217
|
+
it("shows loading indicator when thumbnailUrl changes", async () => {
|
|
218
|
+
const { getByTestId, queryByTestId, rerender } = renderWithContext();
|
|
219
|
+
|
|
220
|
+
// First load completes
|
|
221
|
+
const image1 = getByTestId("test-image");
|
|
222
|
+
fireEvent(image1, "loadStart");
|
|
223
|
+
fireEvent(image1, "loadEnd");
|
|
224
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
225
|
+
|
|
226
|
+
// Thumbnail URL changes (common when thumbnail generation completes)
|
|
227
|
+
const fileWithThumbnail = {
|
|
228
|
+
...mockFile,
|
|
229
|
+
thumbnailUrl: "https://example.com/thumbnail.jpg",
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
rerender(
|
|
233
|
+
<AtlantisFormatFileContext.Provider value={mockContextValue}>
|
|
234
|
+
<MediaView {...defaultProps} file={fileWithThumbnail} />
|
|
235
|
+
</AtlantisFormatFileContext.Provider>,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
await waitFor(() => {
|
|
239
|
+
const image2 = getByTestId("test-image");
|
|
240
|
+
fireEvent(image2, "loadStart");
|
|
241
|
+
|
|
242
|
+
expect(queryByTestId("ActivityIndicator")).toBeTruthy();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe("Context thumbnail changes", () => {
|
|
248
|
+
it("shows loading indicator when context thumbnail changes", async () => {
|
|
249
|
+
const mockContextWithThumbnail = {
|
|
250
|
+
useCreateThumbnail: () => ({
|
|
251
|
+
thumbnail: "https://example.com/context-thumbnail.jpg",
|
|
252
|
+
error: false,
|
|
253
|
+
}),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const { getByTestId, queryByTestId, rerender } = render(
|
|
257
|
+
<AtlantisFormatFileContext.Provider value={mockContextValue}>
|
|
258
|
+
<MediaView {...defaultProps} />
|
|
259
|
+
</AtlantisFormatFileContext.Provider>,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
// First load completes
|
|
263
|
+
const image1 = getByTestId("test-image");
|
|
264
|
+
fireEvent(image1, "loadStart");
|
|
265
|
+
fireEvent(image1, "loadEnd");
|
|
266
|
+
expect(queryByTestId("ActivityIndicator")).toBeNull();
|
|
267
|
+
|
|
268
|
+
// Context provides a new thumbnail
|
|
269
|
+
rerender(
|
|
270
|
+
<AtlantisFormatFileContext.Provider value={mockContextWithThumbnail}>
|
|
271
|
+
<MediaView {...defaultProps} />
|
|
272
|
+
</AtlantisFormatFileContext.Provider>,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
await waitFor(() => {
|
|
276
|
+
const image2 = getByTestId("test-image");
|
|
277
|
+
fireEvent(image2, "loadStart");
|
|
278
|
+
|
|
279
|
+
expect(queryByTestId("ActivityIndicator")).toBeTruthy();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { ImageBackground, View } from "react-native";
|
|
3
3
|
import { useStyles } from "./MediaView.style";
|
|
4
4
|
import type { FormattedFile } from "../../types";
|
|
@@ -27,11 +27,17 @@ export function MediaView({
|
|
|
27
27
|
file,
|
|
28
28
|
styleInGrid,
|
|
29
29
|
onUploadComplete,
|
|
30
|
-
}: MediaViewProps)
|
|
30
|
+
}: MediaViewProps) {
|
|
31
31
|
const { t } = useAtlantisI18n();
|
|
32
32
|
const { useCreateThumbnail } = useAtlantisFormatFileContext();
|
|
33
33
|
const { thumbnail, error } = useCreateThumbnail(file);
|
|
34
34
|
const [isLoading, setIsLoading] = useState(false);
|
|
35
|
+
/**
|
|
36
|
+
* Tracks whether onLoadEnd has fired to prevent race conditions.
|
|
37
|
+
* ImageBackground can fire onLoadEnd before onLoadStart when loading cached images,
|
|
38
|
+
* which would cause isLoading to get stuck at true, showing an infinite spinner.
|
|
39
|
+
*/
|
|
40
|
+
const hasLoadedRef = useRef(false);
|
|
35
41
|
|
|
36
42
|
const a11yLabel = computeA11yLabel({
|
|
37
43
|
accessibilityLabel,
|
|
@@ -40,10 +46,25 @@ export function MediaView({
|
|
|
40
46
|
t,
|
|
41
47
|
});
|
|
42
48
|
|
|
43
|
-
const hasError = showError || error
|
|
44
|
-
|
|
49
|
+
const hasError = showError || error,
|
|
50
|
+
uri = thumbnail || file.thumbnailUrl || file.source,
|
|
51
|
+
styles = useStyles();
|
|
45
52
|
|
|
46
|
-
const
|
|
53
|
+
const handleLoadStart = () => {
|
|
54
|
+
if (!hasLoadedRef.current) {
|
|
55
|
+
setIsLoading(true);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleLoadEnd = () => {
|
|
60
|
+
hasLoadedRef.current = true;
|
|
61
|
+
setIsLoading(false);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
hasLoadedRef.current = false;
|
|
66
|
+
setIsLoading(false);
|
|
67
|
+
}, [uri]);
|
|
47
68
|
|
|
48
69
|
return (
|
|
49
70
|
<View accessible={true} accessibilityLabel={a11yLabel}>
|
|
@@ -55,8 +76,8 @@ export function MediaView({
|
|
|
55
76
|
resizeMode={styleInGrid ? "cover" : "contain"}
|
|
56
77
|
source={{ uri }}
|
|
57
78
|
testID={"test-image"}
|
|
58
|
-
onLoadStart={
|
|
59
|
-
onLoadEnd={
|
|
79
|
+
onLoadStart={handleLoadStart}
|
|
80
|
+
onLoadEnd={handleLoadEnd}
|
|
60
81
|
>
|
|
61
82
|
<Overlay
|
|
62
83
|
isLoading={isLoading}
|
|
@@ -22,7 +22,7 @@ export const ProgressBar = ({
|
|
|
22
22
|
progress,
|
|
23
23
|
status,
|
|
24
24
|
onComplete,
|
|
25
|
-
}: ProgressBarProps)
|
|
25
|
+
}: ProgressBarProps) => {
|
|
26
26
|
const barWidth = useRef(new Animated.Value(0)).current;
|
|
27
27
|
const progressPercentage = barWidth.interpolate({
|
|
28
28
|
inputRange: [0, 1],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useIsMounted } from "@jobber/hooks
|
|
1
|
+
import { useIsMounted } from "@jobber/hooks";
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import type { UseCreateThumbnail } from "../context/types";
|
|
4
4
|
import type { CreateThumbnail, FormattedFile } from "../types";
|
|
@@ -46,7 +46,7 @@ describe("Glimmer", () => {
|
|
|
46
46
|
);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it("renders sets the correct width", () => {
|
|
49
|
+
it("renders sets the correct width", async () => {
|
|
50
50
|
jest.useFakeTimers();
|
|
51
51
|
|
|
52
52
|
// Spy on Animated.timing to verify the animation configuration
|
|
@@ -54,7 +54,7 @@ describe("Glimmer", () => {
|
|
|
54
54
|
|
|
55
55
|
render(<Glimmer />);
|
|
56
56
|
|
|
57
|
-
act(() => {
|
|
57
|
+
await act(async () => {
|
|
58
58
|
fireEvent(screen.getByTestId(GLIMMER_TEST_ID), "onLayout", {
|
|
59
59
|
nativeEvent: { layout: { width: 300 } },
|
|
60
60
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { render } from "@testing-library/react-native";
|
|
3
3
|
import { Heading } from "./Heading";
|
|
4
|
+
import { Text } from "../Text";
|
|
4
5
|
|
|
5
6
|
describe("when Heading called with text as the only prop", () => {
|
|
6
7
|
it("should match snapshot", () => {
|
|
@@ -81,3 +82,15 @@ describe("when Heading called with maxLines", () => {
|
|
|
81
82
|
expect(view).toMatchSnapshot();
|
|
82
83
|
});
|
|
83
84
|
});
|
|
85
|
+
|
|
86
|
+
describe("when Heading contains nested inline text", () => {
|
|
87
|
+
it("renders nested Text within Heading", () => {
|
|
88
|
+
const view = render(
|
|
89
|
+
<Heading>
|
|
90
|
+
Heading before <Text emphasis="strong">Inner</Text> after
|
|
91
|
+
</Heading>,
|
|
92
|
+
).toJSON();
|
|
93
|
+
|
|
94
|
+
expect(view).toMatchSnapshot();
|
|
95
|
+
});
|
|
96
|
+
});
|
package/src/Heading/Heading.tsx
CHANGED
|
@@ -19,9 +19,9 @@ export type HeadingLevel = "title" | "subtitle" | "heading" | "subHeading";
|
|
|
19
19
|
interface HeadingProps<T extends HeadingLevel>
|
|
20
20
|
extends Pick<TypographyProps<"base">, "selectable"> {
|
|
21
21
|
/**
|
|
22
|
-
* Text to display.
|
|
22
|
+
* Text to display. Supports nesting text elements.
|
|
23
23
|
*/
|
|
24
|
-
readonly children:
|
|
24
|
+
readonly children: React.ReactNode;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* The type of heading, e.g., "Title"
|
|
@@ -71,7 +71,7 @@ export function Heading<T extends HeadingLevel = "heading">({
|
|
|
71
71
|
maxLines = "unlimited",
|
|
72
72
|
allowFontScaling = true,
|
|
73
73
|
selectable,
|
|
74
|
-
}: HeadingProps<T>)
|
|
74
|
+
}: HeadingProps<T>) {
|
|
75
75
|
const headingStyle = getHeadingStyle(level, variation);
|
|
76
76
|
const accessibilityRole = "header";
|
|
77
77
|
|
|
@@ -268,3 +268,68 @@ exports[`when Heading called with title variation should match snapshot 1`] = `
|
|
|
268
268
|
Title Heading
|
|
269
269
|
</Text>
|
|
270
270
|
`;
|
|
271
|
+
|
|
272
|
+
exports[`when Heading contains nested inline text renders nested Text within Heading 1`] = `
|
|
273
|
+
<Text
|
|
274
|
+
accessibilityRole="header"
|
|
275
|
+
adjustsFontSizeToFit={false}
|
|
276
|
+
allowFontScaling={true}
|
|
277
|
+
collapsable={false}
|
|
278
|
+
selectable={true}
|
|
279
|
+
selectionColor="hsl(86, 100%, 46%)"
|
|
280
|
+
style={
|
|
281
|
+
[
|
|
282
|
+
{
|
|
283
|
+
"fontFamily": "inter-bold",
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"color": "hsl(197, 90%, 12%)",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"textAlign": "left",
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"fontSize": 20,
|
|
293
|
+
"lineHeight": 22,
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"letterSpacing": 0,
|
|
297
|
+
},
|
|
298
|
+
]
|
|
299
|
+
}
|
|
300
|
+
>
|
|
301
|
+
Heading before
|
|
302
|
+
<Text
|
|
303
|
+
accessibilityRole="text"
|
|
304
|
+
adjustsFontSizeToFit={false}
|
|
305
|
+
allowFontScaling={true}
|
|
306
|
+
collapsable={false}
|
|
307
|
+
maxFontSizeMultiplier={3.125}
|
|
308
|
+
selectable={true}
|
|
309
|
+
selectionColor="hsl(86, 100%, 46%)"
|
|
310
|
+
style={
|
|
311
|
+
[
|
|
312
|
+
{
|
|
313
|
+
"fontFamily": "inter-semibold",
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"color": "hsl(198, 35%, 21%)",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"textAlign": "left",
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"fontSize": 16,
|
|
323
|
+
"lineHeight": 20,
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"letterSpacing": 0,
|
|
327
|
+
},
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
>
|
|
331
|
+
Inner
|
|
332
|
+
</Text>
|
|
333
|
+
after
|
|
334
|
+
</Text>
|
|
335
|
+
`;
|
package/src/Icon/Icon.tsx
CHANGED
|
@@ -80,7 +80,57 @@ const getKeyboard = (props: InputCurrencyProps) => {
|
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
const computeDisplayFromNumericInput = (
|
|
84
|
+
numberedValue: number,
|
|
85
|
+
decimalNumbers: string,
|
|
86
|
+
decimalCount: number,
|
|
87
|
+
maxLength: number,
|
|
88
|
+
maxDecimalPlaces: number,
|
|
89
|
+
decimalPlaces: number,
|
|
90
|
+
formatNumber: (
|
|
91
|
+
value: number,
|
|
92
|
+
opts?: FormatNumberOptions | undefined,
|
|
93
|
+
) => string,
|
|
94
|
+
): {
|
|
95
|
+
onChangeValue: number | string;
|
|
96
|
+
displayValue: string;
|
|
97
|
+
} => {
|
|
98
|
+
const transformedValue = limitInputWholeDigits(numberedValue, maxLength);
|
|
99
|
+
const stringValue =
|
|
100
|
+
decimalNumbers !== ""
|
|
101
|
+
? transformedValue.toString() + "." + decimalNumbers.slice(1)
|
|
102
|
+
: transformedValue.toString();
|
|
103
|
+
|
|
104
|
+
if (checkLastChar(stringValue)) {
|
|
105
|
+
const roundedDecimal = configureDecimal(
|
|
106
|
+
decimalCount,
|
|
107
|
+
maxDecimalPlaces,
|
|
108
|
+
stringValue,
|
|
109
|
+
decimalPlaces,
|
|
110
|
+
);
|
|
111
|
+
const internationalizedValueToDisplay = formatNumber(roundedDecimal, {
|
|
112
|
+
maximumFractionDigits: maxDecimalPlaces,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
onChangeValue: roundedDecimal,
|
|
117
|
+
displayValue: internationalizedValueToDisplay,
|
|
118
|
+
};
|
|
119
|
+
} else {
|
|
120
|
+
const internationalizedValueToDisplay =
|
|
121
|
+
formatNumber(transformedValue, {
|
|
122
|
+
maximumFractionDigits: maxDecimalPlaces,
|
|
123
|
+
}) + decimalNumbers;
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
onChangeValue:
|
|
127
|
+
transformedValue.toString() + "." + decimalNumbers.slice(1),
|
|
128
|
+
displayValue: internationalizedValueToDisplay,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export function InputCurrency(props: InputCurrencyProps) {
|
|
84
134
|
const {
|
|
85
135
|
showCurrencySymbol = true,
|
|
86
136
|
maxDecimalPlaces = 5,
|
|
@@ -99,54 +149,6 @@ export function InputCurrency(props: InputCurrencyProps): JSX.Element {
|
|
|
99
149
|
internalValue,
|
|
100
150
|
);
|
|
101
151
|
|
|
102
|
-
const setOnChangeAndDisplayValues = (
|
|
103
|
-
onChangeValue: number | string | undefined,
|
|
104
|
-
valueToDisplay: string | undefined,
|
|
105
|
-
) => {
|
|
106
|
-
props.onChange?.(onChangeValue);
|
|
107
|
-
setDisplayValue(valueToDisplay);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const checkDecimalAndI18nOfDisplayValue = (
|
|
111
|
-
numberedValue: number,
|
|
112
|
-
decimalNumbers: string,
|
|
113
|
-
decimalCount: number,
|
|
114
|
-
) => {
|
|
115
|
-
const transformedValue = limitInputWholeDigits(numberedValue, maxLength);
|
|
116
|
-
const stringValue =
|
|
117
|
-
decimalNumbers !== ""
|
|
118
|
-
? transformedValue.toString() + "." + decimalNumbers.slice(1)
|
|
119
|
-
: transformedValue.toString();
|
|
120
|
-
|
|
121
|
-
if (checkLastChar(stringValue)) {
|
|
122
|
-
const roundedDecimal = configureDecimal(
|
|
123
|
-
decimalCount,
|
|
124
|
-
maxDecimalPlaces,
|
|
125
|
-
stringValue,
|
|
126
|
-
decimalPlaces,
|
|
127
|
-
);
|
|
128
|
-
const internationalizedValueToDisplay = intl.formatNumber(
|
|
129
|
-
roundedDecimal,
|
|
130
|
-
{
|
|
131
|
-
maximumFractionDigits: maxDecimalPlaces,
|
|
132
|
-
},
|
|
133
|
-
);
|
|
134
|
-
setOnChangeAndDisplayValues(
|
|
135
|
-
roundedDecimal,
|
|
136
|
-
internationalizedValueToDisplay,
|
|
137
|
-
);
|
|
138
|
-
} else {
|
|
139
|
-
const internationalizedValueToDisplay =
|
|
140
|
-
intl.formatNumber(transformedValue, {
|
|
141
|
-
maximumFractionDigits: maxDecimalPlaces,
|
|
142
|
-
}) + decimalNumbers;
|
|
143
|
-
setOnChangeAndDisplayValues(
|
|
144
|
-
transformedValue.toString() + "." + decimalNumbers.slice(1),
|
|
145
|
-
internationalizedValueToDisplay,
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
152
|
const handleChange = (newValue: string | undefined) => {
|
|
151
153
|
const [decimalCount, wholeIntegerValue, decimalNumbers] = parseGivenInput(
|
|
152
154
|
newValue,
|
|
@@ -158,19 +160,37 @@ export function InputCurrency(props: InputCurrencyProps): JSX.Element {
|
|
|
158
160
|
: wholeIntegerValue;
|
|
159
161
|
|
|
160
162
|
if (isValidNumber(numberedValue) && typeof numberedValue === "number") {
|
|
161
|
-
|
|
163
|
+
const result = computeDisplayFromNumericInput(
|
|
162
164
|
numberedValue,
|
|
163
165
|
decimalNumbers,
|
|
164
166
|
decimalCount,
|
|
167
|
+
maxLength,
|
|
168
|
+
maxDecimalPlaces,
|
|
169
|
+
decimalPlaces,
|
|
170
|
+
intl.formatNumber,
|
|
165
171
|
);
|
|
172
|
+
const { onChangeValue, displayValue: valueToDisplay } = result;
|
|
173
|
+
props.onChange?.(onChangeValue);
|
|
174
|
+
setDisplayValue(valueToDisplay);
|
|
166
175
|
} else {
|
|
167
176
|
const value = numberedValue?.toString() + decimalNumbers;
|
|
168
|
-
|
|
177
|
+
props.onChange?.(value);
|
|
178
|
+
setDisplayValue(value);
|
|
169
179
|
}
|
|
170
180
|
};
|
|
171
181
|
|
|
172
182
|
const { t } = useAtlantisI18n();
|
|
173
183
|
|
|
184
|
+
const defaultValidations = {
|
|
185
|
+
pattern: {
|
|
186
|
+
value: NUMBER_VALIDATION_REGEX,
|
|
187
|
+
message: t("errors.notANumber"),
|
|
188
|
+
},
|
|
189
|
+
} as const;
|
|
190
|
+
const mergedValidations = props.validations
|
|
191
|
+
? Object.assign({}, defaultValidations, props.validations)
|
|
192
|
+
: defaultValidations;
|
|
193
|
+
|
|
174
194
|
return (
|
|
175
195
|
<InputText
|
|
176
196
|
{...props}
|
|
@@ -187,13 +207,7 @@ export function InputCurrency(props: InputCurrencyProps): JSX.Element {
|
|
|
187
207
|
.replace(floatSeparators.decimal, ".");
|
|
188
208
|
},
|
|
189
209
|
}}
|
|
190
|
-
validations={
|
|
191
|
-
pattern: {
|
|
192
|
-
value: NUMBER_VALIDATION_REGEX,
|
|
193
|
-
message: t("errors.notANumber"),
|
|
194
|
-
},
|
|
195
|
-
...props.validations,
|
|
196
|
-
}}
|
|
210
|
+
validations={mergedValidations}
|
|
197
211
|
onBlur={() => {
|
|
198
212
|
props.onBlur?.();
|
|
199
213
|
|