@24i/bigscreen-sdk 1.0.0 → 1.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@24i/bigscreen-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "SmartApps BIGscreen SDK monorepo",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -38,8 +38,8 @@
38
38
  "homepage": "https://github.com/24i/smartapps-bigscreen-sdk#readme",
39
39
  "dependencies": {
40
40
  "@24i/player-base": "6.10.0",
41
- "@24i/smartapps-datalayer": "2.3.0-beta.1",
42
- "@sentry/browser": "^7.6.0",
41
+ "@24i/smartapps-datalayer": "2.3.2",
42
+ "@sentry/browser": "^7.8.0",
43
43
  "@types/prop-types": "^15.7.5",
44
44
  "@types/scheduler": "^0.16.1",
45
45
  "csstype": "^3.1.0",
@@ -50,13 +50,13 @@
50
50
  "@24i/bigscreen-sdk": "file:.",
51
51
  "@24i/eslint-config-smartapps-bigscreen-linters": "file:./libs/linters",
52
52
  "@24i/smartapps-bigscreen-changelog-generator": "0.17.0",
53
- "@babel/core": "^7.18.6",
54
- "@babel/preset-env": "^7.18.6",
53
+ "@babel/core": "^7.18.9",
54
+ "@babel/preset-env": "^7.18.9",
55
55
  "@types/fs-extra": "^9.0.13",
56
56
  "@types/inquirer": "^8.2.1",
57
- "@types/jest": "^28.1.5",
57
+ "@types/jest": "^28.1.6",
58
58
  "@types/minimist": "^1.2.2",
59
- "@types/node": "^17.0.43",
59
+ "@types/node": "^18.6.3",
60
60
  "@types/react": "^18.0.15",
61
61
  "babel-jest": "^28.1.2",
62
62
  "chalk": "^4.1.2",
@@ -65,15 +65,15 @@
65
65
  "fs-extra": "^10.1.0",
66
66
  "full-icu": "^1.4.0",
67
67
  "identity-obj-proxy": "^3.0.0",
68
- "inquirer": "^9.0.0",
68
+ "inquirer": "^9.0.2",
69
69
  "jest": "^28.1.2",
70
70
  "jest-canvas-mock": "^2.4.0",
71
71
  "jest-environment-jsdom": "^28.1.2",
72
72
  "minimist": "^1.2.6",
73
73
  "patch-package": "^6.2.1",
74
- "sass": "^1.53.0",
74
+ "sass": "^1.54.0",
75
75
  "sass-true": "^6.1.0",
76
- "ts-jest": "^28.0.4",
76
+ "ts-jest": "^28.0.7",
77
77
  "ts-node": "^10.8.2",
78
78
  "tsconfig-paths-jest": "0.0.1",
79
79
  "typescript": "^4.7.2"
@@ -111,6 +111,7 @@
111
111
  "./driver-kreatv": "./packages/driver-kreatv/src/index.ts",
112
112
  "./driver-saphi": "./packages/driver-saphi/src/index.ts",
113
113
  "./driver-tizen": "./packages/driver-tizen/src/index.ts",
114
+ "./driver-vidaa": "./packages/driver-vidaa/src/index.ts",
114
115
  "./driver-webos/scripts/template": "./packages/driver-webos/scripts/template.ts",
115
116
  "./driver-webos/scripts/exportWebOSTVjs": "./packages/driver-webos/scripts/exportWebOSTVjs.ts",
116
117
  "./driver-webos": "./packages/driver-webos/src/index.ts",
@@ -111,7 +111,7 @@
111
111
  }
112
112
 
113
113
  .leading-ellipses-list-item {
114
- height: fit-content;
114
+ height: auto;
115
115
  }
116
116
  }
117
117
  }
@@ -0,0 +1 @@
1
+ export { DeviceVidaa as Device } from '@24i/bigscreen-sdk/driver-vidaa';
@@ -5,6 +5,7 @@ import { DeviceHbbTV } from '@24i/bigscreen-sdk/driver-hbbtv';
5
5
  import { DeviceKreaTV } from '@24i/bigscreen-sdk/driver-kreatv';
6
6
  import { DeviceSaphi } from '@24i/bigscreen-sdk/driver-saphi';
7
7
  import { DeviceTizen } from '@24i/bigscreen-sdk/driver-tizen';
8
+ import { DeviceVidaa } from '@24i/bigscreen-sdk/driver-vidaa';
8
9
  import { DeviceWebos } from '@24i/bigscreen-sdk/driver-webos';
9
10
  import { DeviceXbox } from '@24i/bigscreen-sdk/driver-xbox';
10
11
 
@@ -30,6 +31,10 @@ const getDevice = () => {
30
31
  info('Saphi');
31
32
  return new DeviceSaphi();
32
33
  }
34
+ if (userAgent.match(/Hisense/)) {
35
+ info('VIDAA');
36
+ return new DeviceVidaa();
37
+ }
33
38
  if (userAgent.match(/Ekioh/)) {
34
39
  info('KreaTV');
35
40
  return new DeviceKreaTV();
@@ -0,0 +1,134 @@
1
+ import {
2
+ DeviceBase,
3
+ ConnectionType,
4
+ ScreenSaverStatus,
5
+ VolumeRange,
6
+ } from '@24i/bigscreen-sdk/driver-base';
7
+ import { getPlatformInfo } from './formatUserAgent';
8
+ import { HisenseVidaaApi } from './types';
9
+ import { getKeyMap } from './keymap';
10
+
11
+ declare global {
12
+ interface Window extends HisenseVidaaApi {}
13
+ }
14
+
15
+ export class DeviceVidaa extends DeviceBase {
16
+ keyMap = getKeyMap();
17
+
18
+ platformInfo = getPlatformInfo(navigator.userAgent);
19
+
20
+ async initDevice(): Promise<void> {
21
+ this.initNetworkChangeListener();
22
+ }
23
+
24
+ initNetworkChangeListener() {
25
+ window.addEventListener('online', () => {
26
+ this.triggerEvent('networkchange', { isConnected: true });
27
+ });
28
+ window.addEventListener('offline', () => {
29
+ this.triggerEvent('networkchange', { isConnected: false });
30
+ });
31
+ }
32
+
33
+ reboot(): void {
34
+ let rootPath = '';
35
+ const splitUrl = window.location.href.split('#');
36
+ if (splitUrl.length > 1) rootPath = splitUrl[0];
37
+ window.location.href = rootPath;
38
+ }
39
+
40
+ exit(): void {
41
+ window.close();
42
+ }
43
+
44
+ async getManufacturerName(): Promise<string> {
45
+ return 'Hisense';
46
+ }
47
+
48
+ async getPlatformName(): Promise<string> {
49
+ return 'VIDAA';
50
+ }
51
+
52
+ async getPlatformVersion(): Promise<string> {
53
+ // platform version of Hisense VIDAA is defined on TV 2020 or later
54
+ return this.platformInfo.platformVersion || '';
55
+ }
56
+
57
+ async getDeviceName(): Promise<string> {
58
+ return `Hisense VIDAA ${await this.getModelName()}`.trim();
59
+ }
60
+
61
+ async getModelName(): Promise<string> {
62
+ return window.Hisense_GetModelName() || '';
63
+ }
64
+
65
+ async getFirmware(): Promise<string> {
66
+ return window.Hisense_GetFirmWareVersion() || '';
67
+ }
68
+
69
+ async is4KSupported(): Promise<boolean> {
70
+ return window.Hisense_Get4KSupportState() || false;
71
+ }
72
+
73
+ async is8KSupported(): Promise<boolean> {
74
+ return false;
75
+ }
76
+
77
+ async getUuid(): Promise<string> {
78
+ return window.Hisense_GetDeviceID() || '';
79
+ }
80
+
81
+ async getVolume(): Promise<number> {
82
+ return VolumeRange.UNSUPPORTED;
83
+ }
84
+
85
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
86
+ async setVolume(volumePercentage: number): Promise<void> {
87
+ // not supported on Hisense VIDAA platform
88
+ }
89
+
90
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
91
+ async mute(mute: boolean): Promise<void> {
92
+ // not supported on Hisense VIDAA platform
93
+ }
94
+
95
+ async isMute(): Promise<boolean> {
96
+ // not supported on Hisense VIDAA platform
97
+ return false;
98
+ }
99
+
100
+ async getScreenSaver(): Promise<ScreenSaverStatus> {
101
+ // not supported on Hisense VIDAA platform
102
+ return { enabled: false };
103
+ }
104
+
105
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
106
+ async setScreenSaver(status: ScreenSaverStatus): Promise<void> {
107
+ // not supported on Hisense VIDAA platform
108
+ }
109
+
110
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
111
+ async setUserAgent(userAgent: string): Promise<boolean> {
112
+ // not supported on Hisense VIDAA platform
113
+ return false;
114
+ }
115
+
116
+ async isNetworkConnected(): Promise<boolean> {
117
+ return window.navigator.onLine;
118
+ }
119
+
120
+ async getConnectionType(): Promise<ConnectionType> {
121
+ // not supported on Hisense VIDAA platform
122
+ return 'unknown';
123
+ }
124
+
125
+ async getLocalIP(): Promise<string> {
126
+ // not supported on Hisense VIDAA platform
127
+ return '';
128
+ }
129
+
130
+ async getMacAddress(): Promise<string> {
131
+ // not supported on Hisense VIDAA platform
132
+ return '';
133
+ }
134
+ }
@@ -0,0 +1,20 @@
1
+ import { HisenseVidaaApi, HisenseVidaaKeys } from '../types';
2
+
3
+ export const mockHisenseVidaaApi: HisenseVidaaApi = {
4
+ Hisense_GetBrand: () => 'hisense',
5
+ Hisense_GetModelName: () => '5U61FQEVS',
6
+ Hisense_GetFirmWareVersion: () => 'V0000.01.00Q.L0301',
7
+ Hisense_Get4KSupportState: () => true,
8
+ Hisense_GetDeviceID: () => '861003009000001000000641123456789012',
9
+ };
10
+
11
+ // Key constants are set to random number values
12
+ export const mockHisenseVidaaKeys = {
13
+ VK_LEFT: 0,
14
+ VK_RIGHT: 1,
15
+ VK_UP: 2,
16
+ VK_DOWN: 3,
17
+ VK_ENTER: 4,
18
+ VK_BACK_SPACE: 5,
19
+ // the rest of key constants is not needed for testing
20
+ } as HisenseVidaaKeys;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Parses platform info of VIDAA from user agent string.
3
+ * NOTE: Platform info of VIDAA is available in user agent of TVs 2020 or later.
4
+ *
5
+ * @param userAgent User agent string
6
+ * @returns object with platform info like platform name and platform version.
7
+ * If values are not not available, values of keys are set to undefined.
8
+ * Ex. for VIDAA platform string like "VIDAA/4.0" in user agent string , it returns
9
+ * object { platformName: "VIDAA", platformVersion: "4.0" }
10
+ */
11
+ export const getPlatformInfo = (userAgent: string) => {
12
+ const [
13
+ , platformName, platformVersion,
14
+ ] = userAgent.match(/(VIDAA)\/([\d.]+)/) || [] as Array<undefined>;
15
+ return { platformName, platformVersion };
16
+ };
@@ -0,0 +1 @@
1
+ export { DeviceVidaa } from './DeviceVidaa';
@@ -0,0 +1,54 @@
1
+ /* eslint-disable no-magic-numbers */
2
+ import { KeyMap } from '@24i/bigscreen-sdk/driver-base';
3
+ import { HisenseVidaaKeys } from './types';
4
+
5
+ declare const window: Window & HisenseVidaaKeys;
6
+
7
+ export const getKeyMap = () => {
8
+ KeyMap.overrideValues({
9
+ LEFT: { keyCode: window.VK_LEFT },
10
+ RIGHT: { keyCode: window.VK_RIGHT },
11
+ UP: { keyCode: window.VK_UP },
12
+ DOWN: { keyCode: window.VK_DOWN },
13
+ ENTER: { keyCode: window.VK_ENTER },
14
+ BACK: { keyCode: window.VK_BACK_SPACE },
15
+
16
+ RED: { keyCode: window.VK_RED },
17
+ GREEN: { keyCode: window.VK_GREEN },
18
+ YELLOW: { keyCode: window.VK_YELLOW },
19
+ BLUE: { keyCode: window.VK_BLUE },
20
+
21
+ PLAY: { keyCode: window.VK_PLAY },
22
+ PAUSE: { keyCode: window.VK_PAUSE },
23
+ PLAYPAUSE: { keyCode: window.VK_PLAY_PAUSE },
24
+ STOP: { keyCode: window.VK_STOP },
25
+ FF: { keyCode: window.VK_FAST_FWD },
26
+ REW: { keyCode: window.VK_REWIND },
27
+
28
+ NUM_0: { keyCode: window.VK_0 },
29
+ NUM_1: { keyCode: window.VK_1 },
30
+ NUM_2: { keyCode: window.VK_2 },
31
+ NUM_3: { keyCode: window.VK_3 },
32
+ NUM_4: { keyCode: window.VK_4 },
33
+ NUM_5: { keyCode: window.VK_5 },
34
+ NUM_6: { keyCode: window.VK_6 },
35
+ NUM_7: { keyCode: window.VK_7 },
36
+ NUM_8: { keyCode: window.VK_8 },
37
+ NUM_9: { keyCode: window.VK_9 },
38
+ KP_0: { keyCode: window.VK_NUMPAD0 },
39
+ KP_1: { keyCode: window.VK_NUMPAD1 },
40
+ KP_2: { keyCode: window.VK_NUMPAD2 },
41
+ KP_3: { keyCode: window.VK_NUMPAD3 },
42
+ KP_4: { keyCode: window.VK_NUMPAD4 },
43
+ KP_5: { keyCode: window.VK_NUMPAD5 },
44
+ KP_6: { keyCode: window.VK_NUMPAD6 },
45
+ KP_7: { keyCode: window.VK_NUMPAD7 },
46
+ KP_8: { keyCode: window.VK_NUMPAD8 },
47
+ KP_9: { keyCode: window.VK_NUMPAD9 },
48
+
49
+ CHANNEL_UP: { keyCode: window.VK_CHANNEL_UP || 427 },
50
+ CHANNEL_DOWN: { keyCode: window.VK_CHANNEL_DOWN || 428 },
51
+ INFO: { keyCode: window.VK_INFO },
52
+ });
53
+ return KeyMap.generateKeyCodeKeyMap();
54
+ };
@@ -0,0 +1,82 @@
1
+ export interface HisenseVidaaApi {
2
+ /**
3
+ * This interface will retrieve Hisense Unique ID for the device.
4
+ * @return a 32bytes string that represents the device id.
5
+ * Example: 861003009000001000000641123456789012
6
+ */
7
+ Hisense_GetDeviceID: () => string,
8
+ /**
9
+ * This interface will retrieve the firmware version for the device.
10
+ * @return a string that represents the firmware version.
11
+ * Example: V00.01.00a.F0116
12
+ */
13
+ Hisense_GetFirmWareVersion: () => string,
14
+ /**
15
+ * This will retrieve the 4K support status for the device.
16
+ * @return true if 4K is supported otherwise returns false.
17
+ */
18
+ Hisense_Get4KSupportState: () => boolean,
19
+ /**
20
+ * This will retrieve the TV brand for the device.
21
+ * @return a brand full name string that represents the TV brand.
22
+ * Example: hisense, sharp
23
+ */
24
+ Hisense_GetBrand: () => string,
25
+ /**
26
+ * This will retrieve the TV model name (hardware version) for the device.
27
+ * @return a string that represents the TV model name.
28
+ */
29
+ Hisense_GetModelName: () => string,
30
+ }
31
+
32
+ export type HisenseVidaaKeys = {
33
+ readonly VK_UP: number,
34
+ readonly VK_RIGHT: number,
35
+ readonly VK_DOWN: number,
36
+ readonly VK_LEFT: number,
37
+ readonly VK_ENTER: number,
38
+ readonly VK_BACK_SPACE: number,
39
+
40
+ readonly VK_BLUE: number,
41
+ readonly VK_RED: number,
42
+ readonly VK_GREEN: number,
43
+ readonly VK_YELLOW: number,
44
+
45
+ readonly VK_MENU: number,
46
+
47
+ readonly VK_0: number,
48
+ readonly VK_1: number,
49
+ readonly VK_2: number,
50
+ readonly VK_3: number,
51
+ readonly VK_4: number,
52
+ readonly VK_5: number,
53
+ readonly VK_6: number,
54
+ readonly VK_7: number,
55
+ readonly VK_8: number,
56
+ readonly VK_9: number,
57
+ // keys VK_NUMPAD{X} are not introduced in Hisense VIDAA spec but exist in global scope
58
+ readonly VK_NUMPAD0: number,
59
+ readonly VK_NUMPAD1: number,
60
+ readonly VK_NUMPAD2: number,
61
+ readonly VK_NUMPAD3: number,
62
+ readonly VK_NUMPAD4: number,
63
+ readonly VK_NUMPAD5: number,
64
+ readonly VK_NUMPAD6: number,
65
+ readonly VK_NUMPAD7: number,
66
+ readonly VK_NUMPAD8: number,
67
+ readonly VK_NUMPAD9: number,
68
+
69
+ readonly VK_PLAY: number,
70
+ readonly VK_PAUSE: number,
71
+ readonly VK_PLAY_PAUSE: number,
72
+ readonly VK_STOP: number,
73
+ readonly VK_TRACK_NEXT: number,
74
+ readonly VK_TRACK_PREV: number,
75
+ readonly VK_FAST_FWD: number,
76
+ readonly VK_REWIND: number,
77
+ readonly VK_SUBTITLE: number,
78
+ readonly VK_INFO: number,
79
+ // VK_CHANNEL_{UP/DOWN} not introduced in Hisense VIDAA spec but exists in global scope
80
+ readonly VK_CHANNEL_UP: number,
81
+ readonly VK_CHANNEL_DOWN: number,
82
+ };
@@ -103,7 +103,10 @@ export class BasicList<T extends Item<U>, U>
103
103
 
104
104
  shouldShowMrcuForward = () => {
105
105
  const { visibleItems, data } = this.props;
106
- return this.controller.scrollIndex < data.length - 1 - visibleItems + VISIBLE_ITEMS_OFFSET;
106
+ return (
107
+ this.controller.scrollIndex < data.length - 1 - visibleItems + VISIBLE_ITEMS_OFFSET
108
+ && data.length > visibleItems - 2
109
+ );
107
110
  };
108
111
 
109
112
  shouldShowMrcuBackward = () => this.controller.scrollIndex > 0;
@@ -93,11 +93,12 @@ export class EdgeOffsetList<T extends Item<U>, U>
93
93
  };
94
94
 
95
95
  shouldShowMrcuForward = () => {
96
- const { edgeOffset, visibleItems } = this.props;
96
+ const { data, edgeOffset, visibleItems } = this.props;
97
97
  const { scrollIndex } = this.controller;
98
- const list = this.base.current!;
98
+
99
99
  return (
100
- scrollIndex < list.data.length - visibleItems + VISIBLE_ITEMS_OFFSET - edgeOffset
100
+ scrollIndex < data.length - visibleItems + VISIBLE_ITEMS_OFFSET - edgeOffset
101
+ && data.length > visibleItems - 2
101
102
  );
102
103
  };
103
104
 
@@ -70,9 +70,9 @@ export class FirstOnlyList<T extends Item<U>, U>
70
70
  };
71
71
 
72
72
  shouldShowMrcuForward = () => {
73
- const { data } = this.props;
73
+ const { data, visibleItems } = this.props;
74
74
  const list = this.base.current!;
75
- return list.index < data.length - 1;
75
+ return list.index < data.length - 1 && data.length > visibleItems - 2;
76
76
  };
77
77
 
78
78
  shouldShowMrcuBackward = () => {
@@ -80,9 +80,9 @@ export class FirstOnlyVariedList<T extends Item<U>, U> extends Component<Props<T
80
80
  };
81
81
 
82
82
  shouldShowMrcuForward = () => {
83
- const { data } = this.props;
83
+ const { data, visibleItems } = this.props;
84
84
  const list = this.base.current!;
85
- return list.index < data.length - 1;
85
+ return list.index < data.length - 1 && data.length > visibleItems - 2;
86
86
  };
87
87
 
88
88
  shouldShowMrcuBackward = () => {
@@ -76,9 +76,9 @@ export class FixedToEndList<T extends Item<U>, U>
76
76
  };
77
77
 
78
78
  shouldShowMrcuForward = () => {
79
- const { visibleItems } = this.props;
79
+ const { data, visibleItems } = this.props;
80
80
  const list = this.base.current!;
81
- return list.index <= list.data.length - visibleItems + 1;
81
+ return list.index <= data.length - visibleItems + 1 && data.length > visibleItems - 2;
82
82
  };
83
83
 
84
84
  shouldShowMrcuBackward = () => this.controller.scrollIndex > 0;
@@ -55,7 +55,7 @@ the output would be:
55
55
  ## Logger Manager
56
56
 
57
57
  Logger Manager is used for managing loggers. We are able to add individual loggers to the Logger Manager and perform various operations on them.
58
- All loggers that we want to add to the Logger Manager should inherit from the [LoggerBase](./src/loggers/LoggerBase/README.md) abstract class.
58
+ All loggers that we want to add to the Logger Manager should inherit from the [LoggerBase](./src/loggers/LoggerBase/README) abstract class.
59
59
 
60
60
  ### Methods
61
61
 
@@ -78,6 +78,10 @@ export class ScrollTextBase extends Component<Props> implements IFocusable {
78
78
  if (processed) stopEvent(event);
79
79
  };
80
80
 
81
+ onMouseEnter = () => {
82
+ this.focus({ preventScroll: true });
83
+ };
84
+
81
85
  updateArrowNavigation(enable: boolean) {
82
86
  if (enable) {
83
87
  removeClass(this.arrowNavRef.current!.domRef, DISABLED_CLASS);
@@ -118,6 +122,7 @@ export class ScrollTextBase extends Component<Props> implements IFocusable {
118
122
  className={className}
119
123
  ref={this.domRef}
120
124
  onKeyDown={this.onKeyDown}
125
+ onMouseEnter={this.onMouseEnter}
121
126
  tabIndex={0}
122
127
  >
123
128
  <Scroller
@@ -1,245 +0,0 @@
1
- import { Component, createRef, removeNode } from '@24i/bigscreen-sdk/jsx';
2
- import { shallow, MountedComponent } from '@24i/bigscreen-sdk/jsx/test-utils';
3
- import {
4
- focusCandidate,
5
- setFocusCandidate,
6
- setActiveElementAsFocusCandidate,
7
- setNextFocusCandidate,
8
- setCandidateFallback,
9
- getLastCandidate,
10
- getCandidateStack,
11
- clearCandidateStack,
12
- } from '../candidate';
13
-
14
- class MockComponent extends Component<{}> {
15
- wrapperRef = createRef<HTMLDivElement>();
16
-
17
- focus(options?: FocusOptions) {
18
- this.wrapperRef.current!.focus(options);
19
- }
20
-
21
- render() {
22
- return <div tabIndex={0} ref={this.wrapperRef} />;
23
- }
24
- }
25
-
26
- describe('Focus candidate', () => {
27
- let elementStructure: MountedComponent<HTMLDivElement>;
28
- const refs = {
29
- testElement: createRef<HTMLDivElement>(),
30
- testComponent1: createRef<MockComponent>(),
31
- testComponent2: createRef<MockComponent>(),
32
- };
33
-
34
- beforeEach(() => {
35
- elementStructure = shallow<HTMLDivElement>(
36
- <div tabIndex={0}>
37
- <div
38
- ref={refs.testElement}
39
- tabIndex={0}
40
- className="test-element"
41
- />
42
- <MockComponent ref={refs.testComponent1} />
43
- <MockComponent ref={refs.testComponent2} />
44
- </div>,
45
- );
46
- });
47
-
48
- afterEach(() => {
49
- jest.clearAllMocks();
50
- clearCandidateStack();
51
- elementStructure.unmount();
52
- });
53
-
54
- it('should register a candidate', () => {
55
- refs.testElement.current!.focus();
56
- setActiveElementAsFocusCandidate();
57
- expect(getLastCandidate()).toBe(refs.testElement.current!);
58
- });
59
-
60
- it('should register a manual candidate', () => {
61
- setFocusCandidate(refs.testComponent1.current!);
62
- expect(getLastCandidate()).toBe(refs.testComponent1.current!);
63
- });
64
-
65
- it('should register the next candidate', () => {
66
- setNextFocusCandidate(refs.testComponent1.current!);
67
- expect(getLastCandidate()).toBe(refs.testComponent1.current!);
68
- });
69
-
70
- it('should not register the same candidates twice', () => {
71
- refs.testElement.current!.focus();
72
- setActiveElementAsFocusCandidate();
73
- setActiveElementAsFocusCandidate();
74
- expect(getCandidateStack()).toEqual([
75
- refs.testElement.current!,
76
- ]);
77
- });
78
-
79
- it('should the focus candidate', () => {
80
- elementStructure.ref().focus();
81
- setActiveElementAsFocusCandidate();
82
- refs.testElement.current!.focus();
83
- focusCandidate();
84
- expect(document.activeElement).toBe(elementStructure.ref());
85
- });
86
-
87
- it('should fill and pop the candidate stack properly', () => {
88
- elementStructure.ref().focus();
89
- setActiveElementAsFocusCandidate();
90
- refs.testElement.current!.focus();
91
- setActiveElementAsFocusCandidate();
92
- refs.testComponent1.current!.focus();
93
- setActiveElementAsFocusCandidate();
94
- refs.testComponent2.current!.focus();
95
- setActiveElementAsFocusCandidate();
96
- expect(getCandidateStack()).toEqual([
97
- elementStructure.ref(),
98
- refs.testElement.current!,
99
- refs.testComponent1.current!.wrapperRef.current!,
100
- refs.testComponent2.current!.wrapperRef.current!,
101
- ]);
102
- // the last registered is skipped, because it's the current activeElement
103
- // so the method should be called only 3x
104
- focusCandidate();
105
- focusCandidate();
106
- focusCandidate();
107
- expect(getCandidateStack()).toEqual([]);
108
- });
109
-
110
- it('should focus candidates during stack popping', () => {
111
- elementStructure.ref().focus();
112
- setActiveElementAsFocusCandidate();
113
- refs.testElement.current!.focus();
114
- setActiveElementAsFocusCandidate();
115
- refs.testComponent1.current!.focus();
116
- setActiveElementAsFocusCandidate();
117
- refs.testComponent2.current!.focus();
118
- focusCandidate();
119
- expect(document.activeElement).toBe(refs.testComponent1.current!.wrapperRef.current!);
120
- focusCandidate();
121
- expect(document.activeElement).toBe(refs.testElement.current!);
122
- focusCandidate();
123
- expect(document.activeElement).toBe(elementStructure.ref());
124
- });
125
-
126
- it('should work with the next candidate', () => {
127
- elementStructure.ref().focus();
128
- setActiveElementAsFocusCandidate();
129
- setNextFocusCandidate(refs.testElement.current!);
130
- refs.testElement.current!.focus();
131
- focusCandidate();
132
- expect(document.activeElement).toBe(elementStructure.ref());
133
- });
134
-
135
- it('should work with the candidate as a component', () => {
136
- refs.testComponent1.current!.focus();
137
- setNextFocusCandidate(refs.testComponent1.current!);
138
- refs.testComponent2.current!.focus();
139
- setNextFocusCandidate(refs.testComponent2.current!);
140
- expect(getCandidateStack()).toEqual([
141
- refs.testComponent1.current!,
142
- refs.testComponent2.current!,
143
- ]);
144
- // last candidate shoul'd not be focused
145
- // because last focused candidate can't be currently focused element
146
- focusCandidate();
147
- expect(document.activeElement).toBe(refs.testComponent1.current!.wrapperRef.current!);
148
- });
149
-
150
- it('should work with the candidate as a reference', () => {
151
- refs.testComponent1.current!.focus();
152
- setNextFocusCandidate(refs.testComponent1);
153
- refs.testComponent2.current!.focus();
154
- setNextFocusCandidate(refs.testComponent2);
155
- expect(getCandidateStack()).toEqual([
156
- refs.testComponent1,
157
- refs.testComponent2,
158
- ]);
159
- focusCandidate();
160
- expect(document.activeElement).toBe(refs.testComponent1.current!.wrapperRef.current!);
161
- });
162
-
163
- it('should work with the candidate as multiple types and focus only unique elements', () => {
164
- refs.testElement.current!.focus();
165
- setActiveElementAsFocusCandidate();
166
- refs.testComponent1.current!.focus();
167
- setNextFocusCandidate(refs.testComponent1);
168
- refs.testComponent2.current!.focus();
169
- setNextFocusCandidate(refs.testComponent2.current!);
170
- expect(getCandidateStack()).toEqual([
171
- refs.testElement.current!,
172
- refs.testComponent1,
173
- refs.testComponent2.current!,
174
- ]);
175
- focusCandidate();
176
- expect(document.activeElement).toBe(refs.testComponent1.current!.wrapperRef.current!);
177
- focusCandidate();
178
- expect(document.activeElement).toBe(refs.testElement.current!);
179
- });
180
-
181
- it('should work with the next candidate as a component', () => {
182
- refs.testComponent1.current!.focus();
183
- setFocusCandidate(refs.testComponent1.current!);
184
- refs.testComponent2.current!.focus();
185
- setNextFocusCandidate(refs.testComponent2.current!);
186
- expect(getCandidateStack()).toEqual([
187
- refs.testComponent1.current!,
188
- refs.testComponent2.current!,
189
- ]);
190
- focusCandidate();
191
- expect(document.activeElement).toBe(refs.testComponent1.current!.wrapperRef.current!);
192
- });
193
-
194
- it('should not focus an unavailable candidate', () => {
195
- elementStructure.ref().focus();
196
- setActiveElementAsFocusCandidate();
197
- refs.testElement.current!.focus();
198
- setActiveElementAsFocusCandidate();
199
- refs.testComponent1.current!.focus();
200
- setFocusCandidate(refs.testComponent1.current!);
201
-
202
- refs.testElement.current!.style.display = 'none';
203
- removeNode(refs.testComponent1.current!.wrapperRef);
204
-
205
- focusCandidate();
206
- expect(document.activeElement).toBe(elementStructure.ref());
207
- });
208
-
209
- it('should set and work with a candidate fallback', () => {
210
- setCandidateFallback(elementStructure.ref());
211
-
212
- refs.testElement.current!.focus();
213
- setActiveElementAsFocusCandidate();
214
- refs.testComponent1.current!.focus();
215
- setActiveElementAsFocusCandidate();
216
- refs.testComponent2.current!.focus();
217
- setActiveElementAsFocusCandidate();
218
-
219
- refs.testElement.current!.style.display = 'none';
220
- removeNode(refs.testComponent1.current!.wrapperRef);
221
- removeNode(refs.testComponent2.current!.wrapperRef);
222
-
223
- focusCandidate();
224
- expect(document.activeElement).toBe(elementStructure.ref());
225
- });
226
-
227
- it('should clear the candidate stack', () => {
228
- elementStructure.ref().focus();
229
- setActiveElementAsFocusCandidate();
230
- refs.testElement.current!.focus();
231
- setActiveElementAsFocusCandidate();
232
- refs.testComponent1.current!.focus();
233
- setActiveElementAsFocusCandidate();
234
-
235
- expect(getCandidateStack()).toEqual([
236
- elementStructure.ref(),
237
- refs.testElement.current!,
238
- refs.testComponent2.current!.wrapperRef.current!,
239
- ]);
240
-
241
- clearCandidateStack();
242
-
243
- expect(getCandidateStack()).toEqual([]);
244
- });
245
- });
@@ -1,21 +0,0 @@
1
- import { focusFirstExisting } from '../focusFirstExisting';
2
-
3
- describe('focusFirstExisting', () => {
4
- const mockFocus1 = jest.fn();
5
- const mockFocus2 = jest.fn();
6
-
7
- it('should properly focus references', () => {
8
- focusFirstExisting(
9
- { current: { focus: mockFocus1 } },
10
- { current: { focus: mockFocus2 } },
11
- )();
12
- expect(mockFocus1).toHaveBeenCalledTimes(1);
13
- expect(mockFocus2).toHaveBeenCalledTimes(0);
14
- focusFirstExisting(
15
- { current: null },
16
- { current: { focus: mockFocus2 } },
17
- )();
18
- expect(mockFocus1).toHaveBeenCalledTimes(1);
19
- expect(mockFocus2).toHaveBeenCalledTimes(1);
20
- });
21
- });
@@ -1,41 +0,0 @@
1
- import { createRef } from '@24i/bigscreen-sdk/jsx';
2
- import { shallow } from '@24i/bigscreen-sdk/jsx/test-utils';
3
- import { refocusAfterHashChange } from '../refocusAfterHashChange';
4
-
5
- jest.useFakeTimers();
6
-
7
- describe('refocusAfterHashChange', () => {
8
- const wrapper = createRef<HTMLDivElement>();
9
- const elementInsideWrapper = createRef<HTMLDivElement>();
10
- const elementOutsideWrapper = createRef<HTMLDivElement>();
11
- shallow(
12
- <div>
13
- <div ref={wrapper}>
14
- <div ref={elementInsideWrapper} tabIndex={0} />
15
- </div>
16
- <div ref={elementOutsideWrapper} tabIndex={0} />
17
- </div>,
18
- );
19
-
20
- afterEach(() => {
21
- jest.clearAllMocks();
22
- });
23
-
24
- it('should refocus active element inside wrapper', () => {
25
- elementInsideWrapper.current!.focus();
26
- const activeElement = document.activeElement as HTMLElement;
27
- const spyOnFocus = jest.spyOn(activeElement, 'focus');
28
- refocusAfterHashChange(wrapper);
29
- jest.runAllTimers();
30
- expect(spyOnFocus).toHaveBeenCalledTimes(1);
31
- });
32
-
33
- it('should not refocus active element outside wrapper', () => {
34
- elementOutsideWrapper.current!.focus();
35
- const activeElement = document.activeElement as HTMLElement;
36
- const spyOnFocus = jest.spyOn(activeElement, 'focus');
37
- refocusAfterHashChange(wrapper);
38
- jest.runAllTimers();
39
- expect(spyOnFocus).toHaveBeenCalledTimes(0);
40
- });
41
- });
@@ -1,41 +0,0 @@
1
- import { createRef } from '@24i/bigscreen-sdk/jsx';
2
- import { shallow } from '@24i/bigscreen-sdk/jsx/test-utils';
3
- import { safeFocus } from '../safeFocus';
4
-
5
- describe('safeFocus', () => {
6
- const wrapperElement = createRef<HTMLDivElement>();
7
- const element1 = createRef<HTMLDivElement>();
8
- const element2 = createRef<HTMLDivElement>();
9
- const testSafeFocus = safeFocus(wrapperElement);
10
-
11
- shallow(
12
- <div>
13
- <div ref={wrapperElement} tabIndex={0}>
14
- <div ref={element1} tabIndex={0} />
15
- </div>
16
- <div ref={element2} tabIndex={0} />
17
- </div>,
18
- );
19
-
20
- it('should focus element when activeElement is inside of the wrapperElement', () => {
21
- wrapperElement.current!.focus();
22
- testSafeFocus(element1.current!);
23
- expect(document.activeElement === element1.current!).toBeTruthy();
24
- testSafeFocus(wrapperElement.current!);
25
- expect(document.activeElement === wrapperElement.current!).toBeTruthy();
26
- });
27
-
28
- it('should focus element when activeElement is body', () => {
29
- document.body.focus();
30
- testSafeFocus(element1.current!);
31
- expect(document.activeElement === element1.current!).toBeTruthy();
32
- });
33
-
34
- it('should not focus element when activeElement is outside of the wrapperElement', () => {
35
- element2.current!.focus();
36
- testSafeFocus(element1.current!);
37
- expect(document.activeElement === element1.current!).toBeFalsy();
38
- testSafeFocus(wrapperElement.current!);
39
- expect(document.activeElement === wrapperElement.current!).toBeFalsy();
40
- });
41
- });
@@ -1,61 +0,0 @@
1
- import { createRef } from '@24i/bigscreen-sdk/jsx';
2
- import { getKeyMap } from '@24i/bigscreen-sdk/device';
3
- import { MountedComponent, shallow, keydown } from '@24i/bigscreen-sdk/jsx/test-utils';
4
- import { MouseElementWrapper } from '../MouseElementWrapper';
5
- import { MouseNavigationInterface } from '../MouseNavigationInterface';
6
-
7
- describe('MouseElementWrapper', () => {
8
- const mPointRef = createRef<HTMLDivElement>();
9
- let root: MountedComponent<HTMLDivElement>;
10
- let nav: MountedComponent<MouseElementWrapper>;
11
-
12
- beforeEach(() => {
13
- jest.clearAllMocks();
14
- root = shallow(<div className="mounting-point" ref={mPointRef} />);
15
- nav = shallow(
16
- <>
17
- <MouseElementWrapper
18
- getMountingPoint={() => mPointRef}
19
- >
20
- <div className="test-child" />
21
- </MouseElementWrapper>
22
- </>,
23
- );
24
- });
25
-
26
- afterEach(() => {
27
- nav.unmount();
28
- root.unmount();
29
- });
30
-
31
- it('should not render child immediately', () => {
32
- expect(root.find('.test-child')).toBe(null);
33
- });
34
-
35
- it('should handle key event correctly', () => {
36
-
37
- const event = new MouseEvent('mousemove', {});
38
- document.dispatchEvent(event);
39
-
40
- expect(root.find('.mounting-point').firstElementChild)
41
- .toBe(nav.ref().domRef.current);
42
-
43
- keydown(document, getKeyMap().UP!);
44
- expect(root.find('.mounting-point').firstElementChild).toBe(null);
45
- });
46
-
47
- it('should handle interface request to hide', () => {
48
- const event = new MouseEvent('mousemove', {});
49
- document.dispatchEvent(event);
50
-
51
- expect(root.find('.mounting-point').firstElementChild)
52
- .toBe(nav.ref().domRef.current);
53
-
54
- MouseNavigationInterface.hide();
55
- expect(root.find('.mounting-point').firstElementChild).toBe(null);
56
-
57
- MouseNavigationInterface.show();
58
- expect(root.find('.mounting-point').firstElementChild)
59
- .toBe(nav.ref().domRef.current);
60
- });
61
- });
@@ -1,107 +0,0 @@
1
- import { createRef } from '@24i/bigscreen-sdk/jsx';
2
- import { getKeyMap } from '@24i/bigscreen-sdk/device';
3
- import { MountedComponent, shallow, keydown } from '@24i/bigscreen-sdk/jsx/test-utils';
4
- import { MouseNavigation } from '../MouseNavigation';
5
- import { MouseNavigationInterface } from '../MouseNavigationInterface';
6
-
7
- const forwardMock = jest.fn();
8
- const backwardMock = jest.fn();
9
- const showForwardMock = jest.fn(() => true);
10
- const showBackwardMock = jest.fn(() => true);
11
-
12
- describe('MouseNavigation', () => {
13
- const mPointRef = createRef<HTMLDivElement>();
14
- let nav: MountedComponent<MouseNavigation>;
15
-
16
- beforeEach(() => {
17
- nav = shallow(
18
- <MouseNavigation
19
- getMountingPoint={() => mPointRef}
20
- direction="vertical"
21
- forward={forwardMock}
22
- backward={backwardMock}
23
- shouldShowMrcuForward={showForwardMock}
24
- shouldShowMrcuBackward={showBackwardMock}
25
- >
26
- <>
27
- <div className="test-child" />
28
- <div className="mounting-point" ref={mPointRef} />
29
- </>
30
- </MouseNavigation>,
31
- );
32
- });
33
-
34
- afterEach(() => {
35
- nav.unmount();
36
- });
37
-
38
- it('should handle key event correctly', () => {
39
-
40
- const event = new MouseEvent('mousemove', {});
41
- document.dispatchEvent(event);
42
-
43
- expect(nav.find('.mounting-point').firstElementChild)
44
- .toBe(nav.ref().arrows.current?.domRef.current);
45
-
46
- keydown(document, getKeyMap().UP!);
47
-
48
- expect(nav.find('.mounting-point').firstElementChild).toBe(null);
49
- });
50
-
51
- it('should render child', () => {
52
- expect(nav.find('.test-child').className).not.toBe(null);
53
- });
54
-
55
- it('should return correct mapped direction', () => {
56
- expect(nav.ref().mapDirectionToProps('horizontal').horizontal).toBe(true);
57
- expect(nav.ref().mapDirectionToProps('vertical').vertical).toBe(true);
58
- });
59
-
60
- it('should handle interface request to hide', () => {
61
- const event = new MouseEvent('mousemove', {});
62
- document.dispatchEvent(event);
63
-
64
- expect(nav.find('.mounting-point').firstElementChild)
65
- .toBe(nav.ref().arrows.current?.domRef.current);
66
-
67
- MouseNavigationInterface.hide();
68
-
69
- expect(nav.find('.mounting-point').firstElementChild).toBe(null);
70
-
71
- MouseNavigationInterface.show();
72
-
73
- expect(nav.find('.mounting-point').firstElementChild)
74
- .toBe(nav.ref().arrows.current?.domRef.current);
75
- });
76
-
77
- it('should propagate forward and backward', () => {
78
- nav.ref().handleArrowState = jest.fn();
79
- nav.ref().forward();
80
- nav.ref().backward();
81
-
82
- expect(forwardMock).toHaveBeenCalledTimes(1);
83
- expect(backwardMock).toHaveBeenCalledTimes(1);
84
- expect(nav.ref().handleArrowState).toHaveBeenCalledTimes(2);
85
- });
86
-
87
- it('should show/hide arrows as reported', () => {
88
- nav.ref().arrows = {
89
- current: {
90
- enableArrow: jest.fn(),
91
- disableArrow: jest.fn(),
92
- },
93
- } as any;
94
-
95
- nav.ref().handleArrowState();
96
- expect(nav.ref().arrows.current?.enableArrow).toHaveBeenNthCalledWith(1, 'down');
97
- expect(nav.ref().arrows.current?.enableArrow).toHaveBeenNthCalledWith(2, 'up');
98
- expect(nav.ref().arrows.current?.enableArrow).toHaveBeenCalledTimes(2);
99
-
100
- showForwardMock.mockImplementationOnce(() => false);
101
- showBackwardMock.mockImplementationOnce(() => false);
102
- nav.ref().handleArrowState();
103
- expect(nav.ref().arrows.current?.disableArrow).toHaveBeenNthCalledWith(1, 'down');
104
- expect(nav.ref().arrows.current?.disableArrow).toHaveBeenNthCalledWith(2, 'up');
105
- expect(nav.ref().arrows.current?.disableArrow).toHaveBeenCalledTimes(2);
106
- });
107
- });