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

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 (183) hide show
  1. package/Components/AnimatedInOut/index.tsx +68 -23
  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/FocusableWrapper.tsx +44 -0
  26. package/Components/Cell/TvOSCellComponent.tsx +92 -17
  27. package/Components/Cell/__tests__/CellWIthFocusable.test.js +3 -2
  28. package/Components/Cell/index.js +7 -3
  29. package/Components/ComponentResolver/index.ts +1 -1
  30. package/Components/FeedLoader/FeedLoader.tsx +7 -16
  31. package/Components/FeedLoader/FeedLoaderHOC.tsx +21 -0
  32. package/Components/FeedLoader/index.js +2 -8
  33. package/Components/Focusable/Focusable.tsx +12 -3
  34. package/Components/Focusable/FocusableTvOS.tsx +5 -5
  35. package/Components/Focusable/FocusableiOS.tsx +2 -2
  36. package/Components/Focusable/Touchable.tsx +5 -3
  37. package/Components/Focusable/__tests__/index.android.test.tsx +3 -0
  38. package/Components/Focusable/index.android.tsx +19 -11
  39. package/Components/Focusable/index.tsx +1 -1
  40. package/Components/FocusableGroup/FocusableTvOS.tsx +1 -1
  41. package/Components/FocusableList/FocusableItem.tsx +4 -3
  42. package/Components/FocusableList/FocusableListItemWrapper.tsx +2 -1
  43. package/Components/FocusableList/hooks/useCellState.android.ts +13 -3
  44. package/Components/FocusableList/index.tsx +20 -9
  45. package/Components/FreezeWithCallback/__tests__/index.test.tsx +67 -43
  46. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +42 -59
  47. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +22 -21
  48. package/Components/HandlePlayable/HandlePlayable.tsx +39 -74
  49. package/Components/HandlePlayable/const.ts +3 -0
  50. package/Components/HandlePlayable/utils.ts +74 -0
  51. package/Components/HookRenderer/HookRenderer.tsx +5 -1
  52. package/Components/Layout/TV/LayoutBackground.tsx +1 -1
  53. package/Components/Layout/TV/__tests__/index.test.tsx +0 -1
  54. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +6 -2
  55. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -1
  56. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -39
  57. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +1 -1
  58. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +1 -1
  59. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +65 -17
  60. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +21 -3
  61. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +6 -3
  62. package/Components/MasterCell/DefaultComponents/Text/index.tsx +26 -6
  63. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +10 -10
  64. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +18 -18
  65. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/index.test.tsx +10 -10
  66. package/Components/MasterCell/elementMapper.tsx +1 -2
  67. package/Components/MasterCell/index.tsx +1 -1
  68. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  69. package/Components/MasterCell/utils/index.ts +11 -5
  70. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  71. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  72. package/Components/OfflineHandler/__tests__/index.test.tsx +26 -35
  73. package/Components/PlayerContainer/ErrorDisplay/index.ts +1 -1
  74. package/Components/PlayerContainer/PlayerContainer.tsx +45 -47
  75. package/Components/PlayerContainer/ProgramInfo/index.tsx +1 -1
  76. package/Components/PlayerContainer/index.ts +1 -1
  77. package/Components/PlayerImageBackground/index.tsx +4 -23
  78. package/Components/River/ComponentsMap/ComponentsMap.tsx +49 -43
  79. package/Components/River/ComponentsMap/ContextProviders/ComponentsMapHeightContext.ts +8 -0
  80. package/Components/River/ComponentsMap/ContextProviders/ComponentsMapRefContext.ts +8 -0
  81. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +378 -0
  82. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
  83. package/Components/River/RefreshControl.tsx +11 -17
  84. package/Components/River/RiverItem.tsx +3 -0
  85. package/Components/River/TV/River.tsx +2 -17
  86. package/Components/River/TV/index.tsx +3 -1
  87. package/Components/River/TV/withPipesV1DataLoader.tsx +43 -0
  88. package/Components/River/TV/withRiverDataLoader.tsx +17 -0
  89. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  90. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  91. package/Components/River/__tests__/river.test.js +12 -26
  92. package/Components/River/index.tsx +1 -1
  93. package/Components/Screen/TV/hooks/useInitialFocus.ts +14 -4
  94. package/Components/Screen/__tests__/Screen.test.tsx +28 -29
  95. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  96. package/Components/Screen/__tests__/navigationHandler.test.ts +133 -22
  97. package/Components/Screen/index.tsx +22 -5
  98. package/Components/Screen/navigationHandler.ts +20 -2
  99. package/Components/ScreenResolver/index.tsx +22 -1
  100. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  101. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  102. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  103. package/Components/ScreenRevealManager/index.ts +1 -0
  104. package/Components/ScreenRevealManager/utils/index.ts +23 -0
  105. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +109 -0
  106. package/Components/Tabs/TV/Tabs.android.tsx +1 -3
  107. package/Components/Tabs/Tabs.tsx +2 -3
  108. package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +13 -0
  109. package/Components/TextInputTv/index.tsx +11 -0
  110. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  111. package/Components/Touchable/__tests__/touchable.test.tsx +12 -17
  112. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  113. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  114. package/Components/VideoLive/animationUtils.ts +3 -3
  115. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +120 -133
  116. package/Components/VideoModal/ModalAnimation/index.ts +2 -13
  117. package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
  118. package/Components/VideoModal/PlayerDetails.tsx +29 -7
  119. package/Components/VideoModal/PlayerWrapper.tsx +25 -215
  120. package/Components/VideoModal/VideoModal.tsx +4 -22
  121. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -5
  122. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +2 -7
  123. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -240
  124. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +9 -1
  125. package/Components/VideoModal/hooks/index.ts +0 -2
  126. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +40 -15
  127. package/Components/VideoModal/hooks/useModalSize.ts +23 -2
  128. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +2 -2
  129. package/Components/VideoModal/hooks/utils/index.ts +4 -0
  130. package/Components/VideoModal/playerWrapperStyle.ts +70 -0
  131. package/Components/VideoModal/playerWrapperUtils.ts +91 -0
  132. package/Components/VideoModal/utils.ts +13 -0
  133. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  134. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  135. package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
  136. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -6
  137. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  138. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
  139. package/Contexts/CellFocusedStateContext/index.tsx +27 -0
  140. package/Contexts/ConfigutaionContext/__tests__/ConfigurationProvider.test.tsx +3 -3
  141. package/Contexts/ScreenContext/index.tsx +46 -6
  142. package/Decorators/ConfigurationWrapper/__tests__/withConfigurationProvider.test.tsx +3 -3
  143. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  144. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +221 -209
  145. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +14 -16
  146. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -20
  147. package/Decorators/RiverFeedLoader/index.tsx +22 -4
  148. package/Decorators/RiverFeedLoader/utils/getDatasourceUrl.ts +6 -10
  149. package/Decorators/RiverFeedLoader/utils/index.ts +0 -18
  150. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +3 -6
  151. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -0
  152. package/Decorators/ZappPipesDataConnector/__tests__/NullFeedResolver.test.tsx +78 -0
  153. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +205 -0
  154. package/Decorators/ZappPipesDataConnector/__tests__/StaticFeedResolver.test.tsx +251 -0
  155. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +368 -0
  156. package/Decorators/ZappPipesDataConnector/__tests__/utils.test.ts +39 -0
  157. package/Decorators/ZappPipesDataConnector/index.tsx +26 -293
  158. package/Decorators/ZappPipesDataConnector/resolvers/NullFeedResolver.tsx +25 -0
  159. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +87 -0
  160. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +266 -0
  161. package/Decorators/ZappPipesDataConnector/types.ts +29 -0
  162. package/Decorators/ZappPipesDataConnector/utils/mongoFilter.ts +738 -0
  163. package/Decorators/ZappPipesDataConnector/utils/useFilter.tsx +157 -0
  164. package/events/index.ts +3 -0
  165. package/package.json +5 -10
  166. package/Components/AudioPlayer/AudioPlayerLayout.tsx +0 -202
  167. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -66
  168. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +0 -28
  169. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
  170. package/Components/AudioPlayer/index.ts +0 -1
  171. package/Components/River/__tests__/__snapshots__/river.test.js.snap +0 -27
  172. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
  173. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -421
  174. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
  175. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
  176. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
  177. package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
  178. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  179. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +0 -0
  180. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  181. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  182. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  183. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -2,7 +2,7 @@ import React from "react";
2
2
 
3
3
  type Props = {
4
4
  children: () => React.ReactNode;
5
- };
5
+ } & Record<string, any>;
6
6
 
7
7
  function FocusableiOSComponent({ children }: Props) {
8
8
  if (typeof children === "function") {
@@ -12,4 +12,4 @@ function FocusableiOSComponent({ children }: Props) {
12
12
  return children;
13
13
  }
14
14
 
15
- export const FocusableiOS = React.forwardRef(FocusableiOSComponent);
15
+ export const FocusableiOS = FocusableiOSComponent;
@@ -9,7 +9,8 @@ type Props = {
9
9
  onLongPress?: (ref: FocusManager.TouchableRef) => void;
10
10
  onFocus?: (
11
11
  ref: FocusManager.TouchableRef,
12
- options: FocusManager.Android.CallbackOptions
12
+ options: FocusManager.Android.CallbackOptions,
13
+ context: Option<FocusManager.FocusContext>
13
14
  ) => void;
14
15
  onBlur?: (
15
16
  ref: FocusManager.TouchableRef,
@@ -39,9 +40,10 @@ export class Touchable extends React.Component<Props> {
39
40
 
40
41
  onFocus(
41
42
  focusableRef: FocusManager.TouchableRef,
42
- options: FocusManager.Android.CallbackOptions
43
+ options: FocusManager.Android.CallbackOptions,
44
+ context: Option<FocusManager.FocusContext>
43
45
  ): void {
44
- this.props?.onFocus?.(focusableRef, options);
46
+ this.props?.onFocus?.(focusableRef, options, context);
45
47
  }
46
48
 
47
49
  onBlur(
@@ -38,6 +38,9 @@ describe("Focusable", () => {
38
38
  });
39
39
 
40
40
  it("updates disableFocus state when disableFocus prop changes", () => {
41
+ const unregister = jest.fn();
42
+ mockFocusManager.registerFocusable.mockReturnValue(unregister);
43
+
41
44
  const { rerender } = render(
42
45
  <Focusable id="test-id" disableFocus={false}>
43
46
  <Touchable testID="touchable" />
@@ -22,7 +22,8 @@ type Props = {
22
22
  onPress?: (ref: FocusManager.FocusableRef) => void;
23
23
  onFocus?: (
24
24
  ref: FocusManager.FocusableRef,
25
- options: FocusManager.Android.CallbackOptions
25
+ options: FocusManager.Android.CallbackOptions,
26
+ context?: FocusManager.FocusContext
26
27
  ) => void;
27
28
  onBlur?: (
28
29
  ref: FocusManager.FocusableRef,
@@ -32,6 +33,7 @@ type Props = {
32
33
  onPressOut?: (ref: FocusManager.FocusableRef) => void;
33
34
  onLongPress?: (ref: FocusManager.FocusableRef) => void;
34
35
  onRegister?: () => void;
36
+ onUnregister?: () => void;
35
37
  isFocusableCell?: boolean;
36
38
  /** only for FocusableScrollView */
37
39
  onSetIsFocusable?: (isFocusable: boolean) => void;
@@ -43,11 +45,13 @@ export const FocusableContext = React.createContext<
43
45
  // eslint-disable-next-line
44
46
  setIsFocusable: (enableFocus: boolean) => void;
45
47
  ref: FocusManager.FocusableRef;
48
+ parentFocusableId: Option<string>;
46
49
  } & ParentFocus
47
50
  >({
48
51
  focused: false,
49
52
  setIsFocusable: () => {},
50
53
  ref: { current: null },
54
+ parentFocusableId: undefined,
51
55
  });
52
56
 
53
57
  export const useFocusable = () => React.useContext(FocusableContext);
@@ -68,13 +72,14 @@ function FocusableComponent(props: Props, forwardedRef) {
68
72
  onPressOut,
69
73
  onLongPress,
70
74
  onRegister = noop,
75
+ onUnregister = noop,
71
76
  isFocusableCell = true,
72
77
  onSetIsFocusable,
73
78
  } = props;
74
79
 
75
80
  const isRTL = useIsRTL();
76
81
  const focusManager = useFocusManager();
77
- const { ref: parentFocusable } = useFocusable();
82
+ const { ref: parentFocusableRef, parentFocusableId } = useFocusable();
78
83
  const touchableRef = React.useRef(null);
79
84
 
80
85
  const [focused, setFocused] = React.useState(() =>
@@ -98,21 +103,23 @@ function FocusableComponent(props: Props, forwardedRef) {
98
103
  }
99
104
  }, [disableFocus]);
100
105
 
101
- React.useEffect(() => {
106
+ React.useLayoutEffect(() => {
102
107
  if (id) {
103
- const unregister = focusManager.registerFocusable(
108
+ const unregister = focusManager.registerFocusable({
104
109
  touchableRef,
105
- parentFocusable,
106
- isFocusableCell
107
- );
110
+ parentFocusableRef,
111
+ isFocusableCell,
112
+ parentFocusableId,
113
+ });
108
114
 
109
115
  onRegister();
110
116
 
111
117
  return () => {
112
118
  unregister();
119
+ onUnregister();
113
120
  };
114
121
  }
115
- }, [id, onRegister, isFocusableCell]);
122
+ }, [id, onRegister, onUnregister, isFocusableCell, parentFocusableId]);
116
123
 
117
124
  if (R.isNil(id)) {
118
125
  // eslint-disable-next-line no-console
@@ -122,9 +129,9 @@ function FocusableComponent(props: Props, forwardedRef) {
122
129
  }
123
130
 
124
131
  const _onFocus = React.useCallback(
125
- (ref, options) => {
132
+ (ref, options, context) => {
126
133
  setFocused(true);
127
- onFocus?.(ref, options);
134
+ onFocus?.(ref, options, context);
128
135
  },
129
136
  [onFocus]
130
137
  );
@@ -164,8 +171,9 @@ function FocusableComponent(props: Props, forwardedRef) {
164
171
  ...parentFocus,
165
172
  ref: touchableRef,
166
173
  setIsFocusable,
174
+ parentFocusableId: id,
167
175
  };
168
- }, [parentFocus, focused]);
176
+ }, [parentFocus, focused, id]);
169
177
 
170
178
  return (
171
179
  <Touchable
@@ -4,7 +4,7 @@ import { FocusableiOS } from "./FocusableiOS";
4
4
 
5
5
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
6
6
 
7
- export const Focusable = platformSelect({
7
+ export const Focusable: React.ComponentType<any> = platformSelect({
8
8
  tvos: FocusableTvOS,
9
9
  ios: FocusableiOS,
10
10
  default: FocusableDefault,
@@ -22,7 +22,7 @@ type FocusableGroupNativeEvent = {
22
22
 
23
23
  type Props = {
24
24
  id: string;
25
- children: (arg1: boolean) => React.ComponentType<any>;
25
+ children: React.ReactNode;
26
26
  isFocusDisabled: boolean;
27
27
  isWithMemory: boolean;
28
28
  focusGroupRef: React.Component;
@@ -10,7 +10,8 @@ type FocusableItemComponentProps = {
10
10
  onFocus: (
11
11
  element: FocusManager.FocusableRef,
12
12
  renderArgs: { item: FocusableItemComponentProps["item"]; index: number },
13
- direction: FocusManager.Android.FocusNavigationDirections
13
+ options: FocusManager.Android.CallbackOptions,
14
+ context: Option<FocusManager.FocusContext>
14
15
  ) => void;
15
16
  onListElementFocus?: (any, RenderItemProps, Direction) => void;
16
17
  onListElementBlur?: (any, RenderItemProps, Direction) => void;
@@ -45,8 +46,8 @@ const FocusableItemComponent = ({
45
46
  const renderArgs = { item, index };
46
47
 
47
48
  const onFocusHandler = React.useCallback(
48
- (element, direction) => {
49
- onFocus?.(element, renderArgs, direction);
49
+ (element, options, context) => {
50
+ onFocus?.(element, renderArgs, options, context);
50
51
  },
51
52
  [item, onFocus]
52
53
  );
@@ -17,7 +17,8 @@ type Props = {
17
17
  onFocus: (
18
18
  element: FocusManager.FocusableRef,
19
19
  renderArgs: { item: Item; index: number },
20
- direction: FocusManager.Android.FocusNavigationDirections
20
+ options: FocusManager.Android.CallbackOptions,
21
+ context: FocusManager.FocusContext
21
22
  ) => void;
22
23
  onListElementBlur?: (any, RenderItemProps, Direction) => void;
23
24
  onListElementPress?: (any, RenderItemProps) => void;
@@ -10,15 +10,25 @@ export const useCellState = (id: string) => {
10
10
  );
11
11
 
12
12
  React.useEffect(() => {
13
- const handler = (focusable) => {
13
+ const focusHandler = (focusable) => {
14
14
  const isChildren = focusManager.isFocusableChildOf(focusable, id);
15
+
15
16
  setCurrentCellFocused(isChildren);
16
17
  };
17
18
 
18
- focusManager.on(FOCUS_EVENTS.FOCUS, handler);
19
+ focusManager.on(FOCUS_EVENTS.FOCUS, focusHandler);
20
+
21
+ const resetHandler = ({ focusedId }) => {
22
+ if (id === focusedId) {
23
+ setCurrentCellFocused(false);
24
+ }
25
+ };
26
+
27
+ focusManager.on(FOCUS_EVENTS.RESET, resetHandler);
19
28
 
20
29
  return () => {
21
- focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
30
+ focusManager.removeHandler(FOCUS_EVENTS.FOCUS, focusHandler);
31
+ focusManager.removeHandler(FOCUS_EVENTS.RESET, resetHandler);
22
32
  };
23
33
  }, [id]);
24
34
 
@@ -20,27 +20,34 @@ import { FocusableScrollView } from "../FocusableScrollView";
20
20
  const mapIndexed = R.addIndex(R.map);
21
21
 
22
22
  export type IListRenderItem<ItemT> = (
23
- info: IListRenderItemInfo<ItemT> & {
23
+ info: {
24
24
  focused: boolean;
25
25
  onLoadFinished: () => void;
26
26
  onLoadFailed: () => void;
27
- }
27
+ } & IListRenderItemInfo<ItemT>
28
28
  ) => React.ReactElement | null;
29
29
 
30
30
  export const getFocusableId = (parentId, index) =>
31
31
  `${parentId}___index:${index}`;
32
32
 
33
+ type Item = ZappEntry | ZappUIComponent | any;
34
+
33
35
  export type Props<ItemT> = FlatListProps<ItemT> & {
34
36
  id?: number | string;
35
37
  horizontal?: boolean;
36
38
  loop?: boolean;
37
39
  numColumns?: number;
38
40
  data: ZappEntry[] | ZappUIComponent[] | any[];
39
- onListElementFocus?: (any, RenderItemProps, Direction) => void;
40
- onListElementBlur?: (any, RenderItemProps, Direction) => void;
41
- onListElementPress?: (any, RenderItemProps) => void;
42
- onListElementPressOut?: (any, RenderItemProps) => void;
43
- onListElementLongPress?: (any, RenderItemProps) => void;
41
+ onListElementFocus?: (
42
+ element: FocusManager.FocusableRef,
43
+ renderItemProps: { item: Item; index: number },
44
+ options: FocusManager.Android.CallbackOptions,
45
+ context: FocusManager.FocusContext
46
+ ) => void;
47
+ onListElementBlur?: (element, renderItemProps, direction) => void;
48
+ onListElementPress?: (element, renderItemProps) => void;
49
+ onListElementPressOut?: (element, renderItemProps) => void;
50
+ onListElementLongPress?: (element, renderItemProps) => void;
44
51
  focusableItemProps?: any;
45
52
  focused?: boolean;
46
53
  initialScrollIndex?: number;
@@ -91,6 +98,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
91
98
  // eslint-disable-next-line unused-imports/no-unused-vars
92
99
  omitPropsPropagation = [],
93
100
  useScrollView = false,
101
+ onScrollToIndexFailed = noop,
94
102
  } = props;
95
103
 
96
104
  useCheckItemIdsForUnique({ componentId: props.id, items: data });
@@ -137,11 +145,11 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
137
145
  );
138
146
 
139
147
  const onFocus = React.useCallback(
140
- (element, renderArgs, direction) => {
148
+ (element, renderArgs, options, context) => {
141
149
  const { index } = renderArgs;
142
150
 
143
151
  updateFocusedIndex?.(index);
144
- onListElementFocus?.(element, renderArgs, direction);
152
+ onListElementFocus?.(element, renderArgs, options, context);
145
153
  },
146
154
  [onListElementFocus, updateFocusedIndex]
147
155
  );
@@ -277,6 +285,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
277
285
  "withStateMemory",
278
286
  "useSequentialLoading",
279
287
  "useScrollView",
288
+ "onScrollToIndexFailed",
280
289
  ...omitPropsPropagation,
281
290
  ],
282
291
  R.__
@@ -305,6 +314,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
305
314
  {...getFlatListProps(props)}
306
315
  onEndReached={onEndReached}
307
316
  initialNumToRender={initialNumToRender}
317
+ onScrollToIndexFailed={onScrollToIndexFailed}
308
318
  renderItem={renderItem}
309
319
  focused={focused}
310
320
  data={data}
@@ -319,6 +329,7 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
319
329
  renderItem={renderItem}
320
330
  onEndReached={onEndReached}
321
331
  initialNumToRender={initialNumToRender}
332
+ onScrollToIndexFailed={onScrollToIndexFailed}
322
333
  />
323
334
  )}
324
335
  </ChildrenFocusDeactivatorView>
@@ -1,19 +1,18 @@
1
1
  import React, { Dispatch, useEffect, useState } from "react";
2
- import {
3
- act,
4
- create,
5
- ReactTestRenderer,
6
- ReactTestRendererJSON,
7
- } from "react-test-renderer";
2
+ import { act, render } from "@testing-library/react-native";
3
+ import { View } from "react-native";
8
4
  import { FreezeWithCallback } from "../index";
9
5
 
10
- const SimpleContent = () => <div />;
6
+ const SimpleContent = () => <View testID="simple-content" />;
11
7
 
12
8
  interface InnerProps {
13
9
  value: number;
14
10
  }
11
+
15
12
  // eslint-disable-next-line unused-imports/no-unused-vars
16
- const Inner = ({ value }: InnerProps) => <div />;
13
+ const Inner = ({ value }: InnerProps) => (
14
+ <View testID="inner-component" data-value={value} />
15
+ );
17
16
 
18
17
  interface TestSubscriber {
19
18
  renderCount: number;
@@ -51,24 +50,20 @@ const Container = ({ freeze, children }: ContainerProps) => (
51
50
  );
52
51
 
53
52
  function setupTest(initialFreeze: boolean = false) {
54
- let testRenderer: ReactTestRenderer | undefined;
55
53
  const [Subscriber, subscriberState] = createSubscriberComponent();
56
54
 
57
- act(() => {
58
- testRenderer = create(
59
- <Container freeze={initialFreeze}>
60
- <Subscriber />
61
- </Container>
62
- );
63
- });
55
+ const renderResult = render(
56
+ <Container freeze={initialFreeze}>
57
+ <Subscriber />
58
+ </Container>
59
+ );
64
60
 
65
61
  return {
66
- testRenderer,
67
- testInstance: testRenderer?.root,
62
+ renderResult,
68
63
  subscriberState,
69
64
  updateFreeze: (freeze: boolean) => {
70
65
  act(() =>
71
- testRenderer?.update(
66
+ renderResult.rerender(
72
67
  <Container freeze={freeze}>
73
68
  <Subscriber />
74
69
  </Container>
@@ -80,91 +75,120 @@ function setupTest(initialFreeze: boolean = false) {
80
75
 
81
76
  describe("FreezeWithCallback", () => {
82
77
  test("Renders stuff not frozen", () => {
83
- const testRenderer = create(
78
+ const { getByTestId } = render(
84
79
  <Container freeze={false}>
85
80
  <SimpleContent />
86
81
  </Container>
87
82
  );
88
83
 
89
- expect(testRenderer.root.findByType(SimpleContent)).toBeTruthy();
84
+ expect(getByTestId("simple-content")).toBeTruthy();
90
85
  });
91
86
 
92
87
  test("Does not render stuff when frozen", () => {
93
- const testRenderer = create(
88
+ const { queryByTestId } = render(
94
89
  <Container freeze>
95
90
  <SimpleContent />
96
91
  </Container>
97
92
  );
98
93
 
99
- expect(testRenderer.root.findAllByType(SimpleContent)).toHaveLength(0);
94
+ expect(queryByTestId("simple-content")).toBe(null);
100
95
  });
101
96
 
102
97
  test("Stuff is gone after freeze", () => {
103
- const testRenderer = create(
98
+ const { toJSON, getByTestId, rerender } = render(
104
99
  <Container freeze={false}>
105
100
  <SimpleContent />
106
101
  </Container>
107
102
  );
108
103
 
109
- expect(testRenderer.root.findByType(SimpleContent)).toBeTruthy();
104
+ expect(getByTestId("simple-content")).toBeTruthy();
110
105
 
111
106
  act(() =>
112
- testRenderer.update(
107
+ rerender(
113
108
  <Container freeze>
114
109
  <SimpleContent />
115
110
  </Container>
116
111
  )
117
112
  );
118
113
 
119
- expect(testRenderer.toJSON()).toBe(null);
114
+ expect(toJSON()).toBe(null);
120
115
  });
121
116
 
122
117
  test("Updates work when not frozen", () => {
123
- const { testInstance, subscriberState } = setupTest();
118
+ const { renderResult, subscriberState } = setupTest();
119
+
120
+ expect(
121
+ renderResult.getByTestId("inner-component").props["data-value"]
122
+ ).toEqual(0);
124
123
 
125
- expect(testInstance?.findByType(Inner).props.value).toEqual(0);
126
124
  act(() => subscriberState.subscription(1));
127
- expect(testInstance?.findByType(Inner).props.value).toEqual(1);
125
+
126
+ expect(
127
+ renderResult.getByTestId("inner-component").props["data-value"]
128
+ ).toEqual(1);
129
+
128
130
  expect(subscriberState.renderCount).toBe(2);
129
131
  });
130
132
 
131
133
  test("Updates does not propagate when frozen", () => {
132
- const { testInstance, subscriberState, updateFreeze } = setupTest();
134
+ const { renderResult, subscriberState, updateFreeze } = setupTest();
135
+
136
+ expect(
137
+ renderResult.getByTestId("inner-component").props["data-value"]
138
+ ).toEqual(0);
133
139
 
134
- expect(testInstance?.findByType(Inner).props.value).toEqual(0);
135
140
  updateFreeze(true);
136
141
  act(() => subscriberState.subscription(1));
137
- expect(testInstance?.findByType(Inner).props.value).toEqual(0);
142
+
143
+ expect(
144
+ renderResult.getByTestId("inner-component").props["data-value"]
145
+ ).toEqual(0);
146
+
138
147
  expect(subscriberState.renderCount).toBe(1);
139
148
  });
140
149
 
141
150
  test("State persists after defrost", () => {
142
- const { testInstance, subscriberState, updateFreeze, testRenderer } =
143
- setupTest();
151
+ const { renderResult, subscriberState, updateFreeze } = setupTest();
152
+
153
+ expect(
154
+ renderResult.getByTestId("inner-component").props["data-value"]
155
+ ).toEqual(0);
144
156
 
145
- expect(testInstance?.findByType(Inner).props.value).toEqual(0);
146
157
  act(() => subscriberState.subscription(1));
147
- expect(testInstance?.findByType(Inner).props.value).toEqual(1);
158
+
159
+ expect(
160
+ renderResult.getByTestId("inner-component").props["data-value"]
161
+ ).toEqual(1);
148
162
 
149
163
  updateFreeze(true);
150
- expect(testRenderer?.toJSON()).toBe(null);
164
+ expect(renderResult.toJSON()).toBe(null);
151
165
 
152
166
  updateFreeze(false);
153
- expect((testRenderer?.toJSON() as ReactTestRendererJSON).type).toBe("div");
154
- expect(testInstance?.findByType(Inner).props.value).toEqual(1);
167
+ expect(renderResult.getByTestId("inner-component")).toBeTruthy();
168
+
169
+ expect(
170
+ renderResult.getByTestId("inner-component").props["data-value"]
171
+ ).toEqual(1);
155
172
  });
156
173
 
157
174
  test("Update propagate after defrost", () => {
158
- const { testInstance, subscriberState, updateFreeze } = setupTest();
175
+ const { renderResult, subscriberState, updateFreeze } = setupTest();
159
176
 
160
177
  updateFreeze(true);
161
178
  act(() => subscriberState.subscription(1));
162
179
  act(() => subscriberState.subscription(2));
163
180
  act(() => subscriberState.subscription(3));
164
- expect(testInstance?.findByType(Inner).props.value).toEqual(0);
181
+
182
+ expect(
183
+ renderResult.getByTestId("inner-component").props["data-value"]
184
+ ).toEqual(0);
165
185
 
166
186
  updateFreeze(false);
167
- expect(testInstance?.findByType(Inner).props.value).toEqual(3);
187
+
188
+ expect(
189
+ renderResult.getByTestId("inner-component").props["data-value"]
190
+ ).toEqual(3);
191
+
168
192
  expect(subscriberState.renderCount).toBe(2);
169
193
  });
170
194
  });
@@ -8,13 +8,10 @@ import {
8
8
  useCurationAPI,
9
9
  } from "../useCurationAPI";
10
10
  import * as redux from "react-redux";
11
- import * as layoutPresets from "@applicaster/zapp-react-native-redux/hooks/useLayoutPresets";
12
- import * as pipesFeeds from "@applicaster/zapp-react-native-redux/hooks/useZappPipesFeeds";
13
11
  import { NavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NavigationContext";
14
12
  import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
15
13
 
16
- import { Provider } from "react-redux";
17
- import configureStore from "redux-mock-store";
14
+ import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
18
15
 
19
16
  jest.mock(
20
17
  "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute",
@@ -42,19 +39,20 @@ const mainStackNavigator = {
42
39
  },
43
40
  };
44
41
 
45
- const store = configureStore()({});
46
-
47
- const wrapper = ({ children }) => (
48
- <Provider store={store}>
49
- <NavigationContext.Provider
50
- value={{ ...mainStackNavigator, currentRoute: homeStack.route }}
51
- >
52
- <PathnameContext.Provider value={homeStack.route}>
53
- {children}
54
- </PathnameContext.Provider>
55
- </NavigationContext.Provider>
56
- </Provider>
57
- );
42
+ const getWrapper =
43
+ (store) =>
44
+ // eslint-disable-next-line react/display-name, react/prop-types
45
+ ({ children }) => (
46
+ <WrappedWithProviders store={store}>
47
+ <NavigationContext.Provider
48
+ value={{ ...mainStackNavigator, currentRoute: homeStack.route }}
49
+ >
50
+ <PathnameContext.Provider value={homeStack.route}>
51
+ {children}
52
+ </PathnameContext.Provider>
53
+ </NavigationContext.Provider>
54
+ </WrappedWithProviders>
55
+ );
58
56
 
59
57
  describe("getTransformedPreset should return the passed components if smartComponents is empty", () => {
60
58
  describe("getTransformedPreset function", () => {
@@ -312,19 +310,10 @@ describe("getTransformedPreset should return the passed components if smartCompo
312
310
  { id: "02", component_type: "not_smart_another" },
313
311
  ];
314
312
 
315
- // mock the hooks
316
- const mockUseZappPipesFeeds = jest.spyOn(pipesFeeds, "useZappPipesFeeds");
317
- mockUseZappPipesFeeds.mockReturnValue({});
318
-
319
- const mockUseLayoutPresets = jest.spyOn(
320
- layoutPresets,
321
- "useLayoutPresets"
322
- );
323
-
324
- mockUseLayoutPresets.mockReturnValue({});
325
-
326
313
  const { result } = renderHook(() => useCurationAPI(mockComponents), {
327
- wrapper,
314
+ wrapper: getWrapper({
315
+ zappPipes: {},
316
+ }),
328
317
  });
329
318
 
330
319
  // if there are no smart components, it should return the original array
@@ -366,18 +355,15 @@ describe("getTransformedPreset should return the passed components if smartCompo
366
355
  "http://curation": { loading: false, data: { entry: mockPresetEntry } },
367
356
  };
368
357
 
369
- const mockUseZappPipesFeeds = jest.spyOn(pipesFeeds, "useZappPipesFeeds");
370
- mockUseZappPipesFeeds.mockReturnValue(mockFeeds);
371
-
372
- const mockUseLayoutPresets = jest.spyOn(
373
- layoutPresets,
374
- "useLayoutPresets"
375
- );
376
-
377
- mockUseLayoutPresets.mockReturnValue(mockLayoutPresets);
378
-
379
358
  const { result } = renderHook(() => useCurationAPI(mockComponents), {
380
- wrapper,
359
+ wrapper: getWrapper({
360
+ zappPipes: mockFeeds,
361
+ presetsMapping: {
362
+ presets_mappings: {
363
+ ...mockLayoutPresets,
364
+ },
365
+ },
366
+ }),
381
367
  });
382
368
 
383
369
  expect(result.current).toEqual(mockTransformedComponents);
@@ -437,19 +423,15 @@ describe("getTransformedPreset should return the passed components if smartCompo
437
423
  },
438
424
  };
439
425
 
440
- // mock the hooks
441
- const mockUseZappPipesFeeds = jest.spyOn(pipesFeeds, "useZappPipesFeeds");
442
- mockUseZappPipesFeeds.mockReturnValue(mockFeeds);
443
-
444
- const mockUseLayoutPresets = jest.spyOn(
445
- layoutPresets,
446
- "useLayoutPresets"
447
- );
448
-
449
- mockUseLayoutPresets.mockReturnValue(mockLayoutPresets);
450
-
451
426
  const { result } = renderHook(() => useCurationAPI(mockComponents), {
452
- wrapper,
427
+ wrapper: getWrapper({
428
+ zappPipes: mockFeeds,
429
+ presetsMapping: {
430
+ presets_mappings: {
431
+ ...mockLayoutPresets,
432
+ },
433
+ },
434
+ }),
453
435
  });
454
436
 
455
437
  expect(result.current).toEqual(mockTransformedComponents);
@@ -495,14 +477,15 @@ describe("getTransformedPreset should return the passed components if smartCompo
495
477
  "http://curation": { loading: false, data: { entry: mockPresetEntry } },
496
478
  };
497
479
 
498
- // mock the hooks
499
- const mockUseZappPipesFeeds = jest.spyOn(pipesFeeds, "useZappPipesFeeds");
500
- mockUseZappPipesFeeds.mockReturnValue(mockFeeds);
501
- const mockUseLayoutPresets = jest.spyOn(layoutPresets, "useLayoutPresets");
502
- mockUseLayoutPresets.mockReturnValue(mockLayoutPresets);
503
-
504
480
  const { result } = renderHook(() => useCurationAPI(mockComponents), {
505
- wrapper,
481
+ wrapper: getWrapper({
482
+ zappPipes: mockFeeds,
483
+ presetsMapping: {
484
+ presets_mappings: {
485
+ ...mockLayoutPresets,
486
+ },
487
+ },
488
+ }),
506
489
  });
507
490
 
508
491
  expect(result.current).toEqual(mockTransformedComponents);