@applicaster/zapp-react-native-ui-components 16.0.0-rc.12 → 16.0.0-rc.120
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/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
- package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
- package/Components/AudioPlayer/tv/index.tsx +5 -1
- package/Components/Cell/Cell.tsx +3 -6
- package/Components/Cell/TvOSCellComponent.tsx +0 -1
- package/Components/CellRendererResolver/index.ts +1 -1
- package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
- package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
- package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
- package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
- package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
- package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
- package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
- package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
- package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
- package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
- package/Components/GeneralContentScreen/index.ts +2 -0
- package/Components/Layout/FullWidthRow.tsx +38 -0
- package/Components/LinkHandler/LinkHandler.tsx +2 -7
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
- package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
- package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
- package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -6
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
- package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +34 -21
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
- package/Components/MasterCell/__tests__/masterCell.test.js +60 -0
- package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
- package/Components/MasterCell/dataAdapter.ts +15 -5
- package/Components/MasterCell/hooks/index.ts +2 -0
- package/Components/MasterCell/hooks/usePressedState.ts +4 -0
- package/Components/MasterCell/index.tsx +57 -8
- package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
- package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
- package/Components/MasterCell/utils/__tests__/withoutFocusedState.test.js +20 -0
- package/Components/MasterCell/utils/index.ts +103 -46
- package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
- package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
- package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
- package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
- package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1028 -0
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +164 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +433 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +563 -0
- package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +440 -50
- package/Components/ModalComponent/Button/index.tsx +5 -3
- package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
- package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
- package/Components/ModalComponent/Header/index.tsx +20 -7
- package/Components/ModalComponent/Header/utils.ts +1 -1
- package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
- package/Components/ModalComponent/SortableList.tsx +156 -0
- package/Components/ModalComponent/SortableList.web.tsx +34 -0
- package/Components/ModalComponent/TextInputContent.tsx +135 -0
- package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
- package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
- package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
- package/Components/ModalComponent/__tests__/showTextInput.test.ts +120 -0
- package/Components/ModalComponent/index.tsx +18 -9
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
- package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
- package/Components/ModalComponent/presets/index.ts +27 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
- package/Components/ModalComponent/presets/standardCell.tsx +41 -0
- package/Components/ModalComponent/presets/types.ts +35 -0
- package/Components/ModalComponent/showTextInput.ts +54 -0
- package/Components/ModalComponent/utils.ts +82 -21
- package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
- package/Components/OfflineHandler/__tests__/index.test.tsx +186 -17
- package/Components/OfflineHandler/hooks.ts +111 -0
- package/Components/OfflineHandler/index.tsx +91 -75
- package/Components/OfflineHandler/utils/index.ts +1 -13
- package/Components/PlayerContainer/PlayerContainer.tsx +28 -11
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
- package/Components/PreloaderWrapper/index.tsx +1 -1
- package/Components/River/ComponentsMap/ComponentsMap.tsx +91 -62
- package/Components/River/River.tsx +4 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +142 -89
- package/Components/River/__tests__/componentsMap.test.js +8 -3
- package/Components/River/__tests__/resultCallback.test.tsx +47 -0
- package/Components/Screen/__tests__/Screen.test.tsx +24 -0
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
- package/Components/Screen/index.tsx +13 -4
- package/Components/Screen/navigationHandler.ts +8 -8
- package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
- package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
- package/Components/TopCutoffOverlay/index.tsx +2 -2
- package/Components/Transitioner/AnimationManager.js +1 -1
- package/Components/Transitioner/Scene.tsx +4 -3
- package/Components/Transitioner/Transitioner.tsx +98 -49
- package/Components/Transitioner/__tests__/transitioner.test.js +372 -3
- package/Components/VideoLive/LiveImageManager.ts +9 -3
- package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
- package/Components/VideoModal/PlayerWrapper.tsx +4 -1
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
- package/Components/VideoModal/__tests__/playerWrapperStyle.test.ts +58 -0
- package/Components/VideoModal/__tests__/playerWrapperUtils.test.ts +16 -0
- package/Components/VideoModal/playerWrapperStyle.ts +21 -3
- package/Components/VideoModal/playerWrapperUtils.ts +7 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
- package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
- package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
- package/Contexts/CachedDimensionsContext/index.ts +17 -2
- package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
- package/Contexts/ModalNavigationContext/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/__tests__/screenToolbox.test.ts +347 -0
- package/Decorators/ZappPipesDataConnector/__tests__/screenToolboxUnavailable.test.ts +53 -0
- package/Decorators/ZappPipesDataConnector/index.tsx +19 -4
- package/Decorators/ZappPipesDataConnector/screenToolbox.ts +384 -0
- package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
- package/Helpers/index.js +0 -39
- package/Hooks/useEntryActionsExpander.ts +63 -0
- package/assets/empty-Info.png +0 -0
- package/package.json +6 -5
- package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
- package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
- package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
- package/Components/Screen/utils.ts +0 -16
- package/Helpers/Analytics/index.js +0 -95
- /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
- /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
- /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
- /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { StyleSheet,
|
|
3
|
-
|
|
2
|
+
import { StyleSheet, SafeAreaView as RNSafeAreaView } from "react-native";
|
|
3
|
+
import { SafeAreaView } from "react-native-safe-area-context";
|
|
4
4
|
import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
5
5
|
import CloseButton from "./Buttons/CloseButton";
|
|
6
6
|
import BackButton from "./Buttons/BackButton";
|
|
7
7
|
import BarTitle from "./BarTitle";
|
|
8
|
+
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
8
9
|
|
|
9
10
|
type Props = {
|
|
10
11
|
screenStyles: {
|
|
@@ -23,6 +24,11 @@ const defaultState: NavBarState = {
|
|
|
23
24
|
title: "",
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
const SafeAreaViewPlatform = platformSelect({
|
|
28
|
+
ios: RNSafeAreaView,
|
|
29
|
+
android: SafeAreaView,
|
|
30
|
+
});
|
|
31
|
+
|
|
26
32
|
const BarView = ({ screenStyles, barState = defaultState }: Props) => {
|
|
27
33
|
const isRTL = useIsRTL();
|
|
28
34
|
|
|
@@ -44,14 +50,15 @@ const BarView = ({ screenStyles, barState = defaultState }: Props) => {
|
|
|
44
50
|
);
|
|
45
51
|
|
|
46
52
|
return (
|
|
47
|
-
<
|
|
53
|
+
<SafeAreaViewPlatform
|
|
54
|
+
edges={["top", "left", "right"]}
|
|
48
55
|
style={[style.view, isRTL ? style.rtlStyle : {}]}
|
|
49
|
-
testID="BarView-
|
|
56
|
+
testID="BarView-safeAreaView"
|
|
50
57
|
>
|
|
51
58
|
{backButton}
|
|
52
59
|
<BarTitle title={barState.title} screenStyles={screenStyles} />
|
|
53
60
|
{closeButton}
|
|
54
|
-
</
|
|
61
|
+
</SafeAreaViewPlatform>
|
|
55
62
|
);
|
|
56
63
|
};
|
|
57
64
|
|
|
@@ -46,7 +46,7 @@ describe("BarView", () => {
|
|
|
46
46
|
<BarView screenStyles={screenStyles} barState={barState} />
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
-
expect(getByTestId("BarView-
|
|
49
|
+
expect(getByTestId("BarView-safeAreaView").props.style).toContainEqual({
|
|
50
50
|
transform: [{ scaleX: -1 }],
|
|
51
51
|
});
|
|
52
52
|
});
|
|
@@ -58,7 +58,7 @@ describe("BarView", () => {
|
|
|
58
58
|
<BarView screenStyles={screenStyles} barState={barState} />
|
|
59
59
|
);
|
|
60
60
|
|
|
61
|
-
expect(getByTestId("BarView-
|
|
61
|
+
expect(getByTestId("BarView-safeAreaView").props.style).not.toContainEqual({
|
|
62
62
|
transform: [{ scaleX: -1 }],
|
|
63
63
|
});
|
|
64
64
|
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { renderHook, act } from "@testing-library/react-native";
|
|
2
|
+
|
|
3
|
+
type DimensionsStore = {
|
|
4
|
+
setState: (
|
|
5
|
+
partial: { dimensions: Record<string, unknown>; ids: string[] },
|
|
6
|
+
replace?: boolean
|
|
7
|
+
) => void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const createdStores: DimensionsStore[] = [];
|
|
11
|
+
|
|
12
|
+
jest.mock("zustand", () => {
|
|
13
|
+
const actual = jest.requireActual<typeof import("zustand")>("zustand");
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
...actual,
|
|
17
|
+
create: ((initializer: Parameters<typeof actual.create>[0]) => {
|
|
18
|
+
const store = actual.create(initializer);
|
|
19
|
+
createdStores.push(store);
|
|
20
|
+
|
|
21
|
+
return store;
|
|
22
|
+
}) as typeof actual.create,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
import { useCachedDimensions } from "../index";
|
|
27
|
+
|
|
28
|
+
const resetStore = () => {
|
|
29
|
+
const store = createdStores[createdStores.length - 1];
|
|
30
|
+
|
|
31
|
+
act(() => {
|
|
32
|
+
store?.setState({ dimensions: {}, ids: [] });
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe("useCachedDimensions", () => {
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
resetStore();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("getCachedDimensions", () => {
|
|
42
|
+
it("returns default dimensions when id has no cached value", () => {
|
|
43
|
+
const { result } = renderHook(() => useCachedDimensions("component-1"));
|
|
44
|
+
|
|
45
|
+
expect(result.current.getCachedDimensions()).toEqual({
|
|
46
|
+
width: undefined,
|
|
47
|
+
height: undefined,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("returns cached dimensions after setCachedDimensions", () => {
|
|
52
|
+
const { result } = renderHook(() => useCachedDimensions("component-1"));
|
|
53
|
+
const dimensions = { width: 100, height: 200 };
|
|
54
|
+
|
|
55
|
+
act(() => {
|
|
56
|
+
result.current.setCachedDimensions(dimensions);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
expect(result.current.getCachedDimensions()).toEqual(dimensions);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("setCachedDimensions", () => {
|
|
64
|
+
it("updates dimensions for the hook id", () => {
|
|
65
|
+
const { result } = renderHook(() => useCachedDimensions("component-1"));
|
|
66
|
+
|
|
67
|
+
act(() => {
|
|
68
|
+
result.current.setCachedDimensions({ width: 50, height: 75 });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(result.current.getCachedDimensions()).toEqual({
|
|
72
|
+
width: 50,
|
|
73
|
+
height: 75,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("shares cached dimensions between hook instances with the same id", () => {
|
|
78
|
+
const { result: first } = renderHook(() =>
|
|
79
|
+
useCachedDimensions("shared-id")
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const { result: second } = renderHook(() =>
|
|
83
|
+
useCachedDimensions("shared-id")
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
act(() => {
|
|
87
|
+
first.current.setCachedDimensions({ width: 10, height: 20 });
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(second.current.getCachedDimensions()).toEqual({
|
|
91
|
+
width: 10,
|
|
92
|
+
height: 20,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("getLastCachedDimensions", () => {
|
|
98
|
+
it("returns undefined when no dimensions have been cached", () => {
|
|
99
|
+
const { result } = renderHook(() => useCachedDimensions("component-1"));
|
|
100
|
+
|
|
101
|
+
expect(result.current.getLastCachedDimensions()).toBeUndefined();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("returns the most recently cached dimensions across ids", () => {
|
|
105
|
+
const { result: first } = renderHook(() => useCachedDimensions("first"));
|
|
106
|
+
|
|
107
|
+
const { result: second } = renderHook(() =>
|
|
108
|
+
useCachedDimensions("second")
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
act(() => {
|
|
112
|
+
first.current.setCachedDimensions({ width: 100, height: 100 });
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
act(() => {
|
|
116
|
+
second.current.setCachedDimensions({ width: 200, height: 200 });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(first.current.getLastCachedDimensions()).toEqual({
|
|
120
|
+
width: 200,
|
|
121
|
+
height: 200,
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("returns the latest dimensions when the same id is updated multiple times", () => {
|
|
126
|
+
const { result } = renderHook(() => useCachedDimensions("component-1"));
|
|
127
|
+
|
|
128
|
+
act(() => {
|
|
129
|
+
result.current.setCachedDimensions({ width: 100, height: 100 });
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
act(() => {
|
|
133
|
+
result.current.setCachedDimensions({ width: 300, height: 400 });
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(result.current.getLastCachedDimensions()).toEqual({
|
|
137
|
+
width: 300,
|
|
138
|
+
height: 400,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("return value", () => {
|
|
144
|
+
it("exposes getCachedDimensions, setCachedDimensions, and getLastCachedDimensions", () => {
|
|
145
|
+
const { result } = renderHook(() => useCachedDimensions("component-1"));
|
|
146
|
+
|
|
147
|
+
expect(result.current).toEqual({
|
|
148
|
+
getCachedDimensions: expect.any(Function),
|
|
149
|
+
setCachedDimensions: expect.any(Function),
|
|
150
|
+
getLastCachedDimensions: expect.any(Function),
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { create, useStore } from "zustand";
|
|
2
2
|
import { produce } from "immer";
|
|
3
3
|
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { last } from "@applicaster/zapp-react-native-utils/utils";
|
|
4
5
|
|
|
5
6
|
type Dimensions = {
|
|
6
7
|
width: Option<number>;
|
|
@@ -10,14 +11,17 @@ type Dimensions = {
|
|
|
10
11
|
type DimensionsState = {
|
|
11
12
|
dimensions: Record<string, Dimensions>;
|
|
12
13
|
setCachedDimensions: (id: string, dimensions: Dimensions) => void;
|
|
14
|
+
ids: string[];
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
const dimensionsStore = create<DimensionsState>((set) => ({
|
|
16
18
|
dimensions: {},
|
|
19
|
+
ids: [],
|
|
17
20
|
setCachedDimensions: (id, dimensions) =>
|
|
18
21
|
set(
|
|
19
22
|
produce((draft) => {
|
|
20
23
|
draft.dimensions[id] = dimensions;
|
|
24
|
+
draft.ids.push(id);
|
|
21
25
|
})
|
|
22
26
|
),
|
|
23
27
|
}));
|
|
@@ -28,7 +32,7 @@ const initialDimensions = () => ({
|
|
|
28
32
|
});
|
|
29
33
|
|
|
30
34
|
export const useCachedDimensions = (id: string) => {
|
|
31
|
-
const { dimensions, setCachedDimensions } = useStore(
|
|
35
|
+
const { dimensions, setCachedDimensions, ids } = useStore(
|
|
32
36
|
dimensionsStore
|
|
33
37
|
) as DimensionsState;
|
|
34
38
|
|
|
@@ -40,11 +44,22 @@ export const useCachedDimensions = (id: string) => {
|
|
|
40
44
|
setCachedDimensions(id, newDimensions);
|
|
41
45
|
}, []);
|
|
42
46
|
|
|
47
|
+
const getLastCachedDimensions = useCallback(() => {
|
|
48
|
+
const previousId = last(ids);
|
|
49
|
+
|
|
50
|
+
if (!previousId) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return dimensions[previousId];
|
|
55
|
+
}, [dimensions, ids]);
|
|
56
|
+
|
|
43
57
|
return useMemo(
|
|
44
58
|
() => ({
|
|
45
59
|
getCachedDimensions,
|
|
46
60
|
setCachedDimensions: handleSetCachedDimensions,
|
|
61
|
+
getLastCachedDimensions,
|
|
47
62
|
}),
|
|
48
|
-
[getCachedDimensions, handleSetCachedDimensions]
|
|
63
|
+
[getCachedDimensions, handleSetCachedDimensions, getLastCachedDimensions]
|
|
49
64
|
);
|
|
50
65
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { withModalNavigationContextProvider } from "../";
|
|
4
|
+
|
|
5
|
+
describe("withModalNavigationContextProvider", () => {
|
|
6
|
+
it("does not re-render the wrapped component when props are unchanged", () => {
|
|
7
|
+
const Component = jest.fn(() => null);
|
|
8
|
+
const WrappedComponent = withModalNavigationContextProvider(Component);
|
|
9
|
+
const { rerender } = render(<WrappedComponent title="Modal" />);
|
|
10
|
+
|
|
11
|
+
rerender(<WrappedComponent title="Modal" />);
|
|
12
|
+
|
|
13
|
+
expect(Component).toHaveBeenCalledTimes(1);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -6,11 +6,11 @@ export function useModalNavigationContext() {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function withModalNavigationContextProvider(Component) {
|
|
9
|
-
return function WrappedWithModalNavigationContextProvider(props) {
|
|
9
|
+
return React.memo(function WrappedWithModalNavigationContextProvider(props) {
|
|
10
10
|
return (
|
|
11
11
|
<ModalNavigationContext.Provider value={true}>
|
|
12
12
|
<Component {...props} />
|
|
13
13
|
</ModalNavigationContext.Provider>
|
|
14
14
|
);
|
|
15
|
-
};
|
|
15
|
+
});
|
|
16
16
|
}
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react-native";
|
|
2
|
+
|
|
3
|
+
const mockRegister = jest.fn().mockResolvedValue(undefined);
|
|
4
|
+
|
|
5
|
+
// The X-Ray mock (__mocks__/@applicaster/quick-brick-xray) has no toolbox API: give it one.
|
|
6
|
+
require("@applicaster/quick-brick-xray").registerReactToolbox = mockRegister;
|
|
7
|
+
|
|
8
|
+
// Required after the toolbox API is in place: loading them can register the player toolbox.
|
|
9
|
+
const {
|
|
10
|
+
modalStore,
|
|
11
|
+
} = require("@applicaster/zapp-react-native-utils/modalState");
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
setXrayNavigator,
|
|
15
|
+
} = require("@applicaster/zapp-react-native-utils/navigationUtils/xrayNavigator");
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
zappHookModalStore,
|
|
19
|
+
} = require("@applicaster/zapp-react-native-ui-components/Contexts/ZappHookModalContext");
|
|
20
|
+
|
|
21
|
+
const mockStore = {
|
|
22
|
+
rivers: { home: { name: "Home", type: "general_content" } },
|
|
23
|
+
plugins: [
|
|
24
|
+
{ identifier: "quick-brick-epg-screen", name: "EPG" },
|
|
25
|
+
{ identifier: "login-plugin", name: "Login" },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
jest.mock("@applicaster/zapp-react-native-redux/AppStore", () => ({
|
|
30
|
+
appStore: { get: (key) => mockStore[key] },
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const { useScreenToolboxRecord, getTopPathname } = require("../screenToolbox");
|
|
34
|
+
|
|
35
|
+
const home = { id: "home", name: "Home", type: "general_content" };
|
|
36
|
+
|
|
37
|
+
const stack = {
|
|
38
|
+
"/river/home": { screen: home },
|
|
39
|
+
"/river/epg": {
|
|
40
|
+
screen: { id: "epg", name: "Guide", type: "quick-brick-epg-screen" },
|
|
41
|
+
},
|
|
42
|
+
"/river/tabs": {
|
|
43
|
+
screen: { id: "tabs", name: "Tabs", type: "quick-brick-tabs" },
|
|
44
|
+
nested: { screen: { id: "t1", name: "Tab 1", type: "general_content" } },
|
|
45
|
+
},
|
|
46
|
+
"/river/home/hooks/login": {
|
|
47
|
+
screen: { identifier: "login-plugin", name: "Login" },
|
|
48
|
+
entry: { payload: { id: "e1", title: "One" } },
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const navigate = (pathname: string, videoModalState = { visible: false }) =>
|
|
53
|
+
setXrayNavigator({
|
|
54
|
+
getPathname: () => pathname,
|
|
55
|
+
getStackForPathname: (route) =>
|
|
56
|
+
stack[route] ? { route, state: stack[route] } : undefined,
|
|
57
|
+
videoModalState,
|
|
58
|
+
} as unknown as QuickBrickAppNavigator);
|
|
59
|
+
|
|
60
|
+
const mount = (
|
|
61
|
+
component,
|
|
62
|
+
index,
|
|
63
|
+
feed?,
|
|
64
|
+
parentId?,
|
|
65
|
+
pathname = "/river/home"
|
|
66
|
+
) => {
|
|
67
|
+
const hook = renderHook(() =>
|
|
68
|
+
useScreenToolboxRecord(component, "home", index, parentId, pathname)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
hook.result.current.feed = feed;
|
|
72
|
+
|
|
73
|
+
return hook;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const current = (args = {}) => {
|
|
77
|
+
// Other toolboxes (player) may register first.
|
|
78
|
+
const { tools } = mockRegister.mock.calls.find(
|
|
79
|
+
([toolbox]) => toolbox.name === "screen"
|
|
80
|
+
)[0];
|
|
81
|
+
|
|
82
|
+
return tools.find((tool) => tool.name === "current").handler(args);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const presentHook = (overrides = {}) =>
|
|
86
|
+
zappHookModalStore.setState({
|
|
87
|
+
state: {
|
|
88
|
+
path: "/hooks/login",
|
|
89
|
+
screenData: {
|
|
90
|
+
hookPlugin: { identifier: "login-plugin", name: "Login" },
|
|
91
|
+
payload: { id: "e1", title: "One" },
|
|
92
|
+
},
|
|
93
|
+
} as any,
|
|
94
|
+
isHooksExecutionInProgress: true,
|
|
95
|
+
isRunningInBackground: false,
|
|
96
|
+
...overrides,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("screenToolbox", () => {
|
|
100
|
+
// Registers the toolbox; the registration is kept for the whole module, as in the app.
|
|
101
|
+
beforeAll(() => mount({ id: "init" }, 0).unmount());
|
|
102
|
+
|
|
103
|
+
beforeEach(() => navigate("/river/home"));
|
|
104
|
+
|
|
105
|
+
afterEach(() => {
|
|
106
|
+
modalStore.getState().setModalState({ visible: false, screen: null });
|
|
107
|
+
zappHookModalStore.getState().resetState();
|
|
108
|
+
zappHookModalStore.getState().setIsHooksExecutionInProgress(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("returns the top screen's mounted components in order, with feed and entries", () => {
|
|
112
|
+
const second = mount({ id: "c2", component_type: "grid" }, 1, {
|
|
113
|
+
url: "https://feed/2",
|
|
114
|
+
loading: true,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const first = mount({ id: "c1", component_type: "horizontal_list" }, 0, {
|
|
118
|
+
url: "https://feed/1",
|
|
119
|
+
loading: false,
|
|
120
|
+
data: {
|
|
121
|
+
entry: [
|
|
122
|
+
{ id: "e1", title: "One" },
|
|
123
|
+
{ id: "e2", title: "Two" },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const third = mount({ id: "c3", component_type: "grid" }, 2, {
|
|
129
|
+
url: "https://feed/3",
|
|
130
|
+
error: new Error("404"),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
expect(current({ limit: 1 })).toEqual({
|
|
134
|
+
pathname: "/river/home",
|
|
135
|
+
screen: {
|
|
136
|
+
kind: "river",
|
|
137
|
+
id: "home",
|
|
138
|
+
name: "Home",
|
|
139
|
+
type: "general_content",
|
|
140
|
+
},
|
|
141
|
+
entry: null,
|
|
142
|
+
rivers: [
|
|
143
|
+
{
|
|
144
|
+
id: "home",
|
|
145
|
+
name: "Home",
|
|
146
|
+
type: "general_content",
|
|
147
|
+
components: [
|
|
148
|
+
{
|
|
149
|
+
index: 0,
|
|
150
|
+
id: "c1",
|
|
151
|
+
type: "horizontal_list",
|
|
152
|
+
feed: { url: "https://feed/1", state: "loaded", count: 2 },
|
|
153
|
+
entries: [{ index: 0, id: "e1", title: "One" }],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
index: 1,
|
|
157
|
+
id: "c2",
|
|
158
|
+
type: "grid",
|
|
159
|
+
feed: { url: "https://feed/2", state: "loading", count: 0 },
|
|
160
|
+
entries: [],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
index: 2,
|
|
164
|
+
id: "c3",
|
|
165
|
+
type: "grid",
|
|
166
|
+
feed: {
|
|
167
|
+
url: "https://feed/3",
|
|
168
|
+
state: "error",
|
|
169
|
+
error: "404",
|
|
170
|
+
count: 0,
|
|
171
|
+
},
|
|
172
|
+
entries: [],
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
mounted: ["/river/home"],
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
[first, second, third].forEach((hook) => hook.unmount());
|
|
181
|
+
|
|
182
|
+
expect(current().rivers).toEqual([]);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("nests a group's children under it", () => {
|
|
186
|
+
const group = mount({ id: "g", component_type: "group" }, 1);
|
|
187
|
+
|
|
188
|
+
const child = mount(
|
|
189
|
+
{ id: "g0", component_type: "hero" },
|
|
190
|
+
0,
|
|
191
|
+
undefined,
|
|
192
|
+
"g"
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const row = mount({ id: "r", component_type: "grid" }, 0);
|
|
196
|
+
|
|
197
|
+
expect(
|
|
198
|
+
current().rivers[0].components.map(({ id, children }) => ({
|
|
199
|
+
id,
|
|
200
|
+
children: children?.map((item) => item.id),
|
|
201
|
+
}))
|
|
202
|
+
).toEqual([
|
|
203
|
+
{ id: "r", children: undefined },
|
|
204
|
+
{ id: "g", children: ["g0"] },
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
[group, child, row].forEach((hook) => hook.unmount());
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("reads another mounted screen by pathname", () => {
|
|
211
|
+
const other = mount({ id: "c" }, 0, undefined, undefined, "/river/epg");
|
|
212
|
+
|
|
213
|
+
expect(current({ pathname: "/river/epg" }).rivers[0].components).toEqual([
|
|
214
|
+
expect.objectContaining({ id: "c" }),
|
|
215
|
+
]);
|
|
216
|
+
|
|
217
|
+
expect(current().mounted).toEqual(["/river/epg"]);
|
|
218
|
+
|
|
219
|
+
other.unmount();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("names the plugin that renders a screen plugin", () => {
|
|
223
|
+
navigate("/river/epg");
|
|
224
|
+
|
|
225
|
+
expect(current().screen).toEqual({
|
|
226
|
+
kind: "plugin",
|
|
227
|
+
id: "epg",
|
|
228
|
+
name: "Guide",
|
|
229
|
+
type: "quick-brick-epg-screen",
|
|
230
|
+
plugin: { identifier: "quick-brick-epg-screen", name: "EPG" },
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("reports the nested tab screen", () => {
|
|
235
|
+
navigate("/river/tabs");
|
|
236
|
+
|
|
237
|
+
expect(current().nested).toEqual({
|
|
238
|
+
id: "t1",
|
|
239
|
+
name: "Tab 1",
|
|
240
|
+
type: "general_content",
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("unwraps the entry of a hook screen on the stack", () => {
|
|
245
|
+
navigate("/river/home/hooks/login");
|
|
246
|
+
|
|
247
|
+
expect(current()).toMatchObject({
|
|
248
|
+
screen: { kind: "hook", plugin: { identifier: "login-plugin" } },
|
|
249
|
+
entry: { id: "e1", title: "One" },
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("says when there is no screen yet", () => {
|
|
254
|
+
navigate("/");
|
|
255
|
+
|
|
256
|
+
expect(current().screen).toEqual({ kind: "none" });
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("reads a modal's screen from the modal store, its pathname as ModalProvider builds it", () => {
|
|
260
|
+
modalStore.getState().openModal({ item: home, props: { screenId: "m1" } });
|
|
261
|
+
|
|
262
|
+
expect(current()).toMatchObject({
|
|
263
|
+
pathname: "modal/m1",
|
|
264
|
+
screen: { kind: "river", id: "home" },
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("describes a modal hook by its plugin", () => {
|
|
269
|
+
modalStore.getState().openModal({
|
|
270
|
+
item: {
|
|
271
|
+
hookPlugin: { identifier: "login-plugin", name: "Login" },
|
|
272
|
+
payload: { id: "e1", title: "One" },
|
|
273
|
+
},
|
|
274
|
+
props: { screenType: "hooks" },
|
|
275
|
+
} as any);
|
|
276
|
+
|
|
277
|
+
expect(current()).toMatchObject({
|
|
278
|
+
screen: {
|
|
279
|
+
kind: "hook",
|
|
280
|
+
plugin: { identifier: "login-plugin", name: "Login" },
|
|
281
|
+
},
|
|
282
|
+
entry: { id: "e1", title: "One" },
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it("describes a bottom sheet", () => {
|
|
287
|
+
modalStore.getState().openModal({
|
|
288
|
+
item: { modalBottomSheetContentProps: {} },
|
|
289
|
+
} as any);
|
|
290
|
+
|
|
291
|
+
expect(current().screen).toEqual({ kind: "bottomSheet" });
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("puts a modal over the video modal, and the video modal over the stack unless docked", () => {
|
|
295
|
+
const item = { id: "v1", title: "Video" };
|
|
296
|
+
|
|
297
|
+
navigate("/river/home", {
|
|
298
|
+
visible: true,
|
|
299
|
+
mode: "MAXIMIZED",
|
|
300
|
+
item,
|
|
301
|
+
} as any);
|
|
302
|
+
|
|
303
|
+
expect(current()).toMatchObject({
|
|
304
|
+
pathname: "video-modal/v1",
|
|
305
|
+
screen: { kind: "player" },
|
|
306
|
+
entry: { id: "v1", title: "Video" },
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
modalStore.getState().openModal({ item: home });
|
|
310
|
+
|
|
311
|
+
expect(getTopPathname()).toBe("modal/home");
|
|
312
|
+
|
|
313
|
+
modalStore.getState().setModalState({ visible: false, screen: null });
|
|
314
|
+
|
|
315
|
+
navigate("/river/home", {
|
|
316
|
+
visible: true,
|
|
317
|
+
mode: "MINIMIZED",
|
|
318
|
+
item,
|
|
319
|
+
} as any);
|
|
320
|
+
|
|
321
|
+
expect(getTopPathname()).toBe("/river/home");
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("puts a presented hook screen on top, not one running in the background", () => {
|
|
325
|
+
presentHook();
|
|
326
|
+
|
|
327
|
+
expect(current()).toMatchObject({
|
|
328
|
+
pathname: "hooks-modal/Login",
|
|
329
|
+
screen: {
|
|
330
|
+
kind: "hook",
|
|
331
|
+
name: "Login",
|
|
332
|
+
plugin: { identifier: "login-plugin" },
|
|
333
|
+
},
|
|
334
|
+
entry: { id: "e1", title: "One" },
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
presentHook({ isRunningInBackground: true });
|
|
338
|
+
|
|
339
|
+
expect(getTopPathname()).toBe("/river/home");
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("describes nothing for a modal pathname that is no longer presented", () => {
|
|
343
|
+
expect(current({ pathname: "modal/gone" }).screen).toEqual({
|
|
344
|
+
kind: "none",
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
});
|