@applicaster/zapp-react-native-ui-components 14.0.0-alpha.2332850672 → 14.0.0-alpha.2373044050

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 (159) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/index.tsx +15 -0
  3. package/Components/AudioPlayer/{AudioPlayerMobileLayout.tsx → mobile/Layout.tsx} +10 -5
  4. package/Components/AudioPlayer/{__tests__ → mobile/__tests__}/__snapshots__/audioPlayerMobileLayout.test.js.snap +1 -1
  5. package/Components/AudioPlayer/{__tests__ → mobile/__tests__}/audioPlayerMobileLayout.test.js +2 -2
  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} +21 -97
  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 +12 -3
  32. package/Components/Focusable/FocusableTvOS.tsx +5 -5
  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 +1 -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/Layout/TV/LayoutBackground.tsx +1 -1
  48. package/Components/Layout/TV/__tests__/index.test.tsx +0 -1
  49. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +6 -2
  50. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -1
  51. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -39
  52. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +1 -1
  53. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +1 -1
  54. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +65 -17
  55. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +21 -3
  56. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +6 -3
  57. package/Components/MasterCell/DefaultComponents/Text/index.tsx +26 -6
  58. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +10 -10
  59. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +18 -18
  60. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/index.test.tsx +10 -10
  61. package/Components/MasterCell/elementMapper.tsx +1 -2
  62. package/Components/MasterCell/index.tsx +1 -1
  63. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  64. package/Components/MasterCell/utils/index.ts +11 -5
  65. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  66. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  67. package/Components/OfflineHandler/__tests__/index.test.tsx +26 -35
  68. package/Components/PlayerContainer/ErrorDisplay/index.ts +1 -1
  69. package/Components/PlayerContainer/PlayerContainer.tsx +46 -33
  70. package/Components/PlayerContainer/ProgramInfo/index.tsx +1 -1
  71. package/Components/PlayerContainer/index.ts +1 -1
  72. package/Components/PlayerImageBackground/index.tsx +1 -1
  73. package/Components/River/ComponentsMap/ComponentsMap.tsx +0 -1
  74. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +378 -0
  75. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
  76. package/Components/River/RefreshControl.tsx +11 -17
  77. package/Components/River/TV/River.tsx +2 -17
  78. package/Components/River/TV/index.tsx +3 -1
  79. package/Components/River/TV/withPipesV1DataLoader.tsx +43 -0
  80. package/Components/River/TV/withRiverDataLoader.tsx +17 -0
  81. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  82. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  83. package/Components/River/__tests__/river.test.js +12 -26
  84. package/Components/River/index.tsx +1 -1
  85. package/Components/Screen/__tests__/Screen.test.tsx +28 -29
  86. package/Components/Screen/__tests__/navigationHandler.test.ts +133 -22
  87. package/Components/Screen/navigationHandler.ts +20 -2
  88. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  89. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  90. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  91. package/Components/ScreenRevealManager/index.ts +1 -0
  92. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
  93. package/Components/Tabs/TV/Tabs.android.tsx +1 -3
  94. package/Components/Tabs/Tabs.tsx +2 -3
  95. package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +13 -0
  96. package/Components/TextInputTv/index.tsx +11 -0
  97. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  98. package/Components/Touchable/__tests__/touchable.test.tsx +12 -17
  99. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  100. package/Components/VideoLive/animationUtils.ts +3 -3
  101. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +3 -9
  102. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +294 -0
  103. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +93 -0
  104. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +73 -29
  105. package/Components/VideoModal/PlayerDetails.tsx +24 -2
  106. package/Components/VideoModal/PlayerWrapper.tsx +26 -142
  107. package/Components/VideoModal/VideoModal.tsx +3 -17
  108. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -5
  109. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -7
  110. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -240
  111. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +9 -1
  112. package/Components/VideoModal/hooks/index.ts +0 -2
  113. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +40 -15
  114. package/Components/VideoModal/hooks/useModalSize.ts +18 -2
  115. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +2 -2
  116. package/Components/VideoModal/hooks/utils/index.ts +4 -0
  117. package/Components/VideoModal/utils.ts +6 -0
  118. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  119. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  120. package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
  121. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
  122. package/Contexts/CellFocusedStateContext/index.tsx +27 -0
  123. package/Contexts/ConfigutaionContext/__tests__/ConfigurationProvider.test.tsx +3 -3
  124. package/Contexts/ScreenContext/index.tsx +46 -6
  125. package/Decorators/ConfigurationWrapper/__tests__/withConfigurationProvider.test.tsx +3 -3
  126. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  127. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +221 -209
  128. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +14 -16
  129. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -20
  130. package/Decorators/RiverFeedLoader/index.tsx +22 -4
  131. package/Decorators/RiverFeedLoader/utils/index.ts +0 -18
  132. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +3 -6
  133. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -0
  134. package/Decorators/ZappPipesDataConnector/__tests__/NullFeedResolver.test.tsx +78 -0
  135. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +205 -0
  136. package/Decorators/ZappPipesDataConnector/__tests__/StaticFeedResolver.test.tsx +251 -0
  137. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +368 -0
  138. package/Decorators/ZappPipesDataConnector/__tests__/utils.test.ts +39 -0
  139. package/Decorators/ZappPipesDataConnector/index.tsx +26 -293
  140. package/Decorators/ZappPipesDataConnector/resolvers/NullFeedResolver.tsx +25 -0
  141. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +87 -0
  142. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +266 -0
  143. package/Decorators/ZappPipesDataConnector/types.ts +29 -0
  144. package/Decorators/ZappPipesDataConnector/utils/mongoFilter.ts +738 -0
  145. package/Decorators/ZappPipesDataConnector/utils/useFilter.tsx +157 -0
  146. package/events/index.ts +3 -0
  147. package/package.json +5 -10
  148. package/Components/AudioPlayer/AudioPlayerTVLayout.tsx +0 -161
  149. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +0 -66
  150. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +0 -28
  151. package/Components/AudioPlayer/index.ts +0 -1
  152. package/Components/River/__tests__/__snapshots__/river.test.js.snap +0 -27
  153. package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
  154. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  155. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +0 -0
  156. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  157. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  158. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  159. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -1,7 +1,11 @@
1
1
  import React from "react";
2
2
  import { render } from "@testing-library/react-native";
3
3
 
4
- import { AudioPlayer } from "../AudioPlayer";
4
+ import { AudioPlayerTV } from "..";
5
+
6
+ jest.mock("@applicaster/zapp-react-native-utils/audioPlayerUtils", () => ({
7
+ useArtworkImage: jest.fn(() => "artwork_url"),
8
+ }));
5
9
 
6
10
  const audioPlayerProps = {
7
11
  audio_item: {
@@ -45,9 +49,9 @@ const audioPlayerProps = {
45
49
  styles: {},
46
50
  };
47
51
 
48
- describe("<AudioPlayer />", () => {
52
+ describe("<AudioPlayerTV />", () => {
49
53
  it("renders correctly", () => {
50
- const { toJSON } = render(<AudioPlayer {...audioPlayerProps} />);
54
+ const { toJSON } = render(<AudioPlayerTV {...audioPlayerProps} />);
51
55
  expect(toJSON()).toMatchSnapshot();
52
56
  });
53
57
  });
@@ -2,9 +2,8 @@ const defaults = {
2
2
  audio_player_title_color: "white",
3
3
  audio_player_summary_color: "white",
4
4
  audio_player_background_color: "black",
5
- audio_player_artwork_aspect_ratio: "1:1",
5
+ audio_player_background_image: undefined,
6
6
  audio_player_rtl: false,
7
- audio_player_background_image_default_color: "",
8
7
  };
9
8
 
10
9
  export function getPropertyFromEntryOrConfig({ entry, plugin_configuration }) {
@@ -28,13 +27,20 @@ const LTR = {
28
27
  justifyContent: "flex-start",
29
28
  textAlign: "left",
30
29
  alignItems: "flex-end",
31
- };
30
+ } as const;
32
31
 
33
32
  const RTL = {
34
33
  flexDirection: "row-reverse",
35
34
  justifyContent: "flex-end",
36
35
  textAlign: "right",
37
36
  alignItems: "flex-start",
38
- };
37
+ } as const;
39
38
 
40
- export const directionStyles = (isRTL) => (isRTL ? RTL : LTR);
39
+ export const directionStyles = (
40
+ isRTL: boolean
41
+ ): {
42
+ flexDirection: "row" | "row-reverse";
43
+ justifyContent: "flex-start" | "flex-end";
44
+ textAlign: "left" | "right";
45
+ alignItems: "flex-end" | "flex-start";
46
+ } => (isRTL ? RTL : LTR);
@@ -1,76 +1,23 @@
1
1
  import React, { useCallback, useMemo } from "react";
2
2
 
3
- import {
4
- platformSelect,
5
- isTV,
6
- } from "@applicaster/zapp-react-native-utils/reactUtils";
3
+ import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
4
+ import { useArtworkImage } from "@applicaster/zapp-react-native-utils/audioPlayerUtils";
7
5
 
8
- import { imageSrcFromMediaItem } from "@applicaster/zapp-react-native-utils/configurationUtils";
9
- import { getBackgroundImage } from "@applicaster/zapp-react-native-utils/audioPlayerUtils";
10
-
11
- import { AudioPlayerMobileLayout } from "./AudioPlayerMobileLayout";
12
- import { AudioPlayerTVLayout } from "./AudioPlayerTVLayout";
6
+ import { AudioPlayerTVLayout } from "./Layout";
13
7
 
14
8
  import { Channel } from "./Channel";
15
9
  import { Title } from "./Title";
16
10
  import { Summary } from "./Summary";
17
11
  import { Runtime } from "./Runtime";
18
12
  import { getPropertyFromEntryOrConfig } from "./helpers";
19
- import { ViewStyle } from "react-native";
20
-
21
- type Props = {
22
- audio_item: ZappEntry & {
23
- extensions?: {
24
- audio_player_artwork_aspect_ratio?: string;
25
- audio_player_background_image?: string;
26
- audio_player_background_color?: string;
27
- audio_player_channel_icon?: string;
28
- audio_player_title_color?: string;
29
- audio_player_summary_color?: string;
30
- audio_player_rtl?: boolean;
31
- audio_player_background_image_default_color?: string;
32
- start_time?: string;
33
- end_time?: string;
34
- };
35
- };
36
- plugin_configuration: {
37
- audio_player_background_color?: string;
38
- audio_player_title_color?: string;
39
- audio_player_summary_color?: string;
40
- audio_player_rtl?: string;
41
- audio_player_background_image_default_color?: string;
42
- audio_player_background_image?: string;
43
- audio_player_artwork_aspect_ratio?: string;
44
- lg_tv_audio_player_title_font_family?: string;
45
- lg_tv_audio_player_title_font_size?: number;
46
- lg_tv_audio_player_summary_font_family?: string;
47
- lg_tv_audio_player_summary_font_size?: number;
48
- samsung_tv_audio_player_title_font_family?: string;
49
- samsung_tv_audio_player_title_font_size?: number;
50
- samsung_tv_audio_player_summary_font_family?: string;
51
- samsung_tv_audio_player_summary_font_size?: number;
52
- tv_os_audio_player_title_font_family?: string;
53
- tv_os_audio_player_title_font_size?: number;
54
- tv_os_audio_player_summary_font_family?: string;
55
- tv_os_audio_player_summary_font_size?: number;
56
- };
57
- style?: ViewStyle;
58
- };
59
-
60
- export function AudioPlayer(props: Props) {
13
+
14
+ import { Props } from "../types";
15
+
16
+ export function AudioPlayerTV(props: Props) {
61
17
  const { audio_item, plugin_configuration, style = {} } = props;
62
18
  const { extensions, title, summary } = audio_item;
63
19
 
64
- const mobileConfig = useMemo(() => {
65
- const backgroundImage = getBackgroundImage({
66
- entry: audio_item,
67
- plugin_configuration,
68
- });
69
-
70
- return {
71
- backgroundImage,
72
- };
73
- }, [audio_item, plugin_configuration]);
20
+ const artwork = useArtworkImage(audio_item);
74
21
 
75
22
  const getProp = useCallback(
76
23
  getPropertyFromEntryOrConfig({
@@ -80,22 +27,21 @@ export function AudioPlayer(props: Props) {
80
27
  [audio_item, plugin_configuration]
81
28
  );
82
29
 
83
- const tvConfig = useMemo(() => {
84
- // Checking if we are recieving items from the DSP
30
+ const config = useMemo(() => {
31
+ // Checking if we are receiving items from the DSP
85
32
  const titleColor = getProp("audio_player_title_color");
86
33
  const summaryColor = getProp("audio_player_summary_color");
87
34
  const backgroundColor = getProp("audio_player_background_color");
88
35
  const backgroundImage = getProp("audio_player_background_image");
89
- const backgroundImageKey = getProp("audio_player_background_image_key");
90
- const artworkAspectRatio = getProp("audio_player_artwork_aspect_ratio");
91
36
  const channelIcon = getProp("audio_player_channel_icon");
92
37
  const rtlFlag = getProp("audio_player_rtl");
93
- const artworkBorderRadius = getProp("audio_player_artwork_border_radius");
94
38
 
95
- const audioPlayerBackgroundImageDefaultColor = getProp(
96
- "audio_player_background_image_default_color"
39
+ const backgroundImageOverlay = getProp(
40
+ "audio_player_background_image_overlay"
97
41
  );
98
42
 
43
+ const artworkBorderRadius = getProp("audio_player_artwork_border_radius");
44
+
99
45
  const isRTL = rtlFlag === "1" || rtlFlag === "true" || rtlFlag === true;
100
46
 
101
47
  const titleFontFamily = getProp(
@@ -163,7 +109,6 @@ export function AudioPlayer(props: Props) {
163
109
  summaryColor,
164
110
  backgroundColor,
165
111
  backgroundImage,
166
- backgroundImageKey,
167
112
  isRTL,
168
113
  titleFontFamily,
169
114
  titleFontSize,
@@ -171,39 +116,18 @@ export function AudioPlayer(props: Props) {
171
116
  summaryFontSize,
172
117
  runTimeFontFamily,
173
118
  runTimeFontSize,
174
- artworkAspectRatio,
175
119
  channelIcon,
176
- audioPlayerBackgroundImageDefaultColor,
177
120
  artworkBorderRadius,
121
+ backgroundImageOverlay,
178
122
  };
179
123
  }, [getProp]);
180
124
 
181
- const artwork = imageSrcFromMediaItem(audio_item, [
182
- tvConfig?.artworkAspectRatio,
183
- ]);
184
-
185
- console.log("debug_2", "AudioPlayer", {
186
- tvConfig,
187
- mobileConfig,
188
- audio_item,
189
- plugin_configuration,
190
- });
191
-
192
- if (isTV()) {
193
- return (
194
- <AudioPlayerTVLayout artwork={artwork} config={tvConfig} style={style}>
195
- <Channel srcImage={tvConfig?.channelIcon} config={tvConfig} />
196
- <Title title={title} config={tvConfig} />
197
- <Summary summary={summary} config={tvConfig} />
198
- <Runtime {...extensions} config={tvConfig} />
199
- </AudioPlayerTVLayout>
200
- );
201
- }
202
-
203
125
  return (
204
- <AudioPlayerMobileLayout
205
- backgroundImage={mobileConfig.backgroundImage}
206
- style={style}
207
- />
126
+ <AudioPlayerTVLayout artwork={artwork} config={config} style={style}>
127
+ <Channel srcImage={config?.channelIcon} config={config} />
128
+ <Title title={title} config={config} />
129
+ <Summary summary={summary} config={config} />
130
+ <Runtime {...extensions} config={config} />
131
+ </AudioPlayerTVLayout>
208
132
  );
209
133
  }
@@ -0,0 +1,40 @@
1
+ import { ViewStyle } from "react-native";
2
+
3
+ export type Props = {
4
+ audio_item: ZappEntry & {
5
+ extensions?: {
6
+ audio_player_artwork_aspect_ratio?: string;
7
+ audio_player_background_image?: string;
8
+ audio_player_background_color?: string;
9
+ audio_player_channel_icon?: string;
10
+ audio_player_title_color?: string;
11
+ audio_player_summary_color?: string;
12
+ audio_player_rtl?: boolean;
13
+ audio_player_background_image_default_color?: string;
14
+ start_time?: string;
15
+ end_time?: string;
16
+ };
17
+ };
18
+ plugin_configuration: {
19
+ audio_player_background_color?: string;
20
+ audio_player_title_color?: string;
21
+ audio_player_summary_color?: string;
22
+ audio_player_rtl?: string;
23
+ audio_player_background_image_default_color?: string;
24
+ audio_player_background_image?: string;
25
+ audio_player_artwork_aspect_ratio?: string;
26
+ lg_tv_audio_player_title_font_family?: string;
27
+ lg_tv_audio_player_title_font_size?: number;
28
+ lg_tv_audio_player_summary_font_family?: string;
29
+ lg_tv_audio_player_summary_font_size?: number;
30
+ samsung_tv_audio_player_title_font_family?: string;
31
+ samsung_tv_audio_player_title_font_size?: number;
32
+ samsung_tv_audio_player_summary_font_family?: string;
33
+ samsung_tv_audio_player_summary_font_size?: number;
34
+ tv_os_audio_player_title_font_family?: string;
35
+ tv_os_audio_player_title_font_size?: number;
36
+ tv_os_audio_player_summary_font_family?: string;
37
+ tv_os_audio_player_summary_font_size?: number;
38
+ };
39
+ style?: ViewStyle;
40
+ };
@@ -146,10 +146,14 @@ export class BaseFocusable<
146
146
  * @param {Object} scrollDirection
147
147
  * @returns {Promise}
148
148
  */
149
- onFocus: FocusManager.FocusEventCB = (focusable, scrollDirection) => {
149
+ onFocus: FocusManager.FocusEventCB = (
150
+ focusable,
151
+ scrollDirection,
152
+ context
153
+ ) => {
150
154
  const { onFocus = noop } = this.props;
151
155
  this.setFocusedState(true);
152
- onFocus(focusable, scrollDirection);
156
+ onFocus(focusable, scrollDirection, context);
153
157
  };
154
158
 
155
159
  /**
@@ -247,8 +251,8 @@ export class BaseFocusable<
247
251
  * @param {Object} scrollDirection
248
252
  * @returns {Promise}
249
253
  */
250
- focus(_, scrollDirection) {
251
- return this.onFocus(this, scrollDirection); // invokeComponentMethod(this, "onFocus", scrollDirection);
254
+ focus(_, scrollDirection, context?: FocusManager.FocusContext) {
255
+ return this.onFocus(this, scrollDirection, context); // invokeComponentMethod(this, "onFocus", scrollDirection, context);
252
256
  }
253
257
 
254
258
  /**
@@ -258,9 +262,10 @@ export class BaseFocusable<
258
262
  */
259
263
  blur(
260
264
  _,
261
- scrollDirection?: FocusManager.Web.Direction | FocusManager.IOS.Direction
265
+ scrollDirection?: FocusManager.Web.Direction | FocusManager.IOS.Direction,
266
+ context?: FocusManager.FocusContext
262
267
  ) {
263
- return this.onBlur(this, scrollDirection);
268
+ return this.onBlur(this, scrollDirection, context);
264
269
  }
265
270
 
266
271
  /**
@@ -272,7 +277,7 @@ export class BaseFocusable<
272
277
  * @param {string} scrollDirection string representation of the direction of the navigation which landed
273
278
  * to this item being focused
274
279
  */
275
- _executeFocusSequence(methodNames, scrollDirection) {
280
+ _executeFocusSequence(methodNames, scrollDirection, context) {
276
281
  return R.reduce(
277
282
  (sequence, methodName) => {
278
283
  const method = this[methodName]; // Access the method by name
@@ -284,7 +289,7 @@ export class BaseFocusable<
284
289
  }
285
290
 
286
291
  return sequence
287
- .then(() => method.call(this, scrollDirection))
292
+ .then(() => method.call(this, this, scrollDirection, context))
288
293
  .catch((e) => {
289
294
  throw e; // Re-throw for consistent error handling
290
295
  });
@@ -294,15 +299,21 @@ export class BaseFocusable<
294
299
  );
295
300
  }
296
301
 
297
- setFocus(scrollDirection) {
302
+ setFocus(
303
+ scrollDirection?: ScrollDirection,
304
+ context?: FocusManager.FocusContext
305
+ ) {
298
306
  const focusMethods = ["willReceiveFocus", "focus", "hasReceivedFocus"];
299
307
 
300
- return this._executeFocusSequence(focusMethods, scrollDirection);
308
+ return this._executeFocusSequence(focusMethods, scrollDirection, context);
301
309
  }
302
310
 
303
- setBlur(scrollDirection) {
311
+ setBlur(
312
+ scrollDirection?: ScrollDirection,
313
+ context?: FocusManager.FocusContext
314
+ ) {
304
315
  const blurMethods = ["willLoseFocus", "blur", "hasLostFocus"];
305
316
 
306
- return this._executeFocusSequence(blurMethods, scrollDirection);
317
+ return this._executeFocusSequence(blurMethods, scrollDirection, context);
307
318
  }
308
319
  }
@@ -8,6 +8,7 @@ import { getItemType } from "@applicaster/zapp-react-native-utils/navigationUtil
8
8
  import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
9
9
  import { sendSelectCellEvent } from "@applicaster/zapp-react-native-utils/analyticsUtils";
10
10
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
11
+ import { CellFocusedStateContextProvider } from "@applicaster/zapp-react-native-ui-components/Contexts/CellFocusedStateContext";
11
12
 
12
13
  import { CellWithFocusable } from "./CellWithFocusable";
13
14
  import { BaseFocusable } from "../BaseFocusable";
@@ -15,6 +16,7 @@ import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUt
15
16
  import { styles } from "./styles";
16
17
 
17
18
  type Props = {
19
+ dataLength: number;
18
20
  item: ZappEntry;
19
21
  index: number;
20
22
  shouldScrollHorizontally: (arg1: [any]) => boolean | null | undefined;
@@ -67,9 +69,12 @@ type Props = {
67
69
 
68
70
  type State = {
69
71
  hasFocusableInside: boolean;
72
+ cellFocused: boolean;
70
73
  };
71
74
 
72
75
  export class CellComponent extends React.Component<Props, State> {
76
+ accessibilityManager: AccessibilityManager;
77
+
73
78
  constructor(props) {
74
79
  super(props);
75
80
  this.onPress = this.onPress.bind(this);
@@ -79,10 +84,14 @@ export class CellComponent extends React.Component<Props, State> {
79
84
  this.hasReceivedFocus = this.hasReceivedFocus.bind(this);
80
85
  this.scrollVertically = this.scrollVertically.bind(this);
81
86
  this.scrollToIndex = this.scrollToIndex.bind(this);
87
+ this.handleAccessibilityFocus = this.handleAccessibilityFocus.bind(this);
82
88
 
83
89
  this.state = {
84
90
  hasFocusableInside: props.CellRenderer.hasFocusableInside?.(props.item),
91
+ cellFocused: false,
85
92
  };
93
+
94
+ this.accessibilityManager = AccessibilityManager.getInstance();
86
95
  }
87
96
 
88
97
  setScreenLayout(componentAnchorPointY, screenLayout) {
@@ -130,6 +139,8 @@ export class CellComponent extends React.Component<Props, State> {
130
139
  } = this.props;
131
140
 
132
141
  if (isFocusable) {
142
+ this.setState({ cellFocused: true });
143
+
133
144
  if (
134
145
  shouldUpdate &&
135
146
  shouldScrollVertically?.(mouse, focusable, id, title)
@@ -139,7 +150,9 @@ export class CellComponent extends React.Component<Props, State> {
139
150
  }
140
151
  }
141
152
 
142
- onBlur() {}
153
+ onBlur() {
154
+ this.setState({ cellFocused: false });
155
+ }
143
156
 
144
157
  willReceiveFocus() {}
145
158
 
@@ -183,6 +196,25 @@ export class CellComponent extends React.Component<Props, State> {
183
196
  return !isFocusable ? false : focused || focusableFocused;
184
197
  }
185
198
 
199
+ handleAccessibilityFocus(index, dataLength) {
200
+ // For loop scrolling, calculate the correct logical index
201
+ const logicalIndex = dataLength ? index % dataLength : index;
202
+
203
+ const positionLabel = dataLength
204
+ ? `item ${logicalIndex + 1} of ${dataLength}`
205
+ : "";
206
+
207
+ if (this.state.hasFocusableInside) {
208
+ this.accessibilityManager.readText({
209
+ text: " ",
210
+ });
211
+ } else {
212
+ this.accessibilityManager.readText({
213
+ text: `${positionLabel}`,
214
+ });
215
+ }
216
+ }
217
+
186
218
  componentDidUpdate(prevProps: Readonly<Props>) {
187
219
  if (prevProps.item !== this.props.item) {
188
220
  this.setState({
@@ -191,6 +223,8 @@ export class CellComponent extends React.Component<Props, State> {
191
223
  ),
192
224
  });
193
225
  }
226
+
227
+ this.handleAccessibilityFocus(this.props.index, this.props.dataLength);
194
228
  }
195
229
 
196
230
  render() {
@@ -212,7 +246,6 @@ export class CellComponent extends React.Component<Props, State> {
212
246
  } = this.props;
213
247
 
214
248
  const { id } = item;
215
-
216
249
  const focusableId = join("-", [component?.id, id, index]);
217
250
 
218
251
  const handleFocus = (focusable, mouse) => {
@@ -223,73 +256,67 @@ export class CellComponent extends React.Component<Props, State> {
223
256
 
224
257
  if (this.state.hasFocusableInside) {
225
258
  return (
226
- <CellWithFocusable
227
- CellRenderer={CellRenderer}
228
- item={item}
229
- id={focusableId}
230
- groupId={groupId || component?.id}
231
- onFocus={handleFocus}
232
- index={index}
233
- scrollTo={this.scrollToIndex()}
234
- isFocusable={isFocusable}
235
- skipFocusManagerRegistration={skipFocusManagerRegistration}
236
- behavior={behavior}
237
- />
259
+ <CellFocusedStateContextProvider cellFocused={this.state.cellFocused}>
260
+ <CellWithFocusable
261
+ CellRenderer={CellRenderer}
262
+ item={item}
263
+ id={focusableId}
264
+ groupId={groupId || component?.id}
265
+ onFocus={handleFocus}
266
+ onBlur={onBlur || this.onBlur}
267
+ index={index}
268
+ scrollTo={this.scrollToIndex()}
269
+ isFocusable={isFocusable}
270
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
271
+ behavior={behavior}
272
+ />
273
+ </CellFocusedStateContextProvider>
238
274
  );
239
275
  }
240
276
 
241
277
  return (
242
- <View
243
- testID={`${component?.id}-${id}`}
244
- accessible={false}
245
- style={styles.touchableCell}
246
- >
247
- <Focusable
248
- id={focusableId}
249
- groupId={groupId || component?.id}
250
- onFocus={handleFocus}
251
- onBlur={onBlur || this.onBlur}
252
- onPress={this.onPress}
253
- willReceiveFocus={willReceiveFocus || this.willReceiveFocus}
254
- hasReceivedFocus={hasReceivedFocus || this.hasReceivedFocus}
255
- preferredFocus={preferredFocus}
256
- offsetUpdater={offsetUpdater}
257
- style={styles.baseCell}
258
- isFocusable={isFocusable}
259
- skipFocusManagerRegistration={skipFocusManagerRegistration}
278
+ <CellFocusedStateContextProvider cellFocused={this.state.cellFocused}>
279
+ <View
280
+ testID={`${component?.id}-${id}`}
281
+ accessible={false}
282
+ style={styles.touchableCell}
260
283
  >
261
- {(focused, event) => {
262
- const isFocused = this.isCellFocused(focused);
263
-
264
- if (isFocused) {
265
- const accessibilityManager = AccessibilityManager.getInstance();
266
-
267
- const accessibilityTitle =
268
- item?.extensions?.accessibility?.label || item?.title || "";
269
-
270
- const accessibilityHint =
271
- item?.extensions?.accessibility?.hint || "";
272
-
273
- accessibilityManager.readText({
274
- text: `${accessibilityTitle} ${accessibilityHint}`,
275
- });
276
- }
277
-
278
- return (
279
- <FocusableCell
280
- {...{
281
- index,
282
- CellRenderer,
283
- item,
284
- focused: isFocused,
285
- scrollTo: this.scrollToIndex(event),
286
- behavior,
287
- }}
288
- />
289
- );
290
- }}
291
- </Focusable>
292
- </View>
284
+ <Focusable
285
+ id={focusableId}
286
+ groupId={groupId || component?.id}
287
+ onFocus={handleFocus}
288
+ onBlur={onBlur || this.onBlur}
289
+ onPress={this.onPress}
290
+ willReceiveFocus={willReceiveFocus || this.willReceiveFocus}
291
+ hasReceivedFocus={hasReceivedFocus || this.hasReceivedFocus}
292
+ preferredFocus={preferredFocus}
293
+ offsetUpdater={offsetUpdater}
294
+ style={styles.baseCell}
295
+ isFocusable={isFocusable}
296
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
297
+ {...this.accessibilityManager.getButtonAccessibilityProps(
298
+ item?.extensions?.accessibility?.label || item?.title
299
+ )}
300
+ >
301
+ {(focused, event) => {
302
+ const isFocused = this.isCellFocused(focused);
303
+
304
+ return (
305
+ <FocusableCell
306
+ {...{
307
+ index,
308
+ CellRenderer,
309
+ item,
310
+ focused: isFocused,
311
+ scrollTo: this.scrollToIndex(event),
312
+ behavior,
313
+ }}
314
+ />
315
+ );
316
+ }}
317
+ </Focusable>
318
+ </View>
319
+ </CellFocusedStateContextProvider>
293
320
  );
294
321
  }
295
322
  }
@@ -14,6 +14,7 @@ type Props = {
14
14
  id: string;
15
15
  groupId: string;
16
16
  onFocus: Function;
17
+ onBlur?: Function;
17
18
  index: number;
18
19
  scrollTo: Function;
19
20
  preferredFocus?: boolean;
@@ -33,6 +34,7 @@ export function CellWithFocusable(props: Props) {
33
34
  id,
34
35
  groupId,
35
36
  onFocus,
37
+ onBlur = noop,
36
38
  scrollTo = noop,
37
39
  preferredFocus,
38
40
  skipFocusManagerRegistration,
@@ -78,6 +80,7 @@ export function CellWithFocusable(props: Props) {
78
80
  const onGroupBlur = React.useCallback(() => {
79
81
  if (!skipFocusManagerRegistration) {
80
82
  setIsFocused(false);
83
+ onBlur?.();
81
84
  }
82
85
  }, [skipFocusManagerRegistration]);
83
86
 
@@ -1,12 +1,13 @@
1
1
  import { View } from "react-native";
2
2
  import React from "react";
3
- import { act, render } from "@testing-library/react-native";
3
+ import { act } from "@testing-library/react-native";
4
4
  import { CellWithFocusable } from "../CellWithFocusable.tsx";
5
5
 
6
6
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
7
+ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils/index.tsx";
7
8
 
8
9
  const renderWith = (props) => {
9
- return render(<CellWithFocusable {...props} />);
10
+ return renderWithProviders(<CellWithFocusable {...props} />);
10
11
  };
11
12
 
12
13
  describe("CellWithFocusable", () => {
@@ -1,13 +1,17 @@
1
1
  import * as R from "ramda";
2
2
 
3
- import { connectToStore } from "@applicaster/zapp-react-native-redux";
3
+ import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
4
4
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
5
5
 
6
- import { HorizontalScrollContext, RiverOffsetContext } from "../../Contexts";
6
+ import {
7
+ HorizontalScrollContext,
8
+ RiverOffsetContext,
9
+ ScreenScrollingContext,
10
+ } from "../../Contexts";
11
+
7
12
  import { CellComponent } from "./Cell";
8
13
  import { TvOSCellComponent } from "./TvOSCellComponent";
9
14
  import { withConsumer } from "../../Contexts/HeaderOffsetContext";
10
- import { ScreenScrollingContext } from "../../Contexts/ScreenScrollingContext";
11
15
 
12
16
  import { ScreenLayoutContextConsumer } from "../../Contexts/ScreenLayoutContext";
13
17
  import { createContext } from "@applicaster/zapp-react-native-utils/reactUtils/createContext";
@@ -1,6 +1,6 @@
1
1
  import * as R from "ramda";
2
2
 
3
- import { connectToStore } from "@applicaster/zapp-react-native-redux";
3
+ import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
4
4
 
5
5
  import { ComponentResolverComponent } from "./ComponentResolver";
6
6