@applicaster/zapp-react-dom-app 14.0.0-alpha.3011001369 → 14.0.0-alpha.3065652724

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.
@@ -7,6 +7,7 @@ jest.mock("@applicaster/zapp-react-native-utils/appUtils", () => ({
7
7
  focusManager: {
8
8
  isFocusOnMenu: jest.fn(),
9
9
  isFocusOnContent: jest.fn(),
10
+ isTabsScreenContentFocused: jest.fn(),
10
11
  },
11
12
  }));
12
13
 
@@ -44,6 +45,20 @@ describe("withBackToTopActionHOC", () => {
44
45
  jest.clearAllMocks();
45
46
  });
46
47
 
48
+ it("returns PLATFORM_BACK when on root screen, and startup hooks", () => {
49
+ expect.assertions(1);
50
+
51
+ (useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
52
+
53
+ (useNavigation as jest.Mock).mockReturnValue({
54
+ startUpHooks: ["some_startup_hook"],
55
+ });
56
+
57
+ render(<Wrapped />);
58
+
59
+ expect(receivedProps.backToTopAction()).toBe("PLATFORM_BACK");
60
+ });
61
+
47
62
  it("returns PLATFORM_BACK when on root screen, focus on menu, and is home", () => {
48
63
  expect.assertions(1);
49
64
 
@@ -52,7 +67,7 @@ describe("withBackToTopActionHOC", () => {
52
67
  (useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
53
68
 
54
69
  (useNavigation as jest.Mock).mockReturnValue({
55
- getNestedEntry: () => undefined,
70
+ startUpHooks: [],
56
71
  });
57
72
 
58
73
  (focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(true);
@@ -60,9 +75,7 @@ describe("withBackToTopActionHOC", () => {
60
75
 
61
76
  render(<Wrapped />);
62
77
 
63
- expect(receivedProps.backToTopAction()).toEqual({
64
- action: "PLATFORM_BACK",
65
- });
78
+ expect(receivedProps.backToTopAction()).toBe("PLATFORM_BACK");
66
79
  });
67
80
 
68
81
  it("returns GO_HOME when on root screen, focus on menu, and not home", () => {
@@ -73,23 +86,23 @@ describe("withBackToTopActionHOC", () => {
73
86
  (useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
74
87
 
75
88
  (useNavigation as jest.Mock).mockReturnValue({
76
- getNestedEntry: () => undefined,
89
+ startUpHooks: [],
77
90
  });
78
91
 
79
92
  (focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(true);
80
93
  (focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
81
94
 
82
95
  render(<Wrapped />);
83
- expect(receivedProps.backToTopAction()).toEqual({ action: "GO_HOME" });
96
+ expect(receivedProps.backToTopAction()).toBe("GO_HOME");
84
97
  });
85
98
 
86
- it("returns FOCUS_TOP_NAVIGATION when on root screen and focus on content without tabs_screen", () => {
99
+ it("returns FOCUS_ON_SELECTED_TOP_MENU_ITEM when on root screen and focus on content without tabs_screen", () => {
87
100
  (useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
88
101
  (useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
89
102
  (useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
90
103
 
91
104
  (useNavigation as jest.Mock).mockReturnValue({
92
- getNestedEntry: () => "selectedEntry",
105
+ startUpHooks: [],
93
106
  });
94
107
 
95
108
  (focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
@@ -97,36 +110,30 @@ describe("withBackToTopActionHOC", () => {
97
110
 
98
111
  render(<Wrapped />);
99
112
 
100
- expect(receivedProps.backToTopAction()).toEqual({
101
- action: "FOCUS_TOP_NAVIGATION",
102
- payload: {
103
- isTabsScreen: false,
104
- selectedEntry: "selectedEntry",
105
- },
106
- });
113
+ expect(receivedProps.backToTopAction()).toBe(
114
+ "FOCUS_ON_SELECTED_TOP_MENU_ITEM"
115
+ );
107
116
  });
108
117
 
109
- it("returns FOCUS_TOP_NAVIGATION when on root screen and focus on content with tabs_screen", () => {
118
+ it("returns FOCUS_ON_SELECTED_TAB_ITEM when on root screen and focus on content with tabs_screen", () => {
110
119
  (useCurrentScreenIsRoot as jest.Mock).mockReturnValue(true);
111
120
  (useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
112
121
  (useCurrentScreenIsTabs as jest.Mock).mockReturnValue(true);
113
122
 
114
123
  (useNavigation as jest.Mock).mockReturnValue({
115
- getNestedEntry: () => "selectedEntry",
124
+ startUpHooks: [],
116
125
  });
117
126
 
118
127
  (focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
119
128
  (focusManager.isFocusOnContent as jest.Mock).mockReturnValue(true);
120
129
 
130
+ (focusManager.isTabsScreenContentFocused as jest.Mock).mockReturnValue(
131
+ true
132
+ );
133
+
121
134
  render(<Wrapped />);
122
135
 
123
- expect(receivedProps.backToTopAction()).toEqual({
124
- action: "FOCUS_TOP_NAVIGATION",
125
- payload: {
126
- isTabsScreen: true,
127
- selectedEntry: "selectedEntry",
128
- },
129
- });
136
+ expect(receivedProps.backToTopAction()).toBe("FOCUS_ON_SELECTED_TAB_ITEM");
130
137
  });
131
138
 
132
139
  it("returns GO_BACK in default case", () => {
@@ -137,13 +144,40 @@ describe("withBackToTopActionHOC", () => {
137
144
  (useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
138
145
 
139
146
  (useNavigation as jest.Mock).mockReturnValue({
140
- getNestedEntry: () => undefined,
147
+ startUpHooks: [],
148
+ });
149
+
150
+ (focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
151
+ (focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
152
+
153
+ (focusManager.isTabsScreenContentFocused as jest.Mock).mockReturnValue(
154
+ false
155
+ );
156
+
157
+ render(<Wrapped />);
158
+ expect(receivedProps.backToTopAction()).toBe("GO_BACK");
159
+ });
160
+
161
+ it("returns FORCE_GO_BACK in hook case", () => {
162
+ expect.assertions(1);
163
+
164
+ (useCurrentScreenIsRoot as jest.Mock).mockReturnValue(false);
165
+ (useCurrentScreenIsHome as jest.Mock).mockReturnValue(false);
166
+ (useCurrentScreenIsTabs as jest.Mock).mockReturnValue(false);
167
+
168
+ (useNavigation as jest.Mock).mockReturnValue({
169
+ startUpHooks: [],
170
+ currentRoute: "/hooks/some_route",
141
171
  });
142
172
 
143
173
  (focusManager.isFocusOnMenu as jest.Mock).mockReturnValue(false);
144
174
  (focusManager.isFocusOnContent as jest.Mock).mockReturnValue(false);
145
175
 
176
+ (focusManager.isTabsScreenContentFocused as jest.Mock).mockReturnValue(
177
+ false
178
+ );
179
+
146
180
  render(<Wrapped />);
147
- expect(receivedProps.backToTopAction()).toEqual({ action: "GO_BACK" });
181
+ expect(receivedProps.backToTopAction()).toBe("FORCE_GO_BACK");
148
182
  });
149
183
  });
@@ -1,5 +1,10 @@
1
1
  import * as React from "react";
2
+ import { isFilledArray } from "@applicaster/zapp-react-native-utils/arrayUtils";
2
3
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
4
+
5
+ import { useSubscriberFor } from "@applicaster/zapp-react-native-utils/reactHooks/useSubscriberFor";
6
+ import { QBUIComponentEvents } from "@applicaster/zapp-react-native-ui-components/events";
7
+
3
8
  import {
4
9
  useCurrentScreenIsHome,
5
10
  useCurrentScreenIsRoot,
@@ -7,10 +12,13 @@ import {
7
12
  } from "./hooks";
8
13
  import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
9
14
 
10
- export type BACK_TO_TOP_ACTION = {
11
- action: "PLATFORM_BACK" | "GO_HOME" | "FOCUS_TOP_NAVIGATION" | "GO_BACK";
12
- payload?: { isTabsScreen: boolean; selectedEntry: ZappEntry };
13
- };
15
+ export type BACK_TO_TOP_ACTION =
16
+ | "PLATFORM_BACK"
17
+ | "GO_HOME"
18
+ | "GO_BACK"
19
+ | "FOCUS_ON_SELECTED_TOP_MENU_ITEM"
20
+ | "FORCE_GO_BACK"
21
+ | "FOCUS_ON_SELECTED_TAB_ITEM";
14
22
 
15
23
  export type BackToTopAction = () => BACK_TO_TOP_ACTION;
16
24
 
@@ -21,36 +29,55 @@ export function withBackToTopActionHOC(Component) {
21
29
  const isTabsScreen = useCurrentScreenIsTabs();
22
30
 
23
31
  const navigator = useNavigation();
24
- const selectedEntry = navigator.getNestedEntry();
25
32
 
26
- const isStartUpHook =
27
- Array.isArray(navigator.startUpHooks) &&
28
- navigator.startUpHooks.length > 0;
33
+ const isStartUpHook = isFilledArray(navigator.startUpHooks);
34
+
35
+ const isHook = navigator.currentRoute?.includes("hook");
36
+
37
+ const emitFocusOnSelectedTab = useSubscriberFor(
38
+ QBUIComponentEvents.focusOnSelectedTab
39
+ );
40
+
41
+ const emitFocusOnSelectedTopMenuItem = useSubscriberFor(
42
+ QBUIComponentEvents.focusOnSelectedTopMenuItem
43
+ );
29
44
 
30
45
  const backToTopAction = React.useCallback((): BACK_TO_TOP_ACTION => {
31
46
  if (isRoot && isStartUpHook) {
32
- return { action: "PLATFORM_BACK" };
47
+ return "PLATFORM_BACK";
33
48
  }
34
49
 
35
50
  if (isRoot && focusManager.isFocusOnMenu() && isHome) {
36
- return { action: "PLATFORM_BACK" };
51
+ return "PLATFORM_BACK";
37
52
  }
38
53
 
39
54
  if (isRoot && focusManager.isFocusOnMenu() && !isHome) {
40
- return { action: "GO_HOME" };
55
+ return "GO_HOME";
56
+ }
57
+
58
+ if (isRoot && isTabsScreen && focusManager.isTabsScreenContentFocused()) {
59
+ return "FOCUS_ON_SELECTED_TAB_ITEM";
41
60
  }
42
61
 
43
62
  if (isRoot && focusManager.isFocusOnContent()) {
44
- return {
45
- action: "FOCUS_TOP_NAVIGATION",
46
- payload: { isTabsScreen, selectedEntry },
47
- };
63
+ return "FOCUS_ON_SELECTED_TOP_MENU_ITEM";
64
+ }
65
+
66
+ if (isHook) {
67
+ return "FORCE_GO_BACK";
48
68
  }
49
69
 
50
70
  // default
51
- return { action: "GO_BACK" };
52
- }, [isHome, isRoot, isTabsScreen, selectedEntry]);
71
+ return "GO_BACK";
72
+ }, [isHome, isRoot, isTabsScreen, isStartUpHook, isHook]);
53
73
 
54
- return <Component {...props} backToTopAction={backToTopAction} />;
74
+ return (
75
+ <Component
76
+ {...props}
77
+ backToTopAction={backToTopAction}
78
+ emitFocusOnSelectedTab={emitFocusOnSelectedTab}
79
+ emitFocusOnSelectedTopMenuItem={emitFocusOnSelectedTopMenuItem}
80
+ />
81
+ );
55
82
  };
56
83
  }
@@ -16,6 +16,7 @@ import {
16
16
  } from "@applicaster/zapp-react-native-utils/functionUtils";
17
17
 
18
18
  import {
19
+ AccessibilityManager,
19
20
  ARROW_KEYS,
20
21
  focusManager,
21
22
  keyCode,
@@ -58,7 +59,7 @@ import { withBackToTopActionHOC, BackToTopAction } from "./hoc";
58
59
  const { withXray } = getXray();
59
60
  const globalAny: any = global;
60
61
 
61
- const { log_debug, log_warning } = createLogger({
62
+ const { log_debug, log_warning, log_info } = createLogger({
62
63
  category: "InteractionManager",
63
64
  subsystem: "zapp-react-dom-app",
64
65
  });
@@ -76,6 +77,8 @@ type Props = {
76
77
  xray: XRayContext;
77
78
  confirmDialog: typeof confirmationDialogStore;
78
79
  backToTopAction: BackToTopAction;
80
+ emitFocusOnSelectedTopMenuItem: Callback;
81
+ emitFocusOnSelectedTab: Callback;
79
82
  };
80
83
 
81
84
  interface State {
@@ -86,8 +89,8 @@ interface State {
86
89
 
87
90
  type KeyCode = { code: string; keyCode: number | string };
88
91
 
89
- export const alertConfirmationMesage =
90
- "Are you sure you want to exit application?";
92
+ const VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE =
93
+ "Video player controls - use left/right to navigate";
91
94
 
92
95
  class InteractionManagerClass extends React.Component<Props, State> {
93
96
  onKeyDownListener: (keyCode: KeyCode) => void;
@@ -104,6 +107,8 @@ class InteractionManagerClass extends React.Component<Props, State> {
104
107
  private confirmDialog: ConfirmationDialogState =
105
108
  confirmationDialogStore.getState();
106
109
 
110
+ accessibilityManager: AccessibilityManager;
111
+
107
112
  state: State = {
108
113
  isKeyboardVisible: false,
109
114
  keyboardInput: "",
@@ -155,6 +160,7 @@ class InteractionManagerClass extends React.Component<Props, State> {
155
160
  this.confirmDialog.setConfirmAction(this.performExit);
156
161
  this.confirmDialog.setCancelAction(this.onConfirmDialogClose);
157
162
  this.longPressDetector = new KeyboardLongPressManager();
163
+ this.accessibilityManager = AccessibilityManager.getInstance();
158
164
  }
159
165
 
160
166
  componentDidMount() {
@@ -260,7 +266,6 @@ class InteractionManagerClass extends React.Component<Props, State> {
260
266
  */
261
267
  showConfirmationDialog({
262
268
  title: "",
263
- message: alertConfirmationMesage,
264
269
  confirmCompletion: this.performExit,
265
270
  });
266
271
  }
@@ -283,7 +288,6 @@ class InteractionManagerClass extends React.Component<Props, State> {
283
288
  } else {
284
289
  showConfirmationDialog({
285
290
  title: "",
286
- message: alertConfirmationMesage,
287
291
  confirmCompletion: this.performExit,
288
292
  cancelCompletion: this.onConfirmDialogClose,
289
293
  });
@@ -390,8 +394,14 @@ class InteractionManagerClass extends React.Component<Props, State> {
390
394
  note: backspace affects the keyboard input, so it behaves differently
391
395
  */
392
396
  onBackPress(event) {
393
- const { displayState, setDisplayState, navigator, backToTopAction } =
394
- this.props;
397
+ const {
398
+ displayState,
399
+ setDisplayState,
400
+ navigator,
401
+ backToTopAction,
402
+ emitFocusOnSelectedTopMenuItem,
403
+ emitFocusOnSelectedTab,
404
+ } = this.props;
395
405
 
396
406
  const { isDialogVisible } = confirmationDialogStore.getState();
397
407
  const { isKeyboardVisible } = this.state;
@@ -411,31 +421,55 @@ class InteractionManagerClass extends React.Component<Props, State> {
411
421
  return;
412
422
  }
413
423
 
414
- const { action, payload } = backToTopAction();
424
+ const action = backToTopAction();
415
425
 
416
426
  switch (displayState) {
417
427
  case DISPLAY_STATES.DEFAULT:
418
428
  if (action === "PLATFORM_BACK") {
429
+ log_info(action);
430
+
419
431
  this.platformBack();
420
432
  }
421
433
 
422
434
  if (action === "GO_HOME") {
435
+ log_info(action);
436
+
423
437
  this.goToHome();
424
438
  }
425
439
 
426
- if (action === "FOCUS_TOP_NAVIGATION") {
427
- focusManager.focusTopNavigation(
428
- payload.isTabsScreen,
429
- payload.selectedEntry
430
- );
440
+ if (action === "FOCUS_ON_SELECTED_TOP_MENU_ITEM") {
441
+ log_info(action);
442
+
443
+ // we don't have enough context at this point to set focus properly on selected top-menu-item, just emit event about it.
444
+ // TopMenu component is supposed to handle this event.
445
+ emitFocusOnSelectedTopMenuItem();
446
+ }
447
+
448
+ if (action === "FOCUS_ON_SELECTED_TAB_ITEM") {
449
+ log_info(action);
450
+
451
+ // we don't have enough context at this point to set focus properly on selected tab-menu-item, just emit event about it
452
+ // Tabs component is supposed to handle this event.
453
+ emitFocusOnSelectedTab();
431
454
  }
432
455
 
433
456
  if (action === "GO_BACK") {
457
+ log_info(action);
458
+
434
459
  if (navigator.canGoBack()) {
435
460
  navigator.goBack();
436
461
  }
437
462
  }
438
463
 
464
+ if (action === "FORCE_GO_BACK") {
465
+ log_info(action);
466
+
467
+ const fallbackToHome = false;
468
+ const fromHook = true;
469
+
470
+ navigator.goBack(fallbackToHome, fromHook);
471
+ }
472
+
439
473
  break;
440
474
  case DISPLAY_STATES.PLAYER:
441
475
  if (isDialogVisible) {
@@ -576,6 +610,13 @@ class InteractionManagerClass extends React.Component<Props, State> {
576
610
  ) {
577
611
  if (displayState === DISPLAY_STATES.PLAYER) {
578
612
  setDisplayState(DISPLAY_STATES.HUD);
613
+
614
+ this.accessibilityManager.addHeading(
615
+ VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE
616
+ );
617
+
618
+ focusManager.recoverFocus();
619
+ this.resetHudTimeout();
579
620
  } else {
580
621
  focusManager.pressIn();
581
622
  focusManager.press();
@@ -620,6 +661,12 @@ class InteractionManagerClass extends React.Component<Props, State> {
620
661
  this.resetHudTimeout();
621
662
  }
622
663
 
664
+ if (displayState === DISPLAY_STATES.PLAYER) {
665
+ this.accessibilityManager.addHeading(
666
+ VIDEO_PLAYER_CONTROLS_NAVIGATION_MESSAGE
667
+ );
668
+ }
669
+
623
670
  return true;
624
671
  }
625
672
  }
@@ -1,4 +1,5 @@
1
1
  import * as React from "react";
2
+ import { createPortal } from "react-dom";
2
3
 
3
4
  import { Background } from "@applicaster/zapp-react-dom-ui-components/Components/Background";
4
5
  import { ConfirmDialog } from "@applicaster/zapp-react-dom-ui-components/Components/ConfirmDialog";
@@ -29,7 +30,7 @@ export function Layout({ children }: Props) {
29
30
  >
30
31
  {children}
31
32
  </LayoutComponent>
32
- <ConfirmDialog />
33
+ {createPortal(<ConfirmDialog />, document.body)}
33
34
  </>
34
35
  );
35
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-dom-app",
3
- "version": "14.0.0-alpha.3011001369",
3
+ "version": "14.0.0-alpha.3065652724",
4
4
  "description": "Zapp App Component for Applicaster's Quick Brick React Native App",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "homepage": "https://github.com/applicaster/zapp-react-dom-app#readme",
24
24
  "dependencies": {
25
- "@applicaster/zapp-react-dom-ui-components": "14.0.0-alpha.3011001369",
26
- "@applicaster/zapp-react-native-bridge": "14.0.0-alpha.3011001369",
27
- "@applicaster/zapp-react-native-redux": "14.0.0-alpha.3011001369",
28
- "@applicaster/zapp-react-native-ui-components": "14.0.0-alpha.3011001369",
29
- "@applicaster/zapp-react-native-utils": "14.0.0-alpha.3011001369",
25
+ "@applicaster/zapp-react-dom-ui-components": "14.0.0-alpha.3065652724",
26
+ "@applicaster/zapp-react-native-bridge": "14.0.0-alpha.3065652724",
27
+ "@applicaster/zapp-react-native-redux": "14.0.0-alpha.3065652724",
28
+ "@applicaster/zapp-react-native-ui-components": "14.0.0-alpha.3065652724",
29
+ "@applicaster/zapp-react-native-utils": "14.0.0-alpha.3065652724",
30
30
  "abortcontroller-polyfill": "^1.7.5",
31
31
  "typeface-montserrat": "^0.0.54",
32
32
  "video.js": "7.14.3",