@applicaster/zapp-react-native-ui-components 15.1.8 → 15.1.9-alpha.3547421944
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
- package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
- package/Components/AudioPlayer/tv/index.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +14 -8
- package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +43 -6
- package/Components/PlayerContainer/PlayerContainer.tsx +2 -4
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +65 -13
- package/package.json +5 -5
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { render } from "@testing-library/react-native";
|
|
3
3
|
|
|
4
|
+
let mockContent = { title: null, summary: null };
|
|
5
|
+
|
|
6
|
+
// Read lazily inside the factory: `jest.mock` is hoisted above the `let` above,
|
|
7
|
+
// so capturing the value eagerly would freeze it at `undefined`.
|
|
8
|
+
jest.mock(
|
|
9
|
+
"@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerContent",
|
|
10
|
+
() => ({ usePlayerContent: () => mockContent })
|
|
11
|
+
);
|
|
12
|
+
|
|
4
13
|
import { AudioPlayerTV } from "..";
|
|
5
14
|
|
|
6
15
|
jest.mock("@applicaster/zapp-react-native-utils/audioPlayerUtils", () => ({
|
|
@@ -51,7 +60,17 @@ const audioPlayerProps = {
|
|
|
51
60
|
|
|
52
61
|
describe("<AudioPlayerTV />", () => {
|
|
53
62
|
it("renders correctly", () => {
|
|
63
|
+
// AudioPlayerTV no longer resolves its own title/summary from
|
|
64
|
+
// audio_item - it reads them from the player manager's content channel.
|
|
65
|
+
// Publishing the same values `audio_item.title`/`.summary` used to resolve
|
|
66
|
+
// to keeps this snapshot byte-for-byte identical to before that change.
|
|
67
|
+
mockContent = {
|
|
68
|
+
title: audioPlayerProps.audio_item.title,
|
|
69
|
+
summary: audioPlayerProps.audio_item.summary,
|
|
70
|
+
};
|
|
71
|
+
|
|
54
72
|
const { toJSON } = render(<AudioPlayerTV {...audioPlayerProps} />);
|
|
73
|
+
|
|
55
74
|
expect(toJSON()).toMatchSnapshot();
|
|
56
75
|
});
|
|
57
76
|
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
|
|
4
|
+
import { AudioPlayerTV } from "..";
|
|
5
|
+
|
|
6
|
+
jest.mock("@applicaster/zapp-react-native-utils/audioPlayerUtils", () => ({
|
|
7
|
+
useArtworkImage: jest.fn(() => "artwork_url"),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
let mockContent: {
|
|
11
|
+
title: Option<string | number>;
|
|
12
|
+
summary: Option<string | number>;
|
|
13
|
+
} = { title: null, summary: null };
|
|
14
|
+
|
|
15
|
+
// Read lazily inside the factory: `jest.mock` is hoisted above the `let`
|
|
16
|
+
// above, so capturing the value eagerly would freeze it at `undefined`.
|
|
17
|
+
jest.mock(
|
|
18
|
+
"@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerContent",
|
|
19
|
+
() => ({ usePlayerContent: () => mockContent })
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const baseAudioItem = {
|
|
23
|
+
title: "tittle",
|
|
24
|
+
summary: "Summary",
|
|
25
|
+
content: {
|
|
26
|
+
src: "https://example.com",
|
|
27
|
+
},
|
|
28
|
+
media_group: [
|
|
29
|
+
{
|
|
30
|
+
type: "audio",
|
|
31
|
+
media_item: [
|
|
32
|
+
{
|
|
33
|
+
type: "audio",
|
|
34
|
+
src: "https://example.com",
|
|
35
|
+
key: "base_code",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
extensions: {
|
|
41
|
+
audio_player_artwork_aspect_ratio: "1:1",
|
|
42
|
+
audio_player_background_image: "https://example.com",
|
|
43
|
+
audio_player_background_color: "black",
|
|
44
|
+
audio_player_channel_icon: "https://example.com",
|
|
45
|
+
audio_player_title_color: "white",
|
|
46
|
+
audio_player_summary_color: "white",
|
|
47
|
+
audio_player_rtl: true,
|
|
48
|
+
start_time: "10:10",
|
|
49
|
+
end_time: "11:11",
|
|
50
|
+
episode_name: "Episode Custom Title",
|
|
51
|
+
episode_summary: "Episode Custom Summary",
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const basePluginConfiguration = {
|
|
56
|
+
audio_player_background_color: "black",
|
|
57
|
+
audio_player_title_color: "white",
|
|
58
|
+
audio_player_summary_color: "white",
|
|
59
|
+
audio_player_rtl: true,
|
|
60
|
+
audio_player_background_image: "https://example.com",
|
|
61
|
+
audio_player_artwork_aspect_ratio: "1:1",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function renderAudioPlayer(
|
|
65
|
+
plugin_configuration,
|
|
66
|
+
content: {
|
|
67
|
+
title?: Option<string | number>;
|
|
68
|
+
summary?: Option<string | number>;
|
|
69
|
+
} = {}
|
|
70
|
+
) {
|
|
71
|
+
mockContent = {
|
|
72
|
+
title: content.title ?? null,
|
|
73
|
+
summary: content.summary ?? null,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return render(
|
|
77
|
+
<AudioPlayerTV
|
|
78
|
+
audio_item={baseAudioItem}
|
|
79
|
+
plugin_configuration={plugin_configuration}
|
|
80
|
+
style={{}}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe("<AudioPlayerTV /> player content", () => {
|
|
86
|
+
it("renders the title and summary published by the player manager", () => {
|
|
87
|
+
const { getByText } = renderAudioPlayer(basePluginConfiguration, {
|
|
88
|
+
title: "Episode Custom Title",
|
|
89
|
+
summary: "Episode Custom Summary",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(getByText("Episode Custom Title")).toBeTruthy();
|
|
93
|
+
expect(getByText("Episode Custom Summary")).toBeTruthy();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("renders nothing for title/summary when the player published neither", () => {
|
|
97
|
+
const { queryByText } = renderAudioPlayer(basePluginConfiguration);
|
|
98
|
+
|
|
99
|
+
expect(queryByText("tittle")).toBeNull();
|
|
100
|
+
expect(queryByText("Summary")).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("renders blank rather than crashing when no player is active", () => {
|
|
104
|
+
// `usePlayerContent` returns `{ title: null, summary: null }` when there is
|
|
105
|
+
// no player. A regression here means AudioPlayerTV crashes instead of
|
|
106
|
+
// rendering blank.
|
|
107
|
+
mockContent = { title: null, summary: null };
|
|
108
|
+
|
|
109
|
+
const { queryByText } = render(
|
|
110
|
+
<AudioPlayerTV
|
|
111
|
+
audio_item={baseAudioItem}
|
|
112
|
+
plugin_configuration={basePluginConfiguration}
|
|
113
|
+
style={{}}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
expect(queryByText("tittle")).toBeNull();
|
|
118
|
+
expect(queryByText("Summary")).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -3,6 +3,8 @@ import React, { useCallback, useMemo } from "react";
|
|
|
3
3
|
import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
4
4
|
import { useArtworkImage } from "@applicaster/zapp-react-native-utils/audioPlayerUtils";
|
|
5
5
|
|
|
6
|
+
import { usePlayerContent } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerContent";
|
|
7
|
+
|
|
6
8
|
import { AudioPlayerTVLayout } from "./Layout";
|
|
7
9
|
|
|
8
10
|
import { Channel } from "./Channel";
|
|
@@ -15,10 +17,12 @@ import { Props } from "../types";
|
|
|
15
17
|
|
|
16
18
|
export function AudioPlayerTV(props: Props) {
|
|
17
19
|
const { audio_item, plugin_configuration, style = {} } = props;
|
|
18
|
-
const { extensions
|
|
20
|
+
const { extensions } = audio_item;
|
|
19
21
|
|
|
20
22
|
const artwork = useArtworkImage(audio_item);
|
|
21
23
|
|
|
24
|
+
const { title, summary } = usePlayerContent();
|
|
25
|
+
|
|
22
26
|
const getProp = useCallback(
|
|
23
27
|
getPropertyFromEntryOrConfig({
|
|
24
28
|
entry: audio_item,
|
|
@@ -18,8 +18,11 @@ export const Toggle = (props: Props) => {
|
|
|
18
18
|
|
|
19
19
|
const { trackColor, trackColorActive, thumbColor, thumbColorActive } = style;
|
|
20
20
|
|
|
21
|
+
// `focused_selected` is the selected state with a finger on the row, so the
|
|
22
|
+
// switch has to read as on for both. Leaving it out reads a pressed selected
|
|
23
|
+
// row as off, which flips the thumb for the duration of the press.
|
|
21
24
|
const isSelected = React.useMemo(
|
|
22
|
-
() => cellState === "selected",
|
|
25
|
+
() => cellState === "selected" || cellState === "focused_selected",
|
|
23
26
|
[item?.id, cellState]
|
|
24
27
|
);
|
|
25
28
|
|
|
@@ -30,13 +33,16 @@ export const Toggle = (props: Props) => {
|
|
|
30
33
|
// selection state and writes through the entry's tap actions, the same path a
|
|
31
34
|
// tap on the row takes.
|
|
32
35
|
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
36
|
+
// An empty identifier is the way to ask for that, which is why the cell
|
|
37
|
+
// manifest declares no initial_value for `toggle_action_identifier`:
|
|
38
|
+
// `castOrDefault` substitutes the default whenever the configured value is
|
|
39
|
+
// empty - and an empty string counts as empty - so any default there would be
|
|
40
|
+
// uncleanable and would force every toggle onto that plugin.
|
|
41
|
+
//
|
|
42
|
+
// The route is still decided on whether the identifier resolves to an
|
|
43
|
+
// installed action plugin rather than on the string being non-empty. App
|
|
44
|
+
// configurations saved while the old default was in the manifest still carry
|
|
45
|
+
// it, and a mistyped plugin id has to fall back the same way.
|
|
40
46
|
const hasActionPlugin = Boolean(
|
|
41
47
|
action?.identifier && actionContext?.invokeAction
|
|
42
48
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { fireEvent, render } from "@testing-library/react-native";
|
|
3
|
+
import { Switch } from "react-native";
|
|
3
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
4
5
|
|
|
5
6
|
import { Toggle } from "../Toggle";
|
|
@@ -24,7 +25,6 @@ const style = {
|
|
|
24
25
|
trackColorActive: "rgba(4,207,153,1)",
|
|
25
26
|
thumbColor: "rgba(255,255,255,1)",
|
|
26
27
|
thumbColorActive: "rgba(255,255,255,1)",
|
|
27
|
-
iosBackgroundColor: "rgba(0,0,0,1)",
|
|
28
28
|
} as any;
|
|
29
29
|
|
|
30
30
|
const item = { id: "true", title: "I agree" } as any;
|
|
@@ -56,6 +56,21 @@ describe("Toggle with an empty action identifier", () => {
|
|
|
56
56
|
expect(getByRole("switch").props.value).toBe(false);
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
+
// A finger on a selected row resolves the cell to focused_selected. Reading
|
|
60
|
+
// that as unselected flipped the thumb for the duration of the press and let
|
|
61
|
+
// it jump back on release, which reads as a second tap.
|
|
62
|
+
it("stays on while a selected cell is pressed", () => {
|
|
63
|
+
const { getByRole } = renderToggle("", "focused_selected");
|
|
64
|
+
|
|
65
|
+
expect(getByRole("switch").props.value).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("is off while an unselected cell is pressed", () => {
|
|
69
|
+
const { getByRole } = renderToggle("", "focused");
|
|
70
|
+
|
|
71
|
+
expect(getByRole("switch").props.value).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
59
74
|
it("routes a change through the cell click path", () => {
|
|
60
75
|
const { getByRole } = renderToggle("", "default");
|
|
61
76
|
|
|
@@ -70,6 +85,28 @@ describe("Toggle with an empty action identifier", () => {
|
|
|
70
85
|
|
|
71
86
|
expect(mockInitialEntryState).not.toHaveBeenCalled();
|
|
72
87
|
});
|
|
88
|
+
|
|
89
|
+
// Asserted on the Switch element rather than the rendered host component:
|
|
90
|
+
// what Toggle owns is the props it hands to Switch, and React Native rewrites
|
|
91
|
+
// them into platform-specific ones (tintColor, a style entry) underneath.
|
|
92
|
+
it("forwards every configured color to the switch", () => {
|
|
93
|
+
const { UNSAFE_getByType } = renderToggle("", "default");
|
|
94
|
+
|
|
95
|
+
expect(UNSAFE_getByType(Switch).props).toMatchObject({
|
|
96
|
+
trackColor: { false: style.trackColor, true: style.trackColorActive },
|
|
97
|
+
thumbColor: style.thumbColor,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ios_backgroundColor paints an opaque layer behind the track that the track
|
|
102
|
+
// color cannot cover, which on iOS reads as a halo around the switch. The
|
|
103
|
+
// switch is left on its platform default instead, so the prop must stay
|
|
104
|
+
// unset even if a style happens to carry a color for it.
|
|
105
|
+
it("does not set an iOS background color on the switch", () => {
|
|
106
|
+
const { UNSAFE_getByType } = renderToggle("", "default");
|
|
107
|
+
|
|
108
|
+
expect(UNSAFE_getByType(Switch).props.ios_backgroundColor).toBeUndefined();
|
|
109
|
+
});
|
|
73
110
|
});
|
|
74
111
|
|
|
75
112
|
describe("Toggle with an action plugin identifier", () => {
|
|
@@ -92,11 +129,11 @@ describe("Toggle with an action plugin identifier", () => {
|
|
|
92
129
|
});
|
|
93
130
|
|
|
94
131
|
describe("Toggle with an identifier whose action plugin is not installed", () => {
|
|
95
|
-
// The cell manifest
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
// non-empty identifier it never asked for - the presence of a real
|
|
99
|
-
// context, not the identifier string, is what decides the route.
|
|
132
|
+
// The cell manifest no longer defaults toggle_action_identifier, but app
|
|
133
|
+
// configurations saved while it did still carry "firebase-push-topic-action",
|
|
134
|
+
// and a mistyped id is always possible. A toggle can therefore still arrive
|
|
135
|
+
// with a non-empty identifier it never asked for - the presence of a real
|
|
136
|
+
// action context, not the identifier string, is what decides the route.
|
|
100
137
|
beforeEach(() => {
|
|
101
138
|
jest.clearAllMocks();
|
|
102
139
|
|
|
@@ -189,12 +189,10 @@ const renderApplePlayer = ({
|
|
|
189
189
|
return null;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
// Title/Subtitle are not resolved here: `ProgramInfo` reads them from the
|
|
193
|
+
// player manager's content channel via `usePlayerContent`.
|
|
194
194
|
return (
|
|
195
195
|
<ProgramInfo
|
|
196
|
-
title={title}
|
|
197
|
-
subtitle={summary}
|
|
198
196
|
plugin_configuration={pluginConfiguration}
|
|
199
197
|
videoStyle={videoStyle}
|
|
200
198
|
/>
|
|
@@ -7,12 +7,11 @@ import * as React from "react";
|
|
|
7
7
|
import { Text, TextStyle, View, ViewStyle } from "react-native";
|
|
8
8
|
import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
|
|
9
9
|
|
|
10
|
+
import { usePlayerContent } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerContent";
|
|
10
11
|
import * as programStyles from "./styles";
|
|
11
12
|
|
|
12
13
|
type Props = {
|
|
13
14
|
logo?: string;
|
|
14
|
-
title: string;
|
|
15
|
-
subtitle: string;
|
|
16
15
|
isAudioContent: boolean;
|
|
17
16
|
styles: {};
|
|
18
17
|
plugin_configuration: {
|
|
@@ -28,13 +27,13 @@ const defaultColor = "#EFEFEF";
|
|
|
28
27
|
|
|
29
28
|
function ProgramInfoComponent({
|
|
30
29
|
logo,
|
|
31
|
-
title = "",
|
|
32
|
-
subtitle = "",
|
|
33
30
|
isAudioContent,
|
|
34
31
|
styles,
|
|
35
32
|
plugin_configuration = {},
|
|
36
33
|
videoStyle = {},
|
|
37
34
|
}: Props) {
|
|
35
|
+
const { title = "", summary: subtitle = "" } = usePlayerContent();
|
|
36
|
+
|
|
38
37
|
const platformKey = styleKeys.style_namespace;
|
|
39
38
|
|
|
40
39
|
const colorTitle =
|
|
@@ -17,17 +17,37 @@ jest.mock(
|
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
|
|
20
|
+
// Defaults to `false` for the hardware-back-handling tests below, which
|
|
21
|
+
// key off `isTV`. The ProgramInfo/content-context describe block below
|
|
22
|
+
// overrides this per test (`isTV()` is also what PlayerContainer's
|
|
23
|
+
// `playerContentValue` uses to pick TV vs mobile data keys, decoupled
|
|
24
|
+
// from `isTvOSPlatform` below).
|
|
20
25
|
isTV: jest.fn(() => false),
|
|
21
26
|
isAndroidTVPlatform: jest.fn(() => false),
|
|
22
|
-
isTvOSPlatform
|
|
27
|
+
// `isTvOSPlatform` backs a module-level constant in PlayerContainer.tsx that
|
|
28
|
+
// is read once at import time, so it must already be `true` here in order
|
|
29
|
+
// to exercise `renderApplePlayer`'s tvOS-only ProgramInfo branch in this
|
|
30
|
+
// test file. It does not affect the hardware-back-handling tests below,
|
|
31
|
+
// which key off `isTV` instead.
|
|
32
|
+
isTvOSPlatform: jest.fn(() => true),
|
|
23
33
|
isApplePlatform: jest.fn(() => true),
|
|
24
34
|
platformSelect: jest.fn(({ native }) => native),
|
|
25
35
|
}));
|
|
26
36
|
|
|
27
|
-
jest.mock("@applicaster/zapp-react-native-utils/playerUtils", () =>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
jest.mock("@applicaster/zapp-react-native-utils/playerUtils", () => {
|
|
38
|
+
// Keep the real data-key resolver (createConfigValueGetter,
|
|
39
|
+
// resolvePlayerContentText, VIDEO_PLAYER_CONTENT_DATA_KEYS) so the tvOS
|
|
40
|
+
// ProgramInfo data-key tests exercise actual resolution logic, not a stub.
|
|
41
|
+
const actual = jest.requireActual(
|
|
42
|
+
"@applicaster/zapp-react-native-utils/playerUtils"
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
...actual,
|
|
47
|
+
isAudioItem: jest.fn(() => false),
|
|
48
|
+
isInlineTV: jest.fn(() => false),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
31
51
|
|
|
32
52
|
jest.mock(
|
|
33
53
|
"@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent",
|
|
@@ -76,6 +96,11 @@ jest.mock("@applicaster/zapp-react-native-bridge/QuickBrick", () => ({
|
|
|
76
96
|
sendQuickBrickEvent: jest.fn(),
|
|
77
97
|
}));
|
|
78
98
|
|
|
99
|
+
// Stubbed so PlayerContainer can render without ProgramInfo's own
|
|
100
|
+
// redux/style plumbing. It no longer needs to capture Title/Subtitle: those
|
|
101
|
+
// come from the player manager's content channel, and the key-set selection
|
|
102
|
+
// they depend on is covered directly in
|
|
103
|
+
// `playerManager/__tests__/playerContent.test.ts`.
|
|
79
104
|
jest.mock("../ProgramInfo", () => ({
|
|
80
105
|
ProgramInfo: () => null,
|
|
81
106
|
}));
|
|
@@ -143,14 +168,33 @@ jest.mock(
|
|
|
143
168
|
jest.mock("../PlayerContainerContext", () => {
|
|
144
169
|
const ReactActual = require("react");
|
|
145
170
|
|
|
171
|
+
const PlayerContainerContext = ReactActual.createContext({
|
|
172
|
+
isLanguageOverlayVisible: false,
|
|
173
|
+
bottomFocusableId: "bottom",
|
|
174
|
+
showComponentsContainer: false,
|
|
175
|
+
refs: null,
|
|
176
|
+
});
|
|
177
|
+
|
|
146
178
|
return {
|
|
147
|
-
PlayerContainerContext
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
})
|
|
153
|
-
PlayerContainerContextProvider: ({ children }) =>
|
|
179
|
+
PlayerContainerContext,
|
|
180
|
+
// Mirrors just enough of the real provider for these tests: renders an
|
|
181
|
+
// actual `PlayerContainerContext.Provider` so the real
|
|
182
|
+
// `PlayerContainerContext.Consumer` inside PlayerContainer.tsx sees the
|
|
183
|
+
// container fields a real provider would give it, instead of a bare
|
|
184
|
+
// `({ children }) => children` passthrough.
|
|
185
|
+
PlayerContainerContextProvider: ({ children }: any) =>
|
|
186
|
+
ReactActual.createElement(
|
|
187
|
+
PlayerContainerContext.Provider,
|
|
188
|
+
{
|
|
189
|
+
value: {
|
|
190
|
+
isLanguageOverlayVisible: false,
|
|
191
|
+
bottomFocusableId: "bottom",
|
|
192
|
+
showComponentsContainer: false,
|
|
193
|
+
refs: null,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
children
|
|
197
|
+
),
|
|
154
198
|
};
|
|
155
199
|
});
|
|
156
200
|
|
|
@@ -203,7 +247,14 @@ const { __player: mockPlayer } = jest.requireMock(
|
|
|
203
247
|
"@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer"
|
|
204
248
|
);
|
|
205
249
|
|
|
206
|
-
const
|
|
250
|
+
const { isTV: mockIsTV } = jest.requireMock(
|
|
251
|
+
"@applicaster/zapp-react-native-utils/reactUtils"
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// Renders children so that `renderApplePlayer`'s returned <ProgramInfo>
|
|
255
|
+
// element (passed as this component's `children` prop) actually mounts —
|
|
256
|
+
// otherwise React never invokes the ProgramInfo function component at all.
|
|
257
|
+
const Player = React.forwardRef((props: any, _ref) => props.children ?? null);
|
|
207
258
|
|
|
208
259
|
const item = {
|
|
209
260
|
id: "entry-1",
|
|
@@ -237,6 +288,7 @@ const pressHardwareBack = () => {
|
|
|
237
288
|
describe("PlayerContainer hardware back handling", () => {
|
|
238
289
|
beforeEach(() => {
|
|
239
290
|
jest.clearAllMocks();
|
|
291
|
+
mockIsTV.mockReturnValue(false);
|
|
240
292
|
mockBackHandlerRef.current = null;
|
|
241
293
|
mockPlayer.getPluginConfiguration.mockReturnValue({});
|
|
242
294
|
mockPlayer.isPaused.mockReturnValue(false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.9-alpha.3547421944",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "15.1.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "15.1.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "15.1.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "15.1.
|
|
31
|
+
"@applicaster/applicaster-types": "15.1.9-alpha.3547421944",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "15.1.9-alpha.3547421944",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "15.1.9-alpha.3547421944",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "15.1.9-alpha.3547421944",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"url": "^0.11.0",
|