@applicaster/zapp-react-native-ui-components 14.0.0-alpha.5351122050 → 14.0.0-alpha.5365702091
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/Cell/Cell.tsx +91 -64
- package/Components/Cell/CellWithFocusable.tsx +3 -0
- package/Components/FeedLoader/FeedLoader.tsx +1 -1
- package/Components/Focusable/Focusable.tsx +10 -3
- package/Components/Focusable/FocusableTvOS.tsx +2 -2
- package/Components/Focusable/Touchable.tsx +5 -3
- package/Components/Focusable/index.android.tsx +8 -4
- package/Components/FocusableGroup/FocusableTvOS.tsx +1 -1
- package/Components/FocusableList/FocusableItem.tsx +4 -3
- package/Components/FocusableList/FocusableListItemWrapper.tsx +2 -1
- package/Components/FocusableList/hooks/useCellState.android.ts +13 -3
- package/Components/FocusableList/index.tsx +16 -9
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +0 -12
- package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +65 -17
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +21 -3
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +6 -3
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +26 -6
- package/Components/PlayerContainer/PlayerContainer.tsx +4 -1
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +2 -2
- package/Components/Tabs/TV/Tabs.android.tsx +1 -1
- package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +13 -0
- package/Components/TextInputTv/index.tsx +11 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +42 -22
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +9 -1
- package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +40 -15
- package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +2 -2
- package/Components/VideoModal/hooks/utils/index.ts +4 -0
- package/Contexts/CellFocusedStateContext/index.tsx +27 -0
- package/events/index.ts +2 -0
- package/package.json +5 -5
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -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
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
|
|
@@ -8,7 +8,7 @@ type Props = {
|
|
|
8
8
|
typeof import("@applicaster/zapp-react-native-utils/reactHooks/feed").useLoadPipesDataDispatch
|
|
9
9
|
>;
|
|
10
10
|
feedUrl: string;
|
|
11
|
-
children: (feed: ZappFeed) => React.
|
|
11
|
+
children: (feed: ZappFeed) => React.ReactNode;
|
|
12
12
|
onFeedLoaded: (feed: ZappFeed) => {};
|
|
13
13
|
onError: (error: ZappPipesData["error"]) => {};
|
|
14
14
|
refreshing: boolean;
|
|
@@ -6,6 +6,7 @@ import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focu
|
|
|
6
6
|
import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
|
|
7
7
|
import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
|
|
8
8
|
import { StyleSheet, ViewStyle } from "react-native";
|
|
9
|
+
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
|
|
9
10
|
|
|
10
11
|
type Props = {
|
|
11
12
|
initialFocus?: boolean;
|
|
@@ -20,7 +21,7 @@ type Props = {
|
|
|
20
21
|
onPressOut?: () => void;
|
|
21
22
|
onLongPress?: () => void;
|
|
22
23
|
handleFocus?: ({ mouse }: { mouse: boolean }) => void;
|
|
23
|
-
children: (boolean, string) => React.
|
|
24
|
+
children: (boolean, string) => React.ReactNode;
|
|
24
25
|
selected?: boolean;
|
|
25
26
|
style?: ViewStyle[] | ViewStyle;
|
|
26
27
|
};
|
|
@@ -29,6 +30,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
29
30
|
isGroup: boolean;
|
|
30
31
|
mouse: boolean;
|
|
31
32
|
longPressTimeout = null;
|
|
33
|
+
accessibilityManager: AccessibilityManager;
|
|
32
34
|
|
|
33
35
|
constructor(props) {
|
|
34
36
|
super(props);
|
|
@@ -43,6 +45,8 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
43
45
|
this.resetLongPressTimeout = this.resetLongPressTimeout.bind(this);
|
|
44
46
|
this.longPress = this.longPress.bind(this);
|
|
45
47
|
this.press = this.press.bind(this);
|
|
48
|
+
|
|
49
|
+
this.accessibilityManager = AccessibilityManager.getInstance();
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
/**
|
|
@@ -123,12 +127,15 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
render() {
|
|
126
|
-
const { children, style
|
|
130
|
+
const { children, style } = this.props;
|
|
127
131
|
const { focused } = this.state;
|
|
128
132
|
|
|
129
133
|
const id = this.getId();
|
|
130
134
|
const focusableId = `focusable-${id}`;
|
|
131
135
|
|
|
136
|
+
const accessibilityProps =
|
|
137
|
+
this.accessibilityManager.getWebAccessibilityProps(this.props);
|
|
138
|
+
|
|
132
139
|
return (
|
|
133
140
|
<div
|
|
134
141
|
id={focusableId}
|
|
@@ -141,7 +148,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
141
148
|
data-testid={focusableId}
|
|
142
149
|
focused-teststate={focused ? "focused" : "default"}
|
|
143
150
|
style={StyleSheet.flatten(style) as any as React.CSSProperties}
|
|
144
|
-
{...
|
|
151
|
+
{...accessibilityProps}
|
|
145
152
|
>
|
|
146
153
|
{children(focused, { mouse: this.mouse })}
|
|
147
154
|
</div>
|
|
@@ -19,7 +19,7 @@ type Props = {
|
|
|
19
19
|
onPress?: (nativeEvent: any) => void;
|
|
20
20
|
onFocus?: (nativeEvent: any) => void;
|
|
21
21
|
onBlur?: (nativeEvent: any) => void;
|
|
22
|
-
children: (focused?: boolean) => React.ReactNode;
|
|
22
|
+
children: ((focused?: boolean) => React.ReactNode) | React.ReactNode;
|
|
23
23
|
isParallaxDisabled: boolean;
|
|
24
24
|
preferredFocus?: boolean;
|
|
25
25
|
selected?: boolean;
|
|
@@ -204,7 +204,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
204
204
|
{...this.nextFocusableReactTags}
|
|
205
205
|
{...otherProps}
|
|
206
206
|
>
|
|
207
|
-
{
|
|
207
|
+
{typeof children === "function" ? children(focused) : children}
|
|
208
208
|
</FocusableItemNative>
|
|
209
209
|
);
|
|
210
210
|
}
|
|
@@ -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(
|
|
@@ -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;
|
|
@@ -70,6 +72,7 @@ function FocusableComponent(props: Props, forwardedRef) {
|
|
|
70
72
|
onPressOut,
|
|
71
73
|
onLongPress,
|
|
72
74
|
onRegister = noop,
|
|
75
|
+
onUnregister = noop,
|
|
73
76
|
isFocusableCell = true,
|
|
74
77
|
onSetIsFocusable,
|
|
75
78
|
} = props;
|
|
@@ -113,9 +116,10 @@ function FocusableComponent(props: Props, forwardedRef) {
|
|
|
113
116
|
|
|
114
117
|
return () => {
|
|
115
118
|
unregister();
|
|
119
|
+
onUnregister();
|
|
116
120
|
};
|
|
117
121
|
}
|
|
118
|
-
}, [id, onRegister, isFocusableCell, parentFocusableId]);
|
|
122
|
+
}, [id, onRegister, onUnregister, isFocusableCell, parentFocusableId]);
|
|
119
123
|
|
|
120
124
|
if (R.isNil(id)) {
|
|
121
125
|
// eslint-disable-next-line no-console
|
|
@@ -125,9 +129,9 @@ function FocusableComponent(props: Props, forwardedRef) {
|
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
const _onFocus = React.useCallback(
|
|
128
|
-
(ref, options) => {
|
|
132
|
+
(ref, options, context) => {
|
|
129
133
|
setFocused(true);
|
|
130
|
-
onFocus?.(ref, options);
|
|
134
|
+
onFocus?.(ref, options, context);
|
|
131
135
|
},
|
|
132
136
|
[onFocus]
|
|
133
137
|
);
|
|
@@ -10,7 +10,8 @@ type FocusableItemComponentProps = {
|
|
|
10
10
|
onFocus: (
|
|
11
11
|
element: FocusManager.FocusableRef,
|
|
12
12
|
renderArgs: { item: FocusableItemComponentProps["item"]; index: number },
|
|
13
|
-
|
|
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,
|
|
49
|
-
onFocus?.(element, renderArgs,
|
|
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
|
-
|
|
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
|
|
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,
|
|
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,
|
|
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:
|
|
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?: (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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;
|
|
@@ -138,11 +145,11 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
|
|
|
138
145
|
);
|
|
139
146
|
|
|
140
147
|
const onFocus = React.useCallback(
|
|
141
|
-
(element, renderArgs,
|
|
148
|
+
(element, renderArgs, options, context) => {
|
|
142
149
|
const { index } = renderArgs;
|
|
143
150
|
|
|
144
151
|
updateFocusedIndex?.(index);
|
|
145
|
-
onListElementFocus?.(element, renderArgs,
|
|
152
|
+
onListElementFocus?.(element, renderArgs, options, context);
|
|
146
153
|
},
|
|
147
154
|
[onListElementFocus, updateFocusedIndex]
|
|
148
155
|
);
|
|
@@ -4,7 +4,6 @@ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Componen
|
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
5
|
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
6
6
|
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
7
|
-
import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
8
7
|
|
|
9
8
|
const { Logger } = getXray();
|
|
10
9
|
|
|
@@ -44,11 +43,6 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
44
43
|
|
|
45
44
|
const actionContext = useActions(pluginIdentifier);
|
|
46
45
|
|
|
47
|
-
const accessibilityManager = useAccessibilityManager({});
|
|
48
|
-
|
|
49
|
-
const { ttsLabel = "" } =
|
|
50
|
-
actionContext?.initialEntryState(item)?.getAccessibility?.(item) || {};
|
|
51
|
-
|
|
52
46
|
const onPress = (event) => {
|
|
53
47
|
event?.preventDefault?.();
|
|
54
48
|
|
|
@@ -71,12 +65,6 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
|
|
|
71
65
|
focusedButtonId,
|
|
72
66
|
mouse: focusable.mouse,
|
|
73
67
|
});
|
|
74
|
-
|
|
75
|
-
if (ttsLabel) {
|
|
76
|
-
accessibilityManager.readText({
|
|
77
|
-
text: ttsLabel,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
68
|
};
|
|
81
69
|
|
|
82
70
|
const handleBlur = (_focusable, _direction) => {
|
|
@@ -28,17 +28,10 @@ interface Props {
|
|
|
28
28
|
onAsyncRender: () => void;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
displayMode,
|
|
36
|
-
imageSizing,
|
|
37
|
-
fitPosition,
|
|
38
|
-
fixedHeight,
|
|
39
|
-
fixedWidth,
|
|
40
|
-
onAsyncRender,
|
|
41
|
-
} = props;
|
|
31
|
+
/** Secondary Image Dynamic does not render until the image is loaded */
|
|
32
|
+
const SecondaryImageDynamic = (props: Props) => {
|
|
33
|
+
const { uri, style, displayMode, imageSizing, fitPosition, onAsyncRender } =
|
|
34
|
+
props;
|
|
42
35
|
|
|
43
36
|
const imageDimension = useGetImageDimensions(
|
|
44
37
|
uri,
|
|
@@ -46,13 +39,9 @@ const SecondaryImageComponent = (props: Props) => {
|
|
|
46
39
|
isImageSizingFit(imageSizing) ? undefined : (style.height as number)
|
|
47
40
|
);
|
|
48
41
|
|
|
49
|
-
const containerHeight =
|
|
50
|
-
? fixedHeight
|
|
51
|
-
: imageDimension?.height;
|
|
42
|
+
const containerHeight = imageDimension?.height;
|
|
52
43
|
|
|
53
|
-
const containerWidth =
|
|
54
|
-
? fixedWidth
|
|
55
|
-
: style?.width;
|
|
44
|
+
const containerWidth = style?.width;
|
|
56
45
|
|
|
57
46
|
if (isNil(imageDimension?.aspectRatio)) {
|
|
58
47
|
return null;
|
|
@@ -80,4 +69,63 @@ const SecondaryImageComponent = (props: Props) => {
|
|
|
80
69
|
);
|
|
81
70
|
};
|
|
82
71
|
|
|
72
|
+
/** Secondary Image Fixed does not render the image until the image is loaded, but keep container rendered */
|
|
73
|
+
const SecondaryImageFixed = (props: Props) => {
|
|
74
|
+
const {
|
|
75
|
+
uri,
|
|
76
|
+
style,
|
|
77
|
+
displayMode,
|
|
78
|
+
imageSizing,
|
|
79
|
+
fitPosition,
|
|
80
|
+
fixedHeight,
|
|
81
|
+
fixedWidth,
|
|
82
|
+
onAsyncRender,
|
|
83
|
+
} = props;
|
|
84
|
+
|
|
85
|
+
const imageDimension = useGetImageDimensions(
|
|
86
|
+
uri,
|
|
87
|
+
style.width as number,
|
|
88
|
+
isImageSizingFit(imageSizing) ? undefined : (style.height as number)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<View style={style} onLayout={onAsyncRender}>
|
|
93
|
+
{isNil(imageDimension?.aspectRatio) ? null : (
|
|
94
|
+
<Image
|
|
95
|
+
{...props}
|
|
96
|
+
source={{ uri }}
|
|
97
|
+
style={{
|
|
98
|
+
...getStyle({
|
|
99
|
+
imageSizing,
|
|
100
|
+
fitPosition,
|
|
101
|
+
displayMode,
|
|
102
|
+
imageDimension,
|
|
103
|
+
containerHeight: fixedHeight,
|
|
104
|
+
containerWidth: fixedWidth,
|
|
105
|
+
}),
|
|
106
|
+
borderRadius: style.borderRadius,
|
|
107
|
+
aspectRatio: imageDimension.aspectRatio,
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
)}
|
|
111
|
+
</View>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const SecondaryImageComponent = (props: Props) => {
|
|
116
|
+
const { uri, displayMode } = props;
|
|
117
|
+
|
|
118
|
+
if (!uri) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const isFixed = isDisplayModeFixed(displayMode);
|
|
123
|
+
|
|
124
|
+
return isFixed ? (
|
|
125
|
+
<SecondaryImageFixed {...props} />
|
|
126
|
+
) : (
|
|
127
|
+
<SecondaryImageDynamic {...props} />
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
83
131
|
export const SecondaryImage = withAsyncRenderHOC(SecondaryImageComponent);
|
|
@@ -4,9 +4,26 @@ import { render } from "@testing-library/react-native";
|
|
|
4
4
|
import { SecondaryImage } from "../Image";
|
|
5
5
|
|
|
6
6
|
describe("SecondaryImage - Image", () => {
|
|
7
|
-
it("SecondaryImage should not render if no
|
|
7
|
+
it("SecondaryImage should not render if no uri", async () => {
|
|
8
8
|
const wrapper = await render(
|
|
9
|
-
<SecondaryImage
|
|
9
|
+
<SecondaryImage
|
|
10
|
+
displayMode="dynamic"
|
|
11
|
+
uri={undefined}
|
|
12
|
+
style={{ width: 100 }}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
expect(wrapper.toJSON()).toEqual(null);
|
|
17
|
+
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("SecondaryImage should not render if no aspect ratio (dynamic)", async () => {
|
|
21
|
+
const wrapper = await render(
|
|
22
|
+
<SecondaryImage
|
|
23
|
+
displayMode="dynamic"
|
|
24
|
+
uri="someurl"
|
|
25
|
+
style={{ width: 100 }}
|
|
26
|
+
/>
|
|
10
27
|
);
|
|
11
28
|
|
|
12
29
|
expect(wrapper.toJSON()).toEqual(null);
|
|
@@ -16,7 +33,8 @@ describe("SecondaryImage - Image", () => {
|
|
|
16
33
|
it("SecondaryImage should render if known dimensions", async () => {
|
|
17
34
|
const wrapper = await render(
|
|
18
35
|
<SecondaryImage
|
|
19
|
-
uri=""
|
|
36
|
+
uri="someUrl"
|
|
37
|
+
displayMode="dynamic"
|
|
20
38
|
style={{ width: 100, height: 100, borderRadius: 10 }}
|
|
21
39
|
/>
|
|
22
40
|
);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`SecondaryImage - Image SecondaryImage should not render if no aspect ratio 1`] = `null`;
|
|
3
|
+
exports[`SecondaryImage - Image SecondaryImage should not render if no aspect ratio (dynamic) 1`] = `null`;
|
|
4
|
+
|
|
5
|
+
exports[`SecondaryImage - Image SecondaryImage should not render if no uri 1`] = `null`;
|
|
4
6
|
|
|
5
7
|
exports[`SecondaryImage - Image SecondaryImage should render if known dimensions 1`] = `
|
|
6
8
|
<View
|
|
@@ -14,10 +16,11 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
|
|
|
14
16
|
}
|
|
15
17
|
>
|
|
16
18
|
<Image
|
|
19
|
+
displayMode="dynamic"
|
|
17
20
|
onAsyncRender={[Function]}
|
|
18
21
|
source={
|
|
19
22
|
{
|
|
20
|
-
"uri": "",
|
|
23
|
+
"uri": "someUrl",
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
style={
|
|
@@ -28,7 +31,7 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
|
|
|
28
31
|
"width": 100,
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
|
-
uri=""
|
|
34
|
+
uri="someUrl"
|
|
32
35
|
/>
|
|
33
36
|
</View>
|
|
34
37
|
`;
|
|
@@ -11,6 +11,8 @@ import { withScaledLineHeight } from "./utils";
|
|
|
11
11
|
import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
12
12
|
import { MeasurementPortalContext } from "../../../MeasurmentsPortal";
|
|
13
13
|
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
14
|
+
import { CellFocusedStateContext } from "@applicaster/zapp-react-native-ui-components/Contexts/CellFocusedStateContext";
|
|
15
|
+
import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
14
16
|
|
|
15
17
|
type Props = {
|
|
16
18
|
style: any;
|
|
@@ -35,10 +37,9 @@ const _Text = ({
|
|
|
35
37
|
}: Props) => {
|
|
36
38
|
const _label = useTextLabel({ label, entry });
|
|
37
39
|
const isMeasurement = React.useContext(MeasurementPortalContext);
|
|
40
|
+
const cellFocused = React.useContext(CellFocusedStateContext);
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
+
const accessibilityManager = useAccessibilityManager({});
|
|
42
43
|
|
|
43
44
|
// set maximum possible height for the text in case of measurement
|
|
44
45
|
const height =
|
|
@@ -46,6 +47,27 @@ const _Text = ({
|
|
|
46
47
|
? toNumber(otherProps.numberOfLines) * toNumber(style.lineHeight)
|
|
47
48
|
: undefined;
|
|
48
49
|
|
|
50
|
+
const textLabel = dateTransformEnabled
|
|
51
|
+
? dateFormat(dateTransform, label)
|
|
52
|
+
: textTransform(transformText, _label);
|
|
53
|
+
|
|
54
|
+
React.useLayoutEffect(() => {
|
|
55
|
+
// For FocusableCells with action buttons
|
|
56
|
+
if (otherProps.state) {
|
|
57
|
+
if (otherProps.state === "focused" && cellFocused === true) {
|
|
58
|
+
accessibilityManager.addHeading(textLabel);
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
if (cellFocused === true) {
|
|
62
|
+
accessibilityManager.addHeading(textLabel);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}, [cellFocused, otherProps.state, textLabel]);
|
|
66
|
+
|
|
67
|
+
if (isNilOrEmpty(_label)) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
49
71
|
return (
|
|
50
72
|
<Text
|
|
51
73
|
style={[
|
|
@@ -55,9 +77,7 @@ const _Text = ({
|
|
|
55
77
|
allowFontScaling={false}
|
|
56
78
|
{...withoutLabel(otherProps)}
|
|
57
79
|
>
|
|
58
|
-
{
|
|
59
|
-
? dateFormat(dateTransform, label)
|
|
60
|
-
: textTransform(transformText, _label)}
|
|
80
|
+
{textLabel}
|
|
61
81
|
</Text>
|
|
62
82
|
);
|
|
63
83
|
};
|
|
@@ -423,7 +423,10 @@ const PlayerContainerComponent = (props: Props) => {
|
|
|
423
423
|
id: playerContainerId,
|
|
424
424
|
listener: {
|
|
425
425
|
onVideoEnd,
|
|
426
|
-
onError
|
|
426
|
+
onError: (err: Error) => {
|
|
427
|
+
// Adapt Error to the expected shape for onError
|
|
428
|
+
onError({ error: err });
|
|
429
|
+
},
|
|
427
430
|
onLoad,
|
|
428
431
|
onPlayerClose: close,
|
|
429
432
|
onPlayerDetached: close,
|
|
@@ -19,9 +19,9 @@ describe("ScreenRevealManager", () => {
|
|
|
19
19
|
|
|
20
20
|
const manager = new ScreenRevealManager(componentsToRender, mockCallback);
|
|
21
21
|
|
|
22
|
-
expect(manager
|
|
22
|
+
expect(manager.numberOfComponentsWaitToLoadBeforePresent).toBe(3);
|
|
23
23
|
|
|
24
|
-
expect(manager
|
|
24
|
+
expect(manager.renderingState).toEqual([
|
|
25
25
|
COMPONENT_LOADING_STATE.UNKNOWN,
|
|
26
26
|
COMPONENT_LOADING_STATE.UNKNOWN,
|
|
27
27
|
COMPONENT_LOADING_STATE.UNKNOWN,
|
|
@@ -62,7 +62,7 @@ const TabsComponent = ({
|
|
|
62
62
|
);
|
|
63
63
|
|
|
64
64
|
const renderItem = useCallback(
|
|
65
|
-
({ item, index, focused }) => {
|
|
65
|
+
({ item, index, focused }: { item: any; index: any; focused?: any }) => {
|
|
66
66
|
const itemId = generateFocusableId(getId(item));
|
|
67
67
|
|
|
68
68
|
const isSelected = R.equals(index, selectedEntryIndex);
|
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`<TextInputTv /> renders 1`] = `
|
|
4
4
|
<input
|
|
5
|
+
accessibilityProps={
|
|
6
|
+
{
|
|
7
|
+
"accessibilityHint": "Enter text into Search",
|
|
8
|
+
"accessibilityLabel": "Search",
|
|
9
|
+
"accessibilityRole": "searchbox",
|
|
10
|
+
"accessible": true,
|
|
11
|
+
"aria-description": "Enter text into Search",
|
|
12
|
+
"aria-label": "Search",
|
|
13
|
+
"aria-role": "searchbox",
|
|
14
|
+
"role": "searchbox",
|
|
15
|
+
"tabindex": 0,
|
|
16
|
+
}
|
|
17
|
+
}
|
|
5
18
|
testID="TextInput-tv"
|
|
6
19
|
/>
|
|
7
20
|
`;
|
|
@@ -4,6 +4,7 @@ import { Appearance, Platform, StyleSheet, TextInput } from "react-native";
|
|
|
4
4
|
import { isFunction } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
5
5
|
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
6
6
|
import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
|
|
7
|
+
import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
7
8
|
|
|
8
9
|
type Props = Partial<{
|
|
9
10
|
style: any;
|
|
@@ -42,6 +43,8 @@ function TextInputTV(props: Props, ref) {
|
|
|
42
43
|
const [colorScheme, setColorScheme] = useState(getInitialColorScheme());
|
|
43
44
|
const isRTL = useIsRTL();
|
|
44
45
|
|
|
46
|
+
const accessibilityManager = useAccessibilityManager({});
|
|
47
|
+
|
|
45
48
|
const onColorChange = useCallback(
|
|
46
49
|
({ colorScheme: color }) => {
|
|
47
50
|
if (color !== colorScheme) {
|
|
@@ -153,6 +156,13 @@ function TextInputTV(props: Props, ref) {
|
|
|
153
156
|
])
|
|
154
157
|
)(props);
|
|
155
158
|
|
|
159
|
+
const getAccessibilityProps = () => {
|
|
160
|
+
return {
|
|
161
|
+
accessibilityProps:
|
|
162
|
+
accessibilityManager.getInputAccessibilityProps("Search"),
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
|
|
156
166
|
const inputProps = {
|
|
157
167
|
...getProps(),
|
|
158
168
|
...getStyle(),
|
|
@@ -161,6 +171,7 @@ function TextInputTV(props: Props, ref) {
|
|
|
161
171
|
...getSecureTextEntry(),
|
|
162
172
|
...getOnEndEditing(),
|
|
163
173
|
...getOnPress(),
|
|
174
|
+
...getAccessibilityProps(),
|
|
164
175
|
};
|
|
165
176
|
|
|
166
177
|
if (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect } from "react";
|
|
1
|
+
import React, { useEffect, useMemo } from "react";
|
|
2
2
|
import { Animated, Dimensions } from "react-native";
|
|
3
3
|
|
|
4
4
|
import {
|
|
@@ -163,31 +163,51 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
|
163
163
|
|
|
164
164
|
return (
|
|
165
165
|
<ReactContext.Provider
|
|
166
|
-
value={
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
166
|
+
value={useMemo(
|
|
167
|
+
() => ({
|
|
168
|
+
yTranslate,
|
|
169
|
+
startComponentsAnimation,
|
|
170
|
+
setStartComponentsAnimation,
|
|
171
|
+
isActiveGesture: playerAnimationState !== null,
|
|
172
|
+
playerAnimationState,
|
|
173
|
+
setPlayerAnimationState,
|
|
174
|
+
resetPlayerAnimationState,
|
|
175
|
+
minimisedHeight,
|
|
176
|
+
animatedValues: {
|
|
177
|
+
lastScrollY,
|
|
178
|
+
dragScrollY,
|
|
179
|
+
dragVideoPlayerY,
|
|
180
|
+
translateYOffset,
|
|
181
|
+
},
|
|
182
|
+
lastScrollYValue,
|
|
183
|
+
scrollPosition,
|
|
184
|
+
modalSnapPoints,
|
|
185
|
+
lastSnap,
|
|
186
|
+
setLastSnap,
|
|
187
|
+
tabletLandscapePlayerTopPosition,
|
|
188
|
+
setTabletLandscapePlayerTopPosition,
|
|
189
|
+
startComponentsAnimationDistance,
|
|
190
|
+
progressBarHeight,
|
|
191
|
+
}),
|
|
192
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
193
|
+
[
|
|
194
|
+
startComponentsAnimation,
|
|
195
|
+
playerAnimationState,
|
|
196
|
+
minimisedHeight,
|
|
176
197
|
lastScrollY,
|
|
177
198
|
dragScrollY,
|
|
178
199
|
dragVideoPlayerY,
|
|
179
200
|
translateYOffset,
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}}
|
|
201
|
+
lastSnap,
|
|
202
|
+
modalSnapPoints,
|
|
203
|
+
lastScrollYValue,
|
|
204
|
+
scrollPosition,
|
|
205
|
+
tabletLandscapePlayerTopPosition,
|
|
206
|
+
startComponentsAnimationDistance,
|
|
207
|
+
progressBarHeight,
|
|
208
|
+
isLiveItem,
|
|
209
|
+
]
|
|
210
|
+
)}
|
|
191
211
|
>
|
|
192
212
|
{children}
|
|
193
213
|
</ReactContext.Provider>
|
|
@@ -2,6 +2,8 @@ import { renderHook } from "@testing-library/react-hooks";
|
|
|
2
2
|
import { useDelayedPlayerDetails } from "../useDelayedPlayerDetails";
|
|
3
3
|
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
4
4
|
|
|
5
|
+
jest.useFakeTimers();
|
|
6
|
+
|
|
5
7
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
6
8
|
useIsTablet: jest.fn().mockReturnValue(false),
|
|
7
9
|
}));
|
|
@@ -11,11 +13,15 @@ describe("useDelayedPlayerDetails", () => {
|
|
|
11
13
|
jest.clearAllMocks();
|
|
12
14
|
});
|
|
13
15
|
|
|
14
|
-
it("should return
|
|
16
|
+
it("should return false initially, that changes after 1 second", () => {
|
|
15
17
|
const { result } = renderHook(() =>
|
|
16
18
|
useDelayedPlayerDetails({ isInline: true, isDocked: false, isPip: false })
|
|
17
19
|
);
|
|
18
20
|
|
|
21
|
+
expect(result.current).toBe(false);
|
|
22
|
+
|
|
23
|
+
jest.advanceTimersByTime(1000);
|
|
24
|
+
|
|
19
25
|
expect(result.current).toBe(true);
|
|
20
26
|
});
|
|
21
27
|
|
|
@@ -46,6 +52,8 @@ describe("useDelayedPlayerDetails", () => {
|
|
|
46
52
|
})
|
|
47
53
|
);
|
|
48
54
|
|
|
55
|
+
jest.advanceTimersByTime(1000);
|
|
56
|
+
|
|
49
57
|
expect(result.current).toBe(true);
|
|
50
58
|
});
|
|
51
59
|
});
|
|
@@ -1,22 +1,26 @@
|
|
|
1
|
+
// const showPlayerDetails = (
|
|
2
|
+
// isInline: boolean,
|
|
3
|
+
// isDocked: boolean,
|
|
4
|
+
// isPip: boolean,
|
|
5
|
+
// isTablet: boolean
|
|
6
|
+
// ) => {
|
|
7
|
+
// return showDetails({
|
|
8
|
+
// isMobile: !isTablet,
|
|
9
|
+
// isInline,
|
|
10
|
+
// isDocked,
|
|
11
|
+
// isPip,
|
|
12
|
+
// });
|
|
13
|
+
// };
|
|
14
|
+
|
|
15
|
+
import * as React from "react";
|
|
1
16
|
import { useIsTablet } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
17
|
+
import { withTimeout$ } from "@applicaster/zapp-react-native-utils/idleUtils";
|
|
2
18
|
|
|
3
19
|
import { showDetails } from "./utils";
|
|
4
20
|
|
|
5
|
-
|
|
21
|
+
const TIMEOUT = 1000; // ms
|
|
6
22
|
|
|
7
|
-
|
|
8
|
-
isInline: boolean,
|
|
9
|
-
isDocked: boolean,
|
|
10
|
-
isPip: boolean,
|
|
11
|
-
isTablet: boolean
|
|
12
|
-
) => {
|
|
13
|
-
return showDetails({
|
|
14
|
-
isMobile: !isTablet,
|
|
15
|
-
isInline,
|
|
16
|
-
isDocked,
|
|
17
|
-
isPip,
|
|
18
|
-
});
|
|
19
|
-
};
|
|
23
|
+
type Props = { isInline: boolean; isDocked: boolean; isPip: boolean };
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* Custom hook to determine whether to show player details with a delay.
|
|
@@ -32,7 +36,28 @@ export const useDelayedPlayerDetails = ({
|
|
|
32
36
|
isDocked,
|
|
33
37
|
isPip,
|
|
34
38
|
}: Props): boolean => {
|
|
39
|
+
const [shouldShowDetails, setShouldShowDetails] = React.useState(false);
|
|
40
|
+
|
|
35
41
|
const isTablet = useIsTablet();
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
const subscription = withTimeout$(TIMEOUT).subscribe({
|
|
45
|
+
next: () => {
|
|
46
|
+
setShouldShowDetails(() => {
|
|
47
|
+
return showDetails({
|
|
48
|
+
isMobile: !isTablet,
|
|
49
|
+
isInline,
|
|
50
|
+
isDocked,
|
|
51
|
+
isPip,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return () => {
|
|
58
|
+
subscription.unsubscribe();
|
|
59
|
+
};
|
|
60
|
+
}, [isDocked, isTablet, isInline, isPip]);
|
|
61
|
+
|
|
62
|
+
return shouldShowDetails;
|
|
38
63
|
};
|
|
@@ -12,7 +12,7 @@ describe("showDetails", () => {
|
|
|
12
12
|
expect(result).toBe(false);
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
it("should return
|
|
15
|
+
it("should return true if isDocked & mobile", () => {
|
|
16
16
|
const result = showDetails({
|
|
17
17
|
isMobile: true,
|
|
18
18
|
isInline: true,
|
|
@@ -20,7 +20,7 @@ describe("showDetails", () => {
|
|
|
20
20
|
isPip: false,
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
expect(result).toBe(
|
|
23
|
+
expect(result).toBe(true);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
it("should return true if isMobile is true and isInline is true", () => {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export const CellFocusedStateContext = React.createContext<boolean>(false);
|
|
4
|
+
|
|
5
|
+
export const CellFocusedStateContextProvider = ({
|
|
6
|
+
cellFocused,
|
|
7
|
+
children,
|
|
8
|
+
}: {
|
|
9
|
+
cellFocused: boolean;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
}) => {
|
|
12
|
+
return (
|
|
13
|
+
<CellFocusedStateContext.Provider value={cellFocused}>
|
|
14
|
+
{children}
|
|
15
|
+
</CellFocusedStateContext.Provider>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function withCellFocusedStateContext(
|
|
20
|
+
Component: React.ComponentType<any>
|
|
21
|
+
) {
|
|
22
|
+
return function WithCellFocusedStateContextWrapper(props) {
|
|
23
|
+
const cellFocusedState = React.useContext(CellFocusedStateContext);
|
|
24
|
+
|
|
25
|
+
return <Component {...props} cellFocusedState={cellFocusedState} />;
|
|
26
|
+
};
|
|
27
|
+
}
|
package/events/index.ts
CHANGED
|
@@ -3,4 +3,6 @@ export enum QBUIComponentEvents {
|
|
|
3
3
|
topMenuBarTV_onFocus = "topMenuBarTV_onFocus",
|
|
4
4
|
navigatorReplaceItem = "navigatorReplaceItem",
|
|
5
5
|
scrollVerticallyToInitialOffset = "scrollVerticallyToInitialOffset",
|
|
6
|
+
focusOnSelectedTab = "focusOnSelectedTab",
|
|
7
|
+
focusOnSelectedTopMenuItem = "focusOnSelectedTopMenuItem",
|
|
6
8
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "14.0.0-alpha.
|
|
3
|
+
"version": "14.0.0-alpha.5365702091",
|
|
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": "14.0.0-alpha.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "14.0.0-alpha.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "14.0.0-alpha.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "14.0.0-alpha.
|
|
31
|
+
"@applicaster/applicaster-types": "14.0.0-alpha.5365702091",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "14.0.0-alpha.5365702091",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "14.0.0-alpha.5365702091",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "14.0.0-alpha.5365702091",
|
|
35
35
|
"promise": "^8.3.0",
|
|
36
36
|
"url": "^0.11.0",
|
|
37
37
|
"uuid": "^3.3.2"
|