@applicaster/zapp-react-native-ui-components 14.0.0-rc.9 → 15.0.0-alpha.2239032089

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/index.tsx +15 -0
  3. package/Components/AudioPlayer/mobile/Layout.tsx +66 -0
  4. package/Components/AudioPlayer/{__tests__/__snapshots__/audioPlayer.test.js.snap → mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap} +2 -2
  5. package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +18 -0
  6. package/Components/AudioPlayer/mobile/index.tsx +18 -0
  7. package/Components/AudioPlayer/{Artwork.tsx → tv/Artwork.tsx} +3 -2
  8. package/Components/AudioPlayer/{Channel.tsx → tv/Channel.tsx} +7 -7
  9. package/Components/AudioPlayer/tv/Layout.tsx +168 -0
  10. package/Components/AudioPlayer/{Runtime.tsx → tv/Runtime.tsx} +7 -1
  11. package/Components/AudioPlayer/{Summary.tsx → tv/Summary.tsx} +6 -2
  12. package/Components/AudioPlayer/{Title.tsx → tv/Title.tsx} +6 -2
  13. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/Runtime.test.js.snap +2 -2
  14. package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +164 -0
  15. package/Components/AudioPlayer/tv/__tests__/__snapshots__/channel.test.js.snap +19 -0
  16. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/summary.test.js.snap +1 -2
  17. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/title.test.js.snap +1 -2
  18. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/audioPlayer.test.js +7 -3
  19. package/Components/AudioPlayer/{helpers.tsx → tv/helpers.tsx} +11 -5
  20. package/Components/AudioPlayer/{AudioPlayer.tsx → tv/index.tsx} +17 -58
  21. package/Components/AudioPlayer/types.ts +40 -0
  22. package/Components/BaseFocusable/index.tsx +23 -12
  23. package/Components/Cell/Cell.tsx +91 -64
  24. package/Components/Cell/CellWithFocusable.tsx +3 -0
  25. package/Components/Cell/__tests__/CellWIthFocusable.test.js +3 -2
  26. package/Components/Cell/index.js +7 -3
  27. package/Components/ComponentResolver/index.ts +1 -1
  28. package/Components/FeedLoader/FeedLoader.tsx +7 -16
  29. package/Components/FeedLoader/FeedLoaderHOC.tsx +21 -0
  30. package/Components/FeedLoader/index.js +2 -8
  31. package/Components/Focusable/Focusable.tsx +16 -5
  32. package/Components/Focusable/FocusableTvOS.tsx +10 -6
  33. package/Components/Focusable/FocusableiOS.tsx +2 -2
  34. package/Components/Focusable/Touchable.tsx +5 -3
  35. package/Components/Focusable/__tests__/index.android.test.tsx +3 -0
  36. package/Components/Focusable/index.android.tsx +19 -11
  37. package/Components/Focusable/index.tsx +1 -1
  38. package/Components/FocusableGroup/FocusableTvOS.tsx +6 -1
  39. package/Components/FocusableList/FocusableItem.tsx +4 -3
  40. package/Components/FocusableList/FocusableListItemWrapper.tsx +2 -1
  41. package/Components/FocusableList/hooks/useCellState.android.ts +13 -3
  42. package/Components/FocusableList/index.tsx +20 -9
  43. package/Components/FreezeWithCallback/__tests__/index.test.tsx +67 -43
  44. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +42 -59
  45. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +13 -10
  46. package/Components/HandlePlayable/HandlePlayable.tsx +25 -9
  47. package/Components/HookRenderer/HookRenderer.tsx +5 -1
  48. package/Components/Layout/TV/LayoutBackground.tsx +1 -1
  49. package/Components/Layout/TV/__tests__/index.test.tsx +0 -1
  50. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +6 -2
  51. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -1
  52. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -39
  53. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +1 -1
  54. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +1 -1
  55. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +65 -17
  56. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +21 -3
  57. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +6 -3
  58. package/Components/MasterCell/DefaultComponents/Text/index.tsx +26 -6
  59. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +10 -10
  60. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +18 -18
  61. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/index.test.tsx +10 -10
  62. package/Components/MasterCell/elementMapper.tsx +1 -2
  63. package/Components/MasterCell/index.tsx +1 -1
  64. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  65. package/Components/MasterCell/utils/index.ts +11 -5
  66. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  67. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  68. package/Components/OfflineHandler/__tests__/index.test.tsx +26 -35
  69. package/Components/PlayerContainer/ErrorDisplay/index.ts +1 -1
  70. package/Components/PlayerContainer/PlayerContainer.tsx +46 -33
  71. package/Components/PlayerContainer/ProgramInfo/index.tsx +1 -1
  72. package/Components/PlayerContainer/index.ts +1 -1
  73. package/Components/PlayerImageBackground/index.tsx +1 -1
  74. package/Components/River/ComponentsMap/ComponentsMap.tsx +49 -43
  75. package/Components/River/ComponentsMap/ContextProviders/ComponentsMapHeightContext.ts +8 -0
  76. package/Components/River/ComponentsMap/ContextProviders/ComponentsMapRefContext.ts +8 -0
  77. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +378 -0
  78. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
  79. package/Components/River/RefreshControl.tsx +11 -17
  80. package/Components/River/RiverItem.tsx +3 -0
  81. package/Components/River/TV/River.tsx +11 -20
  82. package/Components/River/TV/index.tsx +5 -3
  83. package/Components/River/TV/withFocusableGroupForContent.tsx +60 -0
  84. package/Components/River/TV/withPipesV1DataLoader.tsx +43 -0
  85. package/Components/River/TV/withRiverDataLoader.tsx +17 -0
  86. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  87. package/Components/River/__tests__/river.test.js +12 -26
  88. package/Components/River/index.tsx +1 -1
  89. package/Components/Screen/__tests__/Screen.test.tsx +28 -29
  90. package/Components/Screen/__tests__/navigationHandler.test.ts +133 -22
  91. package/Components/Screen/navigationHandler.ts +20 -2
  92. package/Components/ScreenResolver/index.tsx +15 -0
  93. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  94. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  95. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  96. package/Components/ScreenRevealManager/index.ts +1 -0
  97. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
  98. package/Components/Tabs/TV/Tabs.android.tsx +1 -3
  99. package/Components/Tabs/Tabs.tsx +2 -3
  100. package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +13 -0
  101. package/Components/TextInputTv/index.tsx +11 -0
  102. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  103. package/Components/Touchable/__tests__/touchable.test.tsx +12 -17
  104. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  105. package/Components/VideoLive/animationUtils.ts +3 -3
  106. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +6 -10
  107. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +294 -0
  108. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +93 -0
  109. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +73 -29
  110. package/Components/VideoModal/PlayerDetails.tsx +29 -7
  111. package/Components/VideoModal/PlayerWrapper.tsx +26 -142
  112. package/Components/VideoModal/VideoModal.tsx +3 -17
  113. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -5
  114. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -7
  115. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -240
  116. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +9 -1
  117. package/Components/VideoModal/hooks/index.ts +0 -2
  118. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +40 -15
  119. package/Components/VideoModal/hooks/useModalSize.ts +18 -2
  120. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +2 -2
  121. package/Components/VideoModal/hooks/utils/index.ts +4 -0
  122. package/Components/VideoModal/utils.ts +6 -0
  123. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  124. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  125. package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
  126. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
  127. package/Contexts/CellFocusedStateContext/index.tsx +27 -0
  128. package/Contexts/ConfigutaionContext/__tests__/ConfigurationProvider.test.tsx +3 -3
  129. package/Contexts/ScreenContext/index.tsx +46 -6
  130. package/Decorators/ConfigurationWrapper/__tests__/withConfigurationProvider.test.tsx +3 -3
  131. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  132. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +221 -209
  133. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +14 -16
  134. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -20
  135. package/Decorators/RiverFeedLoader/index.tsx +22 -4
  136. package/Decorators/RiverFeedLoader/utils/index.ts +0 -18
  137. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +3 -6
  138. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -0
  139. package/Decorators/ZappPipesDataConnector/__tests__/NullFeedResolver.test.tsx +78 -0
  140. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +205 -0
  141. package/Decorators/ZappPipesDataConnector/__tests__/StaticFeedResolver.test.tsx +251 -0
  142. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +368 -0
  143. package/Decorators/ZappPipesDataConnector/__tests__/utils.test.ts +39 -0
  144. package/Decorators/ZappPipesDataConnector/index.tsx +26 -293
  145. package/Decorators/ZappPipesDataConnector/resolvers/NullFeedResolver.tsx +25 -0
  146. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +87 -0
  147. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +266 -0
  148. package/Decorators/ZappPipesDataConnector/types.ts +29 -0
  149. package/Decorators/ZappPipesDataConnector/utils/mongoFilter.ts +738 -0
  150. package/Decorators/ZappPipesDataConnector/utils/useFilter.tsx +157 -0
  151. package/events/index.ts +5 -0
  152. package/package.json +5 -10
  153. package/Components/AudioPlayer/AudioPlayerLayout.tsx +0 -202
  154. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -66
  155. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +0 -28
  156. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
  157. package/Components/AudioPlayer/index.ts +0 -1
  158. package/Components/River/TV/withTVEventHandler.tsx +0 -27
  159. package/Components/River/__tests__/__snapshots__/river.test.js.snap +0 -27
  160. package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
  161. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  162. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +0 -0
  163. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  164. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  165. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  166. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -28,17 +28,10 @@ interface Props {
28
28
  onAsyncRender: () => void;
29
29
  }
30
30
 
31
- const SecondaryImageComponent = (props: Props) => {
32
- const {
33
- uri,
34
- style,
35
- displayMode,
36
- imageSizing,
37
- fitPosition,
38
- fixedHeight,
39
- fixedWidth,
40
- onAsyncRender,
41
- } = props;
31
+ /** Secondary Image Dynamic does not render until the image is loaded */
32
+ const SecondaryImageDynamic = (props: Props) => {
33
+ const { uri, style, displayMode, imageSizing, fitPosition, onAsyncRender } =
34
+ props;
42
35
 
43
36
  const imageDimension = useGetImageDimensions(
44
37
  uri,
@@ -46,13 +39,9 @@ const SecondaryImageComponent = (props: Props) => {
46
39
  isImageSizingFit(imageSizing) ? undefined : (style.height as number)
47
40
  );
48
41
 
49
- const containerHeight = isDisplayModeFixed(displayMode)
50
- ? fixedHeight
51
- : imageDimension?.height;
42
+ const containerHeight = imageDimension?.height;
52
43
 
53
- const containerWidth = isDisplayModeFixed(displayMode)
54
- ? fixedWidth
55
- : style?.width;
44
+ const containerWidth = style?.width;
56
45
 
57
46
  if (isNil(imageDimension?.aspectRatio)) {
58
47
  return null;
@@ -80,4 +69,63 @@ const SecondaryImageComponent = (props: Props) => {
80
69
  );
81
70
  };
82
71
 
72
+ /** Secondary Image Fixed does not render the image until the image is loaded, but keep container rendered */
73
+ const SecondaryImageFixed = (props: Props) => {
74
+ const {
75
+ uri,
76
+ style,
77
+ displayMode,
78
+ imageSizing,
79
+ fitPosition,
80
+ fixedHeight,
81
+ fixedWidth,
82
+ onAsyncRender,
83
+ } = props;
84
+
85
+ const imageDimension = useGetImageDimensions(
86
+ uri,
87
+ style.width as number,
88
+ isImageSizingFit(imageSizing) ? undefined : (style.height as number)
89
+ );
90
+
91
+ return (
92
+ <View style={style} onLayout={onAsyncRender}>
93
+ {isNil(imageDimension?.aspectRatio) ? null : (
94
+ <Image
95
+ {...props}
96
+ source={{ uri }}
97
+ style={{
98
+ ...getStyle({
99
+ imageSizing,
100
+ fitPosition,
101
+ displayMode,
102
+ imageDimension,
103
+ containerHeight: fixedHeight,
104
+ containerWidth: fixedWidth,
105
+ }),
106
+ borderRadius: style.borderRadius,
107
+ aspectRatio: imageDimension.aspectRatio,
108
+ }}
109
+ />
110
+ )}
111
+ </View>
112
+ );
113
+ };
114
+
115
+ const SecondaryImageComponent = (props: Props) => {
116
+ const { uri, displayMode } = props;
117
+
118
+ if (!uri) {
119
+ return null;
120
+ }
121
+
122
+ const isFixed = isDisplayModeFixed(displayMode);
123
+
124
+ return isFixed ? (
125
+ <SecondaryImageFixed {...props} />
126
+ ) : (
127
+ <SecondaryImageDynamic {...props} />
128
+ );
129
+ };
130
+
83
131
  export const SecondaryImage = withAsyncRenderHOC(SecondaryImageComponent);
@@ -4,9 +4,26 @@ import { render } from "@testing-library/react-native";
4
4
  import { SecondaryImage } from "../Image";
5
5
 
6
6
  describe("SecondaryImage - Image", () => {
7
- it("SecondaryImage should not render if no aspect ratio", async () => {
7
+ it("SecondaryImage should not render if no uri", async () => {
8
8
  const wrapper = await render(
9
- <SecondaryImage uri="" style={{ width: 100 }} />
9
+ <SecondaryImage
10
+ displayMode="dynamic"
11
+ uri={undefined}
12
+ style={{ width: 100 }}
13
+ />
14
+ );
15
+
16
+ expect(wrapper.toJSON()).toEqual(null);
17
+ expect(wrapper.toJSON()).toMatchSnapshot();
18
+ });
19
+
20
+ it("SecondaryImage should not render if no aspect ratio (dynamic)", async () => {
21
+ const wrapper = await render(
22
+ <SecondaryImage
23
+ displayMode="dynamic"
24
+ uri="someurl"
25
+ style={{ width: 100 }}
26
+ />
10
27
  );
11
28
 
12
29
  expect(wrapper.toJSON()).toEqual(null);
@@ -16,7 +33,8 @@ describe("SecondaryImage - Image", () => {
16
33
  it("SecondaryImage should render if known dimensions", async () => {
17
34
  const wrapper = await render(
18
35
  <SecondaryImage
19
- uri=""
36
+ uri="someUrl"
37
+ displayMode="dynamic"
20
38
  style={{ width: 100, height: 100, borderRadius: 10 }}
21
39
  />
22
40
  );
@@ -1,6 +1,8 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`SecondaryImage - Image SecondaryImage should not render if no aspect ratio 1`] = `null`;
3
+ exports[`SecondaryImage - Image SecondaryImage should not render if no aspect ratio (dynamic) 1`] = `null`;
4
+
5
+ exports[`SecondaryImage - Image SecondaryImage should not render if no uri 1`] = `null`;
4
6
 
5
7
  exports[`SecondaryImage - Image SecondaryImage should render if known dimensions 1`] = `
6
8
  <View
@@ -14,10 +16,11 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
14
16
  }
15
17
  >
16
18
  <Image
19
+ displayMode="dynamic"
17
20
  onAsyncRender={[Function]}
18
21
  source={
19
22
  {
20
- "uri": "",
23
+ "uri": "someUrl",
21
24
  }
22
25
  }
23
26
  style={
@@ -28,7 +31,7 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
28
31
  "width": 100,
29
32
  }
30
33
  }
31
- uri=""
34
+ uri="someUrl"
32
35
  />
33
36
  </View>
34
37
  `;
@@ -11,6 +11,8 @@ import { withScaledLineHeight } from "./utils";
11
11
  import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
12
12
  import { MeasurementPortalContext } from "../../../MeasurmentsPortal";
13
13
  import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
14
+ import { CellFocusedStateContext } from "@applicaster/zapp-react-native-ui-components/Contexts/CellFocusedStateContext";
15
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
14
16
 
15
17
  type Props = {
16
18
  style: any;
@@ -35,10 +37,9 @@ const _Text = ({
35
37
  }: Props) => {
36
38
  const _label = useTextLabel({ label, entry });
37
39
  const isMeasurement = React.useContext(MeasurementPortalContext);
40
+ const cellFocused = React.useContext(CellFocusedStateContext);
38
41
 
39
- if (isNilOrEmpty(_label)) {
40
- return null;
41
- }
42
+ const accessibilityManager = useAccessibilityManager({});
42
43
 
43
44
  // set maximum possible height for the text in case of measurement
44
45
  const height =
@@ -46,6 +47,27 @@ const _Text = ({
46
47
  ? toNumber(otherProps.numberOfLines) * toNumber(style.lineHeight)
47
48
  : undefined;
48
49
 
50
+ const textLabel = dateTransformEnabled
51
+ ? dateFormat(dateTransform, label)
52
+ : textTransform(transformText, _label);
53
+
54
+ React.useLayoutEffect(() => {
55
+ // For FocusableCells with action buttons
56
+ if (otherProps.state) {
57
+ if (otherProps.state === "focused" && cellFocused === true) {
58
+ accessibilityManager.addHeading(textLabel);
59
+ }
60
+ } else {
61
+ if (cellFocused === true) {
62
+ accessibilityManager.addHeading(textLabel);
63
+ }
64
+ }
65
+ }, [cellFocused, otherProps.state, textLabel]);
66
+
67
+ if (isNilOrEmpty(_label)) {
68
+ return null;
69
+ }
70
+
49
71
  return (
50
72
  <Text
51
73
  style={[
@@ -55,9 +77,7 @@ const _Text = ({
55
77
  allowFontScaling={false}
56
78
  {...withoutLabel(otherProps)}
57
79
  >
58
- {dateTransformEnabled
59
- ? dateFormat(dateTransform, label)
60
- : textTransform(transformText, _label)}
80
+ {textLabel}
61
81
  </Text>
62
82
  );
63
83
  };
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import TestRenderer from "react-test-renderer";
2
+ import { render } from "@testing-library/react-native";
3
3
  import { Image } from "react-native";
4
4
 
5
5
  jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
@@ -10,35 +10,35 @@ const CustomImage = require("../Image").default;
10
10
 
11
11
  describe("image with no source", () => {
12
12
  it("Uses provided placeholder image string", () => {
13
- const testRenderer = TestRenderer.create(
13
+ const { UNSAFE_getByType } = render(
14
14
  <CustomImage placeholderImage={"foo"} />
15
15
  );
16
16
 
17
- const testInstance = testRenderer.root;
17
+ const imageComponent = UNSAFE_getByType(Image);
18
18
 
19
- expect(testInstance.findByType(Image).props.source).toEqual({
19
+ expect(imageComponent.props.source).toEqual({
20
20
  uri: "foo",
21
21
  });
22
22
  });
23
23
 
24
24
  it("Uses provided placeholder image object", () => {
25
- const testRenderer = TestRenderer.create(
25
+ const { UNSAFE_getByType } = render(
26
26
  <CustomImage placeholderImage={"foo"} />
27
27
  );
28
28
 
29
- const testInstance = testRenderer.root;
29
+ const imageComponent = UNSAFE_getByType(Image);
30
30
 
31
- expect(testInstance.findByType(Image).props.source).toEqual({
31
+ expect(imageComponent.props.source).toEqual({
32
32
  uri: "foo",
33
33
  });
34
34
  });
35
35
 
36
36
  it("Returns empty string if no image or placeholder defined", () => {
37
- const testRenderer = TestRenderer.create(
37
+ const { UNSAFE_getByType } = render(
38
38
  <CustomImage placeholderImage={null} />
39
39
  );
40
40
 
41
- const testInstance = testRenderer.root;
42
- expect(testInstance.findByType(Image).props.source).toBeUndefined();
41
+ const imageComponent = UNSAFE_getByType(Image);
42
+ expect(imageComponent.props.source).toBeUndefined();
43
43
  });
44
44
  });
@@ -1,8 +1,6 @@
1
1
  import React from "react";
2
2
  import RN from "react-native";
3
- import { Provider } from "react-redux";
4
- import TestRenderer from "react-test-renderer";
5
- import configureStoreFn from "redux-mock-store";
3
+ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
6
4
 
7
5
  const mockUseIsRTL = jest.fn(() => true);
8
6
  const mockGetIsRTL = jest.fn(() => true);
@@ -17,7 +15,6 @@ jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
17
15
  }));
18
16
 
19
17
  const CustomText = require("../Text").default;
20
- const mockStore = configureStoreFn();
21
18
 
22
19
  const defaultProps = {
23
20
  entry: {},
@@ -25,16 +22,15 @@ const defaultProps = {
25
22
  transformText: "default",
26
23
  };
27
24
 
28
- const getRenderedText = (label: string, store: any) => {
25
+ const getRenderedText = (label: string, storeConfig: any) => {
29
26
  const props = { ...{ ...defaultProps, label } };
30
27
 
31
- const testRenderer = TestRenderer.create(
32
- <Provider store={store}>
33
- <CustomText {...props} />
34
- </Provider>
28
+ const { UNSAFE_getByType } = renderWithProviders(
29
+ <CustomText {...props} />,
30
+ storeConfig
35
31
  );
36
32
 
37
- return testRenderer.root.findByType(RN.Text).props.children;
33
+ return UNSAFE_getByType(RN.Text).props.children;
38
34
  };
39
35
 
40
36
  describe("RTL app: Hebrew text contains english word", () => {
@@ -42,28 +38,32 @@ describe("RTL app: Hebrew text contains english word", () => {
42
38
  const textWithNotFirstEnglishWord = "השיר של נועה נועה קירל באירוויזיון Word";
43
39
  const textWithoutEnglishWord = "השיר של נועה נועה קירל באירוויזיון";
44
40
 
45
- const store = mockStore({
41
+ const storeConfig = {
46
42
  remoteConfigurations: { localizations: { he: {} } },
47
43
  appData: {
48
44
  languageCode: "he",
49
45
  countryCode: "IL",
50
46
  },
51
- });
47
+ };
52
48
 
53
49
  it("Hebrew text contains first english word", () => {
54
- const renderedText = getRenderedText(textWithFirstEnglishWord, store);
50
+ const renderedText = getRenderedText(textWithFirstEnglishWord, storeConfig);
55
51
  const desiredText = "\u200fWord\u202c השיר של נועה נועה קירל באירוויזיון";
56
52
 
57
53
  expect(renderedText).toEqual(desiredText);
58
54
  });
59
55
 
60
56
  it("Hebrew text contains not first english word", () => {
61
- const renderedText = getRenderedText(textWithNotFirstEnglishWord, store);
57
+ const renderedText = getRenderedText(
58
+ textWithNotFirstEnglishWord,
59
+ storeConfig
60
+ );
61
+
62
62
  expect(renderedText).toEqual(textWithNotFirstEnglishWord);
63
63
  });
64
64
 
65
65
  it("Hebrew text doesn't contain english word", () => {
66
- const renderedText = getRenderedText(textWithoutEnglishWord, store);
66
+ const renderedText = getRenderedText(textWithoutEnglishWord, storeConfig);
67
67
  expect(renderedText).toEqual(textWithoutEnglishWord);
68
68
  });
69
69
  });
@@ -77,16 +77,16 @@ describe("LTR app: English text", () => {
77
77
 
78
78
  const englishText = "Test sentence";
79
79
 
80
- const store = mockStore({
80
+ const storeConfig = {
81
81
  remoteConfigurations: { localizations: { en: {} } },
82
82
  appData: {
83
83
  languageCode: "en",
84
84
  countryCode: "US",
85
85
  },
86
- });
86
+ };
87
87
 
88
88
  it("English text", () => {
89
- const renderedText = getRenderedText(englishText, store);
89
+ const renderedText = getRenderedText(englishText, storeConfig);
90
90
  expect(renderedText).toEqual(englishText);
91
91
  });
92
92
  });
@@ -1,12 +1,12 @@
1
1
  import * as React from "react";
2
2
  import { View } from "react-native";
3
- import ReactTestRenderer from "react-test-renderer";
3
+ import { render } from "@testing-library/react-native";
4
4
 
5
5
  import { CollapsibleTextContainer } from "../CollapsibleTextContainer";
6
6
 
7
7
  describe("CollapsibleTextContainer", () => {
8
8
  it("render container+children when label is presented", () => {
9
- const renderer = ReactTestRenderer.create(
9
+ const { toJSON } = render(
10
10
  <CollapsibleTextContainer
11
11
  backgroundColor="#000000"
12
12
  label={"label"}
@@ -16,36 +16,36 @@ describe("CollapsibleTextContainer", () => {
16
16
  </CollapsibleTextContainer>
17
17
  );
18
18
 
19
- const result = renderer.toJSON();
19
+ const result = toJSON();
20
20
 
21
21
  expect(result).not.toBeNull();
22
- expect(renderer.toJSON()).toMatchSnapshot();
22
+ expect(toJSON()).toMatchSnapshot();
23
23
  });
24
24
 
25
25
  it("render nothing when label is empty", () => {
26
- const renderer = ReactTestRenderer.create(
26
+ const { toJSON } = render(
27
27
  <CollapsibleTextContainer backgroundColor="#000000" label={""} style={{}}>
28
28
  <View />
29
29
  </CollapsibleTextContainer>
30
30
  );
31
31
 
32
- const result = renderer.toJSON();
32
+ const result = toJSON();
33
33
  expect(result).toBeNull();
34
34
  });
35
35
 
36
36
  it("render nothing when label is not passed", () => {
37
- const renderer = ReactTestRenderer.create(
37
+ const { toJSON } = render(
38
38
  <CollapsibleTextContainer backgroundColor="#000000" label={""} style={{}}>
39
39
  <View />
40
40
  </CollapsibleTextContainer>
41
41
  );
42
42
 
43
- const result = renderer.toJSON();
43
+ const result = toJSON();
44
44
  expect(result).toBeNull();
45
45
  });
46
46
 
47
47
  it("render nothing when label is undefined", () => {
48
- const renderer = ReactTestRenderer.create(
48
+ const { toJSON } = render(
49
49
  <CollapsibleTextContainer
50
50
  backgroundColor="#000000"
51
51
  label={undefined}
@@ -55,7 +55,7 @@ describe("CollapsibleTextContainer", () => {
55
55
  </CollapsibleTextContainer>
56
56
  );
57
57
 
58
- const result = renderer.toJSON();
58
+ const result = toJSON();
59
59
  expect(result).toBeNull();
60
60
  });
61
61
  });
@@ -73,7 +73,6 @@ export function elementMapper(
73
73
  : {};
74
74
 
75
75
  const componentProps = {
76
- key,
77
76
  style,
78
77
  skipButtons: otherProps?.skipButtons,
79
78
  emitAsyncElementRegistrate: otherProps?.emitAsyncElementRegistrate,
@@ -91,7 +90,7 @@ export function elementMapper(
91
90
  const fn = mapElementWithKey(elementMapper(components, otherProps));
92
91
 
93
92
  return (
94
- <Component {...componentProps}>
93
+ <Component key={key} {...componentProps}>
95
94
  {focusableTypes.has(type) && elements.length > 0
96
95
  ? elements.map(fn)
97
96
  : null}
@@ -86,7 +86,7 @@ export function masterCellBuilder({
86
86
  entry: item,
87
87
  state: getEntryState(state, entryIsSelected),
88
88
  }),
89
- [state, item?.id, entryIsSelected] // Assuming that item won't mutate
89
+ [state, item, entryIsSelected] // Assuming that item won't mutate
90
90
  );
91
91
 
92
92
  const wrapperRef = React.useRef(null);
@@ -1,28 +1,58 @@
1
1
  import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
2
- import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageSingleSelectProvider";
2
+ import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/StorageSingleSelectProvider";
3
3
  import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
4
- import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
4
+ import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/StorageMultiSelectProvider";
5
5
  import React, { useEffect } from "react";
6
6
  import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
7
7
  import { BehaviorSubject } from "rxjs";
8
8
  import { masterCellLogger } from "../logger";
9
9
  import get from "lodash/get";
10
-
11
- const parseContextKey = (key: string): string | null => {
12
- if (!key?.startsWith("@{ctx/")) return null;
13
-
14
- return key.substring("@{ctx/".length, key.length - 1);
10
+ import { ScreenMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenStateMultiSelectProvider";
11
+ import { ScreenSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenSingleValueProvider";
12
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
13
+ import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
14
+
15
+ const parseContextKey = (
16
+ key: string,
17
+ context: string = "ctx"
18
+ ): string | null => {
19
+ if (!key?.startsWith(`@{${context}/`)) return null;
20
+
21
+ return key.substring(`@{${context}/`.length, key.length - 1);
15
22
  };
16
23
 
17
24
  const getDataSourceProvider = (
18
- behavior: Behavior
25
+ behavior: Behavior,
26
+ screenRoute: string,
27
+ screenStateStore: ReturnType<typeof useScreenStateStore>
19
28
  ): BehaviorSubject<string[] | string> | null => {
20
29
  if (!behavior) return null;
21
30
 
22
31
  const selection = String(behavior.current_selection);
32
+ const screenKey = parseContextKey(selection, "screen");
33
+
34
+ if (screenKey) {
35
+ if (behavior.select_mode === "multi") {
36
+ return ScreenMultiSelectProvider.getProvider(
37
+ screenKey,
38
+ screenRoute,
39
+ screenStateStore
40
+ ).getObservable();
41
+ }
42
+
43
+ if (behavior.select_mode === "single") {
44
+ return ScreenSingleValueProvider.getProvider(
45
+ screenKey,
46
+ screenRoute,
47
+ screenStateStore
48
+ ).getObservable();
49
+ }
50
+ }
51
+
23
52
  const contextKey = parseContextKey(selection);
24
53
 
25
54
  if (contextKey) {
55
+ // TODO: Add storage scope to behavior
26
56
  if (behavior.select_mode === "multi") {
27
57
  return StorageMultiSelectProvider.getProvider(contextKey).getObservable();
28
58
  }
@@ -41,6 +71,8 @@ const getDataSourceProvider = (
41
71
 
42
72
  export const useBehaviorUpdate = (behavior: Behavior) => {
43
73
  const [lastUpdate, setLastUpdate] = React.useState<number | null>(null);
74
+ const screenRoute = useRoute()?.pathname || "";
75
+ const screenStateStore = useScreenStateStore();
44
76
  const player = usePlayer();
45
77
 
46
78
  const triggerUpdate = () => setLastUpdate(Date.now());
@@ -48,7 +80,11 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
48
80
  useEffect(() => {
49
81
  if (!behavior) return;
50
82
 
51
- const dataSource = getDataSourceProvider(behavior);
83
+ const dataSource = getDataSourceProvider(
84
+ behavior,
85
+ screenRoute,
86
+ screenStateStore
87
+ );
52
88
 
53
89
  if (dataSource) {
54
90
  const subscription = dataSource.subscribe(triggerUpdate);
@@ -72,10 +108,17 @@ export const useBehaviorUpdate = (behavior: Behavior) => {
72
108
 
73
109
  // We cant use async in this function (its inside render),
74
110
  // so we rely on useBehaviorUpdate to update current value and trigger re-render
75
- export const isCellSelected = (
76
- item: ZappEntry,
77
- behavior?: Behavior
78
- ): boolean => {
111
+ export const isCellSelected = ({
112
+ item,
113
+ screenRoute,
114
+ screenStateStore,
115
+ behavior,
116
+ }: {
117
+ item: ZappEntry;
118
+ screenRoute: string;
119
+ screenStateStore: ReturnType<typeof useScreenStateStore>;
120
+ behavior?: Behavior;
121
+ }): boolean => {
79
122
  if (!behavior) return false;
80
123
 
81
124
  const id = behavior.selector ? get(item, behavior.selector) : item.id;
@@ -99,7 +142,32 @@ export const isCellSelected = (
99
142
  }
100
143
 
101
144
  const selection = String(behavior.current_selection);
102
- const contextKey = parseContextKey(selection);
145
+
146
+ const screenKey = parseContextKey(selection, "screen");
147
+
148
+ if (screenKey) {
149
+ if (behavior.select_mode === "single") {
150
+ const selectedItem = ScreenSingleValueProvider.getProvider(
151
+ screenKey,
152
+ screenRoute,
153
+ screenStateStore
154
+ ).getValue();
155
+
156
+ return selectedItem === String(id);
157
+ }
158
+
159
+ if (behavior.select_mode === "multi") {
160
+ const selectedItems = ScreenMultiSelectProvider.getProvider(
161
+ screenKey,
162
+ screenRoute,
163
+ screenStateStore
164
+ ).getSelectedItems();
165
+
166
+ return selectedItems?.includes(String(id));
167
+ }
168
+ }
169
+
170
+ const contextKey = parseContextKey(selection, "ctx");
103
171
 
104
172
  if (contextKey) {
105
173
  if (behavior.select_mode === "single") {
@@ -8,6 +8,8 @@ import { masterCellLogger } from "../logger";
8
8
  import { getCellState } from "../../Cell/utils";
9
9
  import { getColorFromData } from "@applicaster/zapp-react-native-utils/cellUtils";
10
10
  import { isCellSelected, useBehaviorUpdate } from "./behaviorProvider";
11
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
12
+ import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
11
13
 
12
14
  const hasElementSpecificViewType = (viewType) => (element) => {
13
15
  if (R.isNil(element)) {
@@ -190,10 +192,6 @@ export const getFocusedButtonId = (focusable) => {
190
192
  });
191
193
  };
192
194
 
193
- export const isSelected = (item: ZappEntry, behavior?: Behavior) => {
194
- return isCellSelected(item, behavior);
195
- };
196
-
197
195
  export const useCellState = ({
198
196
  item,
199
197
  behavior,
@@ -204,9 +202,17 @@ export const useCellState = ({
204
202
  focused: boolean;
205
203
  }): CellState => {
206
204
  const lastUpdate = useBehaviorUpdate(behavior);
205
+ const router = useRoute();
206
+ const screenStateStore = useScreenStateStore();
207
207
 
208
208
  const _isSelected = useMemo(
209
- () => isSelected(item, behavior),
209
+ () =>
210
+ isCellSelected({
211
+ item,
212
+ screenRoute: router?.pathname,
213
+ screenStateStore,
214
+ behavior,
215
+ }),
210
216
  [behavior, item, lastUpdate]
211
217
  );
212
218