@applicaster/zapp-react-native-ui-components 15.0.0-rc.13 → 15.0.0-rc.131
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/AnimatedInOut/index.tsx +69 -26
- package/Components/BaseFocusable/index.ios.ts +12 -2
- package/Components/Cell/Cell.tsx +14 -3
- package/Components/Cell/CellWithFocusable.tsx +9 -0
- package/Components/Cell/FocusableWrapper.tsx +3 -0
- package/Components/Cell/TvOSCellComponent.tsx +30 -6
- package/Components/Focusable/Focusable.tsx +4 -2
- package/Components/Focusable/FocusableTvOS.tsx +18 -1
- package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
- package/Components/FocusableGroup/FocusableTvOS.tsx +32 -1
- package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
- package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
- package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +22 -6
- package/Components/HandlePlayable/HandlePlayable.tsx +33 -94
- package/Components/HandlePlayable/const.ts +3 -0
- package/Components/HandlePlayable/utils.ts +105 -0
- package/Components/HookRenderer/HookRenderer.tsx +40 -10
- package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
- package/Components/Layout/TV/LayoutBackground.tsx +5 -2
- package/Components/Layout/TV/NavBarContainer.tsx +1 -10
- package/Components/Layout/TV/ScreenContainer.tsx +2 -6
- package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
- package/Components/Layout/TV/index.tsx +3 -4
- package/Components/Layout/TV/index.web.tsx +3 -4
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
- package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +30 -2
- package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
- package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
- package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +43 -22
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +196 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +40 -39
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +95 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +86 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/index.test.ts +141 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
- package/Components/MasterCell/DefaultComponents/SecondaryImage/index.ts +1 -1
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +10 -14
- package/Components/MasterCell/DefaultComponents/index.ts +2 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +46 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +126 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ButtonContainerView.ts +23 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +32 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +191 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +140 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +222 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +104 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +73 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +86 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +35 -48
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +39 -144
- package/Components/MasterCell/elementMapper.tsx +1 -0
- package/Components/MasterCell/hoc/__tests__/withAsyncRender.test.tsx +219 -0
- package/Components/MasterCell/hoc/withAsyncRender.tsx +9 -7
- package/Components/MasterCell/index.tsx +2 -0
- package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
- package/Components/MasterCell/utils/index.ts +61 -31
- package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
- package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
- package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +17 -9
- package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +16 -8
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
- package/Components/OfflineHandler/NotificationView/utils.ts +34 -0
- package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
- package/Components/PlayerContainer/PlayerContainer.tsx +43 -64
- package/Components/PlayerImageBackground/index.tsx +3 -22
- package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
- package/Components/PreloaderWrapper/index.tsx +15 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +16 -0
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
- package/Components/River/RefreshControl.tsx +36 -13
- package/Components/River/RiverItem.tsx +26 -20
- package/Components/River/TV/River.tsx +31 -14
- package/Components/River/TV/index.tsx +8 -4
- package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
- package/Components/River/TV/utils/index.ts +4 -0
- package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
- package/Components/River/__tests__/componentsMap.test.js +38 -0
- package/Components/Screen/TV/index.web.tsx +4 -2
- package/Components/Screen/__tests__/Screen.test.tsx +66 -42
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
- package/Components/Screen/hooks.ts +75 -6
- package/Components/Screen/index.tsx +9 -4
- package/Components/Screen/navigationHandler.ts +49 -24
- package/Components/Screen/orientationHandler.ts +10 -13
- package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
- package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
- package/Components/ScreenFeedLoader/index.ts +1 -0
- package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
- package/Components/ScreenResolver/hooks/index.ts +3 -0
- package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
- package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
- package/Components/ScreenResolver/index.tsx +15 -111
- package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
- package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
- package/Components/ScreenResolver/utils/index.ts +1 -0
- package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
- package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
- package/Components/ScreenResolverFeedProvider/index.ts +1 -0
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +44 -26
- package/Components/Tabs/TV/Tabs.tsx +20 -3
- package/Components/Tabs/TabContent.tsx +7 -4
- package/Components/Transitioner/Scene.tsx +10 -3
- package/Components/Transitioner/index.js +3 -3
- package/Components/VideoLive/LiveImageManager.ts +199 -54
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +118 -171
- package/Components/VideoModal/ModalAnimation/index.ts +2 -13
- package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
- package/Components/VideoModal/PlayerWrapper.tsx +14 -88
- package/Components/VideoModal/VideoModal.tsx +1 -5
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
- package/Components/VideoModal/hooks/useModalSize.ts +10 -5
- package/Components/VideoModal/playerWrapperStyle.ts +70 -0
- package/Components/VideoModal/playerWrapperUtils.ts +91 -0
- package/Components/VideoModal/utils.ts +19 -9
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
- package/Components/Viewport/ViewportAware/index.tsx +16 -7
- package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
- package/Components/ZappUIComponent/index.tsx +12 -6
- package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
- package/Components/index.js +1 -1
- package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
- package/Contexts/ScreenContext/index.tsx +71 -19
- package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
- package/Contexts/ZappHookModalContext/index.tsx +37 -61
- package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
- package/Contexts/index.ts +0 -2
- package/Decorators/Analytics/index.tsx +6 -5
- package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
- package/Decorators/ConfigurationWrapper/const.ts +1 -0
- package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
- package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
- package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
- package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
- package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
- package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
- package/Helpers/DataSourceHelper/index.ts +19 -0
- package/events/index.ts +3 -0
- package/events/scrollEndReached.ts +15 -0
- package/index.d.ts +7 -0
- package/package.json +6 -5
- package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
- package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
- package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
- package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
- package/Components/River/TV/withTVEventHandler.tsx +0 -27
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -417
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +0 -294
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +0 -93
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
- package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
- package/Helpers/DataSourceHelper/index.js +0 -19
- /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { Text, View } from "react-native";
|
|
4
|
+
import { withAsyncRenderHOC } from "../withAsyncRender";
|
|
5
|
+
|
|
6
|
+
describe("withAsyncRenderHOC", () => {
|
|
7
|
+
// Test component that requires onAsyncRender
|
|
8
|
+
const TestComponent = ({
|
|
9
|
+
onAsyncRender,
|
|
10
|
+
text,
|
|
11
|
+
}: {
|
|
12
|
+
onAsyncRender: () => void;
|
|
13
|
+
text?: string;
|
|
14
|
+
}) => {
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
onAsyncRender();
|
|
17
|
+
}, [onAsyncRender]);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<View>
|
|
21
|
+
<Text>{text || "Test"}</Text>
|
|
22
|
+
</View>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
it("should wrap component and provide onAsyncRender callback", () => {
|
|
27
|
+
const WrappedComponent = withAsyncRenderHOC(TestComponent);
|
|
28
|
+
|
|
29
|
+
const wrapper = render(<WrappedComponent text="Hello" />);
|
|
30
|
+
|
|
31
|
+
expect(wrapper.toJSON()).not.toBeNull();
|
|
32
|
+
expect(wrapper.getByText("Hello")).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should call emitAsyncElementRegistrate on mount", () => {
|
|
36
|
+
const mockRegistrate = jest.fn();
|
|
37
|
+
const WrappedComponent = withAsyncRenderHOC(TestComponent);
|
|
38
|
+
|
|
39
|
+
render(<WrappedComponent emitAsyncElementRegistrate={mockRegistrate} />);
|
|
40
|
+
|
|
41
|
+
expect(mockRegistrate).toHaveBeenCalledTimes(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should call emitAsyncElementLayout when onAsyncRender is called", () => {
|
|
45
|
+
const mockLayout = jest.fn();
|
|
46
|
+
|
|
47
|
+
const ComponentWithAsyncCall = ({
|
|
48
|
+
onAsyncRender,
|
|
49
|
+
}: {
|
|
50
|
+
onAsyncRender: () => void;
|
|
51
|
+
}) => {
|
|
52
|
+
return (
|
|
53
|
+
<View onLayout={onAsyncRender}>
|
|
54
|
+
<Text>Async Component</Text>
|
|
55
|
+
</View>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const WrappedComponent = withAsyncRenderHOC(ComponentWithAsyncCall);
|
|
60
|
+
|
|
61
|
+
const wrapper = render(
|
|
62
|
+
<WrappedComponent emitAsyncElementLayout={mockLayout} />
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Trigger onLayout
|
|
66
|
+
const view = wrapper.UNSAFE_getByType(View);
|
|
67
|
+
view.props.onLayout();
|
|
68
|
+
|
|
69
|
+
expect(mockLayout).toHaveBeenCalled();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should return unsubscribe function from emitAsyncElementRegistrate", () => {
|
|
73
|
+
const mockUnsubscribe = jest.fn();
|
|
74
|
+
const mockRegistrate = jest.fn(() => mockUnsubscribe);
|
|
75
|
+
const WrappedComponent = withAsyncRenderHOC(TestComponent);
|
|
76
|
+
|
|
77
|
+
const { unmount } = render(
|
|
78
|
+
<WrappedComponent emitAsyncElementRegistrate={mockRegistrate} />
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(mockRegistrate).toHaveBeenCalledTimes(1);
|
|
82
|
+
|
|
83
|
+
// Unmount should trigger cleanup
|
|
84
|
+
unmount();
|
|
85
|
+
|
|
86
|
+
expect(mockUnsubscribe).toHaveBeenCalled();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should work without emitAsyncElementRegistrate prop", () => {
|
|
90
|
+
const WrappedComponent = withAsyncRenderHOC(TestComponent);
|
|
91
|
+
|
|
92
|
+
const wrapper = render(<WrappedComponent text="No Registration" />);
|
|
93
|
+
|
|
94
|
+
expect(wrapper.toJSON()).not.toBeNull();
|
|
95
|
+
expect(wrapper.getByText("No Registration")).toBeDefined();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should work without emitAsyncElementLayout prop", () => {
|
|
99
|
+
const ComponentWithLayout = ({
|
|
100
|
+
onAsyncRender,
|
|
101
|
+
}: {
|
|
102
|
+
onAsyncRender: () => void;
|
|
103
|
+
}) => {
|
|
104
|
+
return (
|
|
105
|
+
<View onLayout={onAsyncRender}>
|
|
106
|
+
<Text>Test Layout</Text>
|
|
107
|
+
</View>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const WrappedComponent = withAsyncRenderHOC(ComponentWithLayout);
|
|
112
|
+
|
|
113
|
+
const wrapper = render(<WrappedComponent />);
|
|
114
|
+
|
|
115
|
+
// Should not throw when calling onLayout without emitAsyncElementLayout
|
|
116
|
+
const view = wrapper.UNSAFE_getByType(View);
|
|
117
|
+
expect(() => view.props.onLayout()).not.toThrow();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should pass through all other props to wrapped component", () => {
|
|
121
|
+
const ComponentWithProps = ({
|
|
122
|
+
customProp,
|
|
123
|
+
anotherProp,
|
|
124
|
+
}: {
|
|
125
|
+
onAsyncRender: () => void;
|
|
126
|
+
customProp: string;
|
|
127
|
+
anotherProp: number;
|
|
128
|
+
}) => {
|
|
129
|
+
return (
|
|
130
|
+
<View>
|
|
131
|
+
<Text>{customProp}</Text>
|
|
132
|
+
<Text>{anotherProp}</Text>
|
|
133
|
+
</View>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const WrappedComponent = withAsyncRenderHOC(ComponentWithProps);
|
|
138
|
+
|
|
139
|
+
const wrapper = render(
|
|
140
|
+
<WrappedComponent customProp="custom value" anotherProp={42} />
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
expect(wrapper.getByText("custom value")).toBeDefined();
|
|
144
|
+
expect(wrapper.getByText("42")).toBeDefined();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("should call emitAsyncElementLayout multiple times if onAsyncRender is called multiple times", () => {
|
|
148
|
+
const mockLayout = jest.fn();
|
|
149
|
+
|
|
150
|
+
const ComponentWithMultipleCalls = ({
|
|
151
|
+
onAsyncRender,
|
|
152
|
+
}: {
|
|
153
|
+
onAsyncRender: () => void;
|
|
154
|
+
}) => {
|
|
155
|
+
return (
|
|
156
|
+
<View>
|
|
157
|
+
<View onLayout={onAsyncRender} testID="view1">
|
|
158
|
+
<Text>First</Text>
|
|
159
|
+
</View>
|
|
160
|
+
<View onLayout={onAsyncRender} testID="view2">
|
|
161
|
+
<Text>Second</Text>
|
|
162
|
+
</View>
|
|
163
|
+
</View>
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const WrappedComponent = withAsyncRenderHOC(ComponentWithMultipleCalls);
|
|
168
|
+
|
|
169
|
+
const wrapper = render(
|
|
170
|
+
<WrappedComponent emitAsyncElementLayout={mockLayout} />
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Trigger both onLayout callbacks
|
|
174
|
+
const view1 = wrapper.getByTestId("view1");
|
|
175
|
+
const view2 = wrapper.getByTestId("view2");
|
|
176
|
+
|
|
177
|
+
view1.props.onLayout();
|
|
178
|
+
view2.props.onLayout();
|
|
179
|
+
|
|
180
|
+
expect(mockLayout).toHaveBeenCalledTimes(2);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("should handle components that use onAsyncRender in effects", () => {
|
|
184
|
+
const mockLayout = jest.fn();
|
|
185
|
+
|
|
186
|
+
const ComponentWithEffect = ({
|
|
187
|
+
onAsyncRender,
|
|
188
|
+
}: {
|
|
189
|
+
onAsyncRender: () => void;
|
|
190
|
+
}) => {
|
|
191
|
+
React.useEffect(() => {
|
|
192
|
+
// Simulate async operation completing
|
|
193
|
+
const timer = setTimeout(() => {
|
|
194
|
+
onAsyncRender();
|
|
195
|
+
}, 0);
|
|
196
|
+
|
|
197
|
+
return () => clearTimeout(timer);
|
|
198
|
+
}, [onAsyncRender]);
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<View>
|
|
202
|
+
<Text>Effect Component</Text>
|
|
203
|
+
</View>
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const WrappedComponent = withAsyncRenderHOC(ComponentWithEffect);
|
|
208
|
+
|
|
209
|
+
render(<WrappedComponent emitAsyncElementLayout={mockLayout} />);
|
|
210
|
+
|
|
211
|
+
// The effect should have been triggered
|
|
212
|
+
return new Promise((resolve) => {
|
|
213
|
+
setTimeout(() => {
|
|
214
|
+
expect(mockLayout).toHaveBeenCalled();
|
|
215
|
+
resolve(true);
|
|
216
|
+
}, 10);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -6,12 +6,12 @@ type withAsyncRenderHOCProps = {
|
|
|
6
6
|
emitAsyncElementLayout?: () => void;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
Component: React.ComponentType<
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
export const withAsyncRenderHOC = <P extends { onAsyncRender: () => void }>(
|
|
10
|
+
Component: React.ComponentType<P>
|
|
11
|
+
): React.FC<Omit<P, "onAsyncRender"> & withAsyncRenderHOCProps> => {
|
|
12
|
+
const WithAsyncRender = (
|
|
13
|
+
props: Omit<P, "onAsyncRender"> & withAsyncRenderHOCProps
|
|
14
|
+
) => {
|
|
15
15
|
const { emitAsyncElementRegistrate = noop, emitAsyncElementLayout = noop } =
|
|
16
16
|
props;
|
|
17
17
|
|
|
@@ -27,7 +27,9 @@ export const withAsyncRenderHOC: withAsyncRenderHOCT = (Component) => {
|
|
|
27
27
|
}
|
|
28
28
|
}, [emitAsyncElementLayout]);
|
|
29
29
|
|
|
30
|
-
return
|
|
30
|
+
return (
|
|
31
|
+
<Component {...(props as unknown as P)} onAsyncRender={onAsyncRender} />
|
|
32
|
+
);
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
return WithAsyncRender;
|
|
@@ -32,14 +32,16 @@ describe("resolveColor", () => {
|
|
|
32
32
|
color: "invalid_path",
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
expect(resolveColor(entry, style)).toEqual(
|
|
35
|
+
expect(resolveColor(entry, style)).toEqual({
|
|
36
|
+
color: undefined,
|
|
37
|
+
});
|
|
36
38
|
|
|
37
39
|
expect(loggerSpy).toHaveBeenCalledWith(
|
|
38
40
|
expect.objectContaining({
|
|
39
41
|
message: "Cannot resolve property invalid_path from the entry.",
|
|
40
42
|
data: {
|
|
43
|
+
colorFromProp: "invalid_path",
|
|
41
44
|
configurationValue: "invalid_path",
|
|
42
|
-
entry,
|
|
43
45
|
},
|
|
44
46
|
})
|
|
45
47
|
);
|
|
@@ -102,7 +104,9 @@ describe("resolveColor", () => {
|
|
|
102
104
|
color: "not.exist.path",
|
|
103
105
|
};
|
|
104
106
|
|
|
105
|
-
expect(resolveColor(entry, style)).toEqual(
|
|
107
|
+
expect(resolveColor(entry, style)).toEqual({
|
|
108
|
+
color: undefined,
|
|
109
|
+
});
|
|
106
110
|
});
|
|
107
111
|
|
|
108
112
|
it("not modify style with empty path", () => {
|
|
@@ -112,4 +116,79 @@ describe("resolveColor", () => {
|
|
|
112
116
|
|
|
113
117
|
expect(resolveColor(entry, style)).toEqual(style);
|
|
114
118
|
});
|
|
119
|
+
|
|
120
|
+
describe("memoization", () => {
|
|
121
|
+
beforeEach(() => {
|
|
122
|
+
// Clear memoization cache before each test
|
|
123
|
+
resolveColor.clear && resolveColor.clear();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("hits cache with same entry and style references", () => {
|
|
127
|
+
const style = { color: "extensions.color" };
|
|
128
|
+
|
|
129
|
+
const result1 = resolveColor(entry, style);
|
|
130
|
+
const result2 = resolveColor(entry, style);
|
|
131
|
+
|
|
132
|
+
expect(result1).toBe(result2); // Same object reference
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("hits cache with new references but equal entry/style values", () => {
|
|
136
|
+
const entryClone = {
|
|
137
|
+
extensions: {
|
|
138
|
+
color: "red",
|
|
139
|
+
green_color: "green",
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const style = { color: "extensions.color" };
|
|
144
|
+
const styleClone = { color: "extensions.color" };
|
|
145
|
+
|
|
146
|
+
const result1 = resolveColor(entry, style);
|
|
147
|
+
const result2 = resolveColor(entryClone, styleClone);
|
|
148
|
+
|
|
149
|
+
expect(result1).toBe(result2);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("misses cache when entry is new object", () => {
|
|
153
|
+
const entry2 = { extensions: { color: "blue" } }; // Same values, different object
|
|
154
|
+
const style = { color: "extensions.color" };
|
|
155
|
+
|
|
156
|
+
const result1 = resolveColor(entry, style);
|
|
157
|
+
const result2 = resolveColor(entry2, style);
|
|
158
|
+
|
|
159
|
+
expect(result1).not.toBe(result2); // Different object references
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("misses cache when entry property changes", () => {
|
|
163
|
+
const myEntry = {
|
|
164
|
+
extensions: {
|
|
165
|
+
color: "red",
|
|
166
|
+
green_color: "green",
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const style = { color: "extensions.color" };
|
|
171
|
+
|
|
172
|
+
const result1 = resolveColor(myEntry, style);
|
|
173
|
+
|
|
174
|
+
myEntry.extensions.color = "blue"; // Change property
|
|
175
|
+
const result2 = resolveColor(myEntry, style);
|
|
176
|
+
|
|
177
|
+
expect(result1).toEqual({ color: "red" });
|
|
178
|
+
expect(result2).toEqual({ color: "blue" });
|
|
179
|
+
expect(result1).not.toBe(result2);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("misses cache when style changes", () => {
|
|
183
|
+
const style1 = { color: "extensions.color" };
|
|
184
|
+
const style2 = { backgroundColor: "extensions.color" };
|
|
185
|
+
|
|
186
|
+
const result1 = resolveColor(entry, style1);
|
|
187
|
+
const result2 = resolveColor(entry, style2);
|
|
188
|
+
|
|
189
|
+
expect(result1).toEqual({ color: "red" });
|
|
190
|
+
expect(result2).toEqual({ backgroundColor: "red" });
|
|
191
|
+
expect(result1).not.toBe(result2);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
115
194
|
});
|
|
@@ -7,9 +7,12 @@ import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/acti
|
|
|
7
7
|
import { masterCellLogger } from "../logger";
|
|
8
8
|
import { getCellState } from "../../Cell/utils";
|
|
9
9
|
import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
10
|
+
import { get } from "@applicaster/zapp-react-native-utils/utils";
|
|
10
11
|
import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
|
|
11
12
|
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
12
13
|
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
|
|
14
|
+
import memoizee from "memoizee";
|
|
15
|
+
import stringify from "fast-json-stable-stringify";
|
|
13
16
|
|
|
14
17
|
const hasElementSpecificViewType = (viewType) => (element) => {
|
|
15
18
|
if (R.isNil(element)) {
|
|
@@ -24,21 +27,21 @@ const hasElementSpecificViewType = (viewType) => (element) => {
|
|
|
24
27
|
return hasElementsSpecificViewType(viewType)(element.elements);
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
const logWarning =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
const logWarning = (
|
|
31
|
+
colorValueFromCellStyle,
|
|
32
|
+
colorFromProp,
|
|
33
|
+
colorValueFromEntry
|
|
34
|
+
) => {
|
|
35
|
+
if (R.isNil(colorValueFromEntry)) {
|
|
36
|
+
masterCellLogger.warn({
|
|
37
|
+
message: `Cannot resolve property ${colorValueFromCellStyle} from the entry.`,
|
|
38
|
+
data: {
|
|
39
|
+
configurationValue: colorValueFromCellStyle,
|
|
40
|
+
colorFromProp,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
40
43
|
}
|
|
41
|
-
|
|
44
|
+
};
|
|
42
45
|
|
|
43
46
|
export const hasElementsSpecificViewType = (viewType) => (elements) => {
|
|
44
47
|
if (R.isNil(elements) || R.isEmpty(elements)) {
|
|
@@ -48,14 +51,22 @@ export const hasElementsSpecificViewType = (viewType) => (elements) => {
|
|
|
48
51
|
return R.any(hasElementSpecificViewType(viewType))(elements);
|
|
49
52
|
};
|
|
50
53
|
|
|
51
|
-
function resolveColorForProp(entry
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
function resolveColorForProp(entry: any, colorFromProp: string | undefined) {
|
|
55
|
+
if (!colorFromProp) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const nestedEntryValue: string | undefined = get(
|
|
60
|
+
entry,
|
|
61
|
+
colorFromProp.split(".")
|
|
62
|
+
);
|
|
54
63
|
|
|
55
64
|
const color = colorFromProp.replace(".00", "").replace(".0", ""); // https://github.com/dreamyguy/validate-color/issues/44
|
|
56
65
|
|
|
57
66
|
if (nestedEntryValue === undefined && !validateColor(color)) {
|
|
58
|
-
logWarning(colorFromProp,
|
|
67
|
+
logWarning(colorFromProp, colorFromProp, nestedEntryValue);
|
|
68
|
+
|
|
69
|
+
return undefined;
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
const colorValue = getColorFromData({
|
|
@@ -64,27 +75,46 @@ function resolveColorForProp(entry, style, colorProp) {
|
|
|
64
75
|
});
|
|
65
76
|
|
|
66
77
|
if (!colorValue) {
|
|
67
|
-
logWarning(
|
|
78
|
+
logWarning(colorFromProp, colorFromProp, nestedEntryValue);
|
|
68
79
|
|
|
69
|
-
return
|
|
80
|
+
return undefined;
|
|
70
81
|
}
|
|
71
82
|
|
|
72
|
-
return
|
|
83
|
+
return colorValue;
|
|
73
84
|
}
|
|
74
85
|
|
|
75
|
-
|
|
76
|
-
if (style === null || style === undefined) {
|
|
77
|
-
return style;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// TODO can be optimized to remove 3 O(n) loops
|
|
86
|
+
const getColorKeys = memoizee((style) => {
|
|
81
87
|
const styleKeys = Object.keys(style);
|
|
82
88
|
const colorKeys = styleKeys.filter((key) => /color/i.test(key));
|
|
83
89
|
|
|
84
|
-
return colorKeys
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
return colorKeys;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export const resolveColor = memoizee(
|
|
94
|
+
(entry, style) => {
|
|
95
|
+
if (style === null || style === undefined) {
|
|
96
|
+
return style;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return getColorKeys(style).reduce(
|
|
100
|
+
(acc, value) => {
|
|
101
|
+
if (acc[value] && typeof acc[value] === "string") {
|
|
102
|
+
const colorStyle = resolveColorForProp(entry, acc[value]);
|
|
103
|
+
|
|
104
|
+
acc[value] = colorStyle;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return acc;
|
|
108
|
+
},
|
|
109
|
+
{ ...style }
|
|
110
|
+
);
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
normalizer: (args) => {
|
|
114
|
+
return [stringify(args[0]), stringify(args[1])].join("|");
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
);
|
|
88
118
|
|
|
89
119
|
export function isVideoPreviewEnabled({
|
|
90
120
|
enable_video_preview = false,
|