@24i/bigscreen-sdk 1.0.25-alpha.2440 → 1.0.26-alpha.2447
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 +1 -1
- package/packages/create-package/dist/createPackage.js +66 -0
- package/packages/create-package/dist/createPackage.js.map +1 -0
- package/packages/create-package/dist/index.js +5 -0
- package/packages/create-package/dist/index.js.map +1 -0
- package/packages/create-package/dist/questionnaire/questions.js +35 -0
- package/packages/create-package/dist/questionnaire/questions.js.map +1 -0
- package/packages/create-package/dist/settings/Settings.js +10 -0
- package/packages/create-package/dist/settings/Settings.js.map +1 -0
- package/packages/create-package/dist/types.js +3 -0
- package/packages/create-package/dist/types.js.map +1 -0
- package/packages/focus/README.md +18 -0
- package/packages/focus/src/IFocusable.ts +1 -0
- package/packages/focus/src/Layout/Base.tsx +12 -2
- package/packages/focus/src/hasFocus.ts +36 -0
- package/packages/focus/src/index.ts +1 -0
- package/packages/sdk-installer/dist/index.js +5 -0
- package/packages/sdk-installer/dist/sdkInstaller.js +38 -0
- package/packages/sdk-installer/dist/services/clean.js +11 -0
- package/packages/sdk-installer/dist/services/download.js +55 -0
- package/packages/sdk-installer/dist/services/index.js +11 -0
- package/packages/sdk-installer/dist/services/install.js +24 -0
- package/packages/sdk-installer/dist/services/unzip.js +12 -0
- package/packages/sdk-installer/dist/types/index.d.ts +1 -0
- package/packages/sdk-installer/dist/types/sdkInstaller.d.ts +7 -0
- package/packages/sdk-installer/dist/types/services/clean.d.ts +2 -0
- package/packages/sdk-installer/dist/types/services/download.d.ts +2 -0
- package/packages/sdk-installer/dist/types/services/index.d.ts +4 -0
- package/packages/sdk-installer/dist/types/services/install.d.ts +2 -0
- package/packages/sdk-installer/dist/types/services/unzip.d.ts +2 -0
- package/packages/utils/README.md +4 -0
- package/packages/utils/src/elementUtils.ts +25 -0
- package/packages/utils/src/index.ts +1 -0
- package/packages/focus/src/Layout/__test__/Base.spec.ts +0 -191
- package/packages/focus/src/Layout/__test__/Horizontal.spec.ts +0 -92
- package/packages/focus/src/Layout/__test__/Matrix.spec.ts +0 -299
- package/packages/focus/src/Layout/__test__/Vertical.spec.ts +0 -44
- package/packages/focus/src/Layout/__test__/isRtl.spec.ts +0 -23
- package/packages/focus/src/Layout/__test__/shared.ts +0 -19
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.main = void 0;
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const fse = require("fs-extra");
|
|
8
|
+
const chalk = require("chalk");
|
|
9
|
+
const minimist = require("minimist");
|
|
10
|
+
const questions_1 = require("./questionnaire/questions");
|
|
11
|
+
const Settings_1 = require("./settings/Settings");
|
|
12
|
+
async function isDirectoryEmpty(dirPath) {
|
|
13
|
+
const files = await fs.promises.readdir(dirPath);
|
|
14
|
+
return !files.length;
|
|
15
|
+
}
|
|
16
|
+
async function copyTemplateFiles(templateDir, targetDir) {
|
|
17
|
+
return fse.copy(templateDir, targetDir);
|
|
18
|
+
}
|
|
19
|
+
function updatePackageJsonFile(filePath, options) {
|
|
20
|
+
let data = fs.readFileSync(filePath, 'utf8');
|
|
21
|
+
const keywords = options.keywords.map((word) => `"${word}"`).join(', ');
|
|
22
|
+
data = data.replace(/("name": ")(.*)(")/, `$1${options.name}$3`);
|
|
23
|
+
data = data.replace(/("version": ")(.*)(")/, `$1${options.version}$3`);
|
|
24
|
+
data = data.replace(/("description": ")(.*)(")/, `$1${options.description}$3`);
|
|
25
|
+
data = data.replace(/("keywords": \[)(.*)(\])/, `$1${keywords}$3`);
|
|
26
|
+
data = data.replace(/("author": ")(.*)(")/, `$1${options.author}$3`);
|
|
27
|
+
fs.writeFileSync(filePath, data);
|
|
28
|
+
}
|
|
29
|
+
function parseArguments(rawArgv) {
|
|
30
|
+
const argv = minimist(rawArgv.slice(2));
|
|
31
|
+
const dirName = argv._[0];
|
|
32
|
+
if (!dirName) {
|
|
33
|
+
console.error('Error: Missing package name');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
dirName,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async function main() {
|
|
41
|
+
const { dirName: destDirName } = parseArguments(process.argv);
|
|
42
|
+
const destDir = path.join(process.cwd(), 'packages/', destDirName);
|
|
43
|
+
if (fs.existsSync(destDir)) {
|
|
44
|
+
console.error(`Error: Target directory "${destDir}" already exists`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
48
|
+
const isDirEmpty = await isDirectoryEmpty(destDir);
|
|
49
|
+
if (!isDirEmpty) {
|
|
50
|
+
console.error(`Error: Target directory "${destDir}" is not empty.`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const settings = Object.assign({}, Settings_1.Settings);
|
|
54
|
+
settings.name = `${Settings_1.Settings.name}${destDirName}`.toLowerCase();
|
|
55
|
+
const details = await (0, questions_1.promptPackageDetails)(settings);
|
|
56
|
+
const templateDir = path.join(__dirname, '..', 'templates/typescript');
|
|
57
|
+
await copyTemplateFiles(templateDir, destDir);
|
|
58
|
+
const packageJsonFile = path.join(destDir, 'package.json');
|
|
59
|
+
updatePackageJsonFile(packageJsonFile, details);
|
|
60
|
+
const docsSymlinkTarget = path.join('../../../packages/', destDirName, '/README.md');
|
|
61
|
+
const docsSymlinkPath = path.join(process.cwd(), 'docs/packages/', destDirName, '/README.md');
|
|
62
|
+
await fse.ensureSymlink(docsSymlinkTarget, docsSymlinkPath);
|
|
63
|
+
console.log(`${chalk.green('Success')}, package has been initialized!`);
|
|
64
|
+
}
|
|
65
|
+
exports.main = main;
|
|
66
|
+
//# sourceMappingURL=createPackage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createPackage.js","sourceRoot":"","sources":["../src/createPackage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,mDAAuC;AAEvC,IAAA,oBAAI,GAAE,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.promptPackageDetails = void 0;
|
|
4
|
+
const inquirer = require("inquirer");
|
|
5
|
+
async function promptPackageDetails(defaultValues) {
|
|
6
|
+
return inquirer.prompt([
|
|
7
|
+
{
|
|
8
|
+
type: 'input',
|
|
9
|
+
name: 'name',
|
|
10
|
+
message: 'Package name:',
|
|
11
|
+
default: defaultValues.name,
|
|
12
|
+
}, {
|
|
13
|
+
type: 'input',
|
|
14
|
+
name: 'version',
|
|
15
|
+
message: 'Version:',
|
|
16
|
+
default: defaultValues.version,
|
|
17
|
+
}, {
|
|
18
|
+
type: 'input',
|
|
19
|
+
name: 'description',
|
|
20
|
+
message: 'Description:',
|
|
21
|
+
}, {
|
|
22
|
+
type: 'input',
|
|
23
|
+
name: 'keywords',
|
|
24
|
+
message: 'Keywords:',
|
|
25
|
+
filter: (ans) => ans.split(/[\s,]+/),
|
|
26
|
+
}, {
|
|
27
|
+
type: 'input',
|
|
28
|
+
name: 'author',
|
|
29
|
+
message: 'Author:',
|
|
30
|
+
default: defaultValues.author,
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
exports.promptPackageDetails = promptPackageDetails;
|
|
35
|
+
//# sourceMappingURL=questions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"questions.js","sourceRoot":"","sources":["../../src/questionnaire/questions.ts"],"names":[],"mappings":";;;AAAA,qCAAqC;AAWrC,KAAK,UAAU,oBAAoB,CAAC,aAA4C;IAC5E,OAAO,QAAQ,CAAC,MAAM,CAAC;QACnB;YACI,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,eAAe;YACxB,OAAO,EAAE,aAAa,CAAC,IAAI;SAC9B,EAAE;YACC,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,UAAU;YACnB,OAAO,EAAE,aAAa,CAAC,OAAO;SACjC,EAAE;YACC,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,cAAc;SAC1B,EAAE;YACC,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,WAAW;YACpB,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;SACvC,EAAE;YACC,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE,aAAa,CAAC,MAAM;SAChC;KACJ,CAAC,CAAC;AACP,CAAC;AAEQ,oDAAoB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Settings = void 0;
|
|
4
|
+
const Settings = {
|
|
5
|
+
name: '@24i/smartapps-bigscreen-',
|
|
6
|
+
version: '0.0.1',
|
|
7
|
+
author: '24i',
|
|
8
|
+
};
|
|
9
|
+
exports.Settings = Settings;
|
|
10
|
+
//# sourceMappingURL=Settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Settings.js","sourceRoot":"","sources":["../../src/settings/Settings.ts"],"names":[],"mappings":";;;AAAA,MAAM,QAAQ,GAAG;IACb,IAAI,EAAE,2BAA2B;IACjC,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,KAAK;CAChB,CAAC;AAEO,4BAAQ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/packages/focus/README.md
CHANGED
|
@@ -14,11 +14,13 @@ IFocusable is an interface that should be implemented by anything that can be fo
|
|
|
14
14
|
interface IFocusable {
|
|
15
15
|
focus(options?: FocusOptions): void,
|
|
16
16
|
hasDom?(element: HTMLElement): boolean,
|
|
17
|
+
hasFocus?(): boolean,
|
|
17
18
|
}
|
|
18
19
|
```
|
|
19
20
|
- focus is the main function that is called when focusing component/element.
|
|
20
21
|
- hasDom is optional optimization for components to help Layout components locate the currently
|
|
21
22
|
focused child.
|
|
23
|
+
- hasFocus is optional check if the component has focus inside or is itself focused.
|
|
22
24
|
|
|
23
25
|
## Layout components
|
|
24
26
|
- Vertical: Use it to create focus between elements vertically (up/down)
|
|
@@ -40,6 +42,7 @@ to the focused element and its index. Ideal place to change other elements
|
|
|
40
42
|
- onBeforeFocusChange: Optional callback just before focus change. It receives a reference
|
|
41
43
|
to the focused element and its index. Ideal place for preparing for the change
|
|
42
44
|
(e.g. moving the container to fit new element in the screen)
|
|
45
|
+
- circular: makes focus circular, e.g. when pressing left at leftmost element focus will go to rightmost element
|
|
43
46
|
- dir: Optional forceful setting of the component direction. Defaults to `'auto'` which follows
|
|
44
47
|
the direction of the app.
|
|
45
48
|
`'ltr'` always forces left-to-right layout,
|
|
@@ -328,3 +331,18 @@ outside of the component and not focused.
|
|
|
328
331
|
```ts
|
|
329
332
|
const refocusAfterHashChange = (focusWrapperOrReference: HTMLElement | Reference<HTMLElement>) => void;
|
|
330
333
|
```
|
|
334
|
+
|
|
335
|
+
## hasFocus
|
|
336
|
+
Test if the element (or element in the reference) has the `activeElement` inside or optionally,
|
|
337
|
+
if the element itself is the `activeElement`.
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
const hasFocus = (elementOrReference: HTMLElement | Reference<HTMLElement>, includeElementItself = false) => boolean;
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## isFocused
|
|
344
|
+
Test if the element (or element in the reference) is the `activeElement`.
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
const isFocused = (elementOrReference: HTMLElement | Reference<HTMLElement>) => boolean;
|
|
348
|
+
```
|
|
@@ -12,6 +12,7 @@ export type Props = SharedProps & {
|
|
|
12
12
|
focusableChildren: Array<Reference>,
|
|
13
13
|
onFocusChanged?: (focused: Reference, index: number) => any,
|
|
14
14
|
onBeforeFocusChange?: (focused: Reference, index: number) => any,
|
|
15
|
+
circular?: boolean,
|
|
15
16
|
preventBlurOnLongPress?: {
|
|
16
17
|
forward?: boolean,
|
|
17
18
|
backward?: boolean,
|
|
@@ -39,6 +40,7 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
|
|
|
39
40
|
onBeforeFocusChange: noop,
|
|
40
41
|
focusOptions: { preventScroll: true },
|
|
41
42
|
dir: 'auto',
|
|
43
|
+
circular: false,
|
|
42
44
|
preventBlurOnLongPress: {},
|
|
43
45
|
} as const;
|
|
44
46
|
|
|
@@ -94,7 +96,7 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
|
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
backward(e: KeyboardEvent) {
|
|
97
|
-
const { preventBlurOnLongPress } = this.props;
|
|
99
|
+
const { focusableChildren, preventBlurOnLongPress, circular } = this.props;
|
|
98
100
|
const { target } = e;
|
|
99
101
|
const fromIndex = this.getFromIndex(target as HTMLElement);
|
|
100
102
|
if (preventBlurOnLongPress.backward) {
|
|
@@ -104,13 +106,17 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
|
|
|
104
106
|
this.setIndex(fromIndex - 1);
|
|
105
107
|
this.focusCurrentIndex();
|
|
106
108
|
stopEvent(e);
|
|
109
|
+
} else if (circular) {
|
|
110
|
+
this.setIndex(focusableChildren.length - 1);
|
|
111
|
+
this.focusCurrentIndex();
|
|
112
|
+
stopEvent(e);
|
|
107
113
|
} else if (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS) {
|
|
108
114
|
stopEvent(e);
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
forward(e: KeyboardEvent) {
|
|
113
|
-
const { focusableChildren, preventBlurOnLongPress } = this.props;
|
|
119
|
+
const { focusableChildren, preventBlurOnLongPress, circular } = this.props;
|
|
114
120
|
const { target } = e;
|
|
115
121
|
const fromIndex = this.getFromIndex(target as HTMLElement);
|
|
116
122
|
if (preventBlurOnLongPress.forward) {
|
|
@@ -120,6 +126,10 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
|
|
|
120
126
|
this.setIndex(fromIndex + 1);
|
|
121
127
|
this.focusCurrentIndex();
|
|
122
128
|
stopEvent(e);
|
|
129
|
+
} else if (circular) {
|
|
130
|
+
this.setIndex(0);
|
|
131
|
+
this.focusCurrentIndex();
|
|
132
|
+
stopEvent(e);
|
|
123
133
|
} else if (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS) {
|
|
124
134
|
stopEvent(e);
|
|
125
135
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Reference, unwrapReference } from '@24i/bigscreen-sdk/jsx';
|
|
2
|
+
import { isElementWrappedBy } from '@24i/bigscreen-sdk/utils/elementUtils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Test if the element (or element in the reference) has the `activeElement` inside or optionally,
|
|
6
|
+
* if the element itself is the `activeElement`.
|
|
7
|
+
* @param elementOrReference DOM element or reference to element on which we test focus
|
|
8
|
+
* @param includeElementItself if true, active element is also tested with the provided element.
|
|
9
|
+
* Default is false
|
|
10
|
+
* @returns true if the element has focused element inside or is itself focused
|
|
11
|
+
*/
|
|
12
|
+
export const hasFocus = (
|
|
13
|
+
elementOrReference: HTMLElement | Reference<HTMLElement>,
|
|
14
|
+
includeElementItself = false,
|
|
15
|
+
) => {
|
|
16
|
+
const element = unwrapReference(elementOrReference);
|
|
17
|
+
if (!element) return false;
|
|
18
|
+
return isElementWrappedBy(
|
|
19
|
+
document.activeElement as HTMLElement,
|
|
20
|
+
element,
|
|
21
|
+
includeElementItself,
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Test if the element (or element in the reference) is the `activeElement`.
|
|
27
|
+
* @param elementOrReference DOM element or reference to element on which we test focus
|
|
28
|
+
* @returns true if the element is focused
|
|
29
|
+
*/
|
|
30
|
+
export const isFocused = (
|
|
31
|
+
elementOrReference: HTMLElement | Reference<HTMLElement>,
|
|
32
|
+
) => {
|
|
33
|
+
const element = unwrapReference(elementOrReference);
|
|
34
|
+
if (!element) return false;
|
|
35
|
+
return document.activeElement === element;
|
|
36
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sdkInstaller = void 0;
|
|
4
|
+
var sdkInstaller_1 = require("./sdkInstaller");
|
|
5
|
+
Object.defineProperty(exports, "sdkInstaller", { enumerable: true, get: function () { return sdkInstaller_1.sdkInstaller; } });
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sdkInstaller = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const url_1 = require("url");
|
|
6
|
+
const services_1 = require("./services");
|
|
7
|
+
const log = (message) => process.stdout.write(message);
|
|
8
|
+
const sdkInstaller = async ({ zipPath, installPath, enableLogs }) => {
|
|
9
|
+
const logger = enableLogs ? log : () => false;
|
|
10
|
+
const pathsToDelete = [];
|
|
11
|
+
let usedInstallPath;
|
|
12
|
+
try {
|
|
13
|
+
const parsedZipPath = (0, path_1.parse)(zipPath);
|
|
14
|
+
const parsedZipUrl = (0, url_1.parse)(zipPath);
|
|
15
|
+
if (!parsedZipUrl.protocol) {
|
|
16
|
+
throw new Error('Installation from local packages is not supported yet.');
|
|
17
|
+
}
|
|
18
|
+
logger('Downloading...');
|
|
19
|
+
const downloadedFilePath = `${process.cwd()}/${parsedZipPath.base}`;
|
|
20
|
+
pathsToDelete.push(downloadedFilePath);
|
|
21
|
+
await (0, services_1.download)(zipPath, downloadedFilePath);
|
|
22
|
+
logger('\rUnpacking...');
|
|
23
|
+
const unzippedDirPath = `${process.cwd()}/${parsedZipPath.name}`;
|
|
24
|
+
pathsToDelete.push(unzippedDirPath);
|
|
25
|
+
(0, services_1.unzip)(downloadedFilePath);
|
|
26
|
+
logger('\rInstalling...');
|
|
27
|
+
usedInstallPath = (0, services_1.install)(unzippedDirPath, installPath);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
(0, services_1.clean)(pathsToDelete);
|
|
31
|
+
throw e;
|
|
32
|
+
}
|
|
33
|
+
logger('\rCleaning...');
|
|
34
|
+
(0, services_1.clean)(pathsToDelete);
|
|
35
|
+
logger(`\rInstalled to ${usedInstallPath}\n`);
|
|
36
|
+
return usedInstallPath;
|
|
37
|
+
};
|
|
38
|
+
exports.sdkInstaller = sdkInstaller;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clean = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const deleteFileOrDirectory = (path) => {
|
|
6
|
+
(0, child_process_1.spawnSync)('rm', ['-r', path]);
|
|
7
|
+
};
|
|
8
|
+
const clean = (pathsToBeDeleted) => {
|
|
9
|
+
pathsToBeDeleted.forEach(deleteFileOrDirectory);
|
|
10
|
+
};
|
|
11
|
+
exports.clean = clean;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.download = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const url = require("url");
|
|
7
|
+
const http = require("http");
|
|
8
|
+
const https = require("https");
|
|
9
|
+
const createDirPathIfDoesntExist = async (dirPath) => {
|
|
10
|
+
if (!fs.existsSync(dirPath)) {
|
|
11
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const getNetworkProtocolAdapter = (requestedUrl) => {
|
|
15
|
+
const { protocol } = url.parse(requestedUrl);
|
|
16
|
+
switch (protocol) {
|
|
17
|
+
case 'http:': return http;
|
|
18
|
+
case 'https:': return https;
|
|
19
|
+
default: throw new Error(`${protocol} protocol is not supported`);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const getResponseError = (response) => {
|
|
23
|
+
const HTTP_RESPONSE_STATUS_CODE_OK = 200;
|
|
24
|
+
const { statusCode, headers } = response;
|
|
25
|
+
const contentType = headers['content-type'];
|
|
26
|
+
if (statusCode !== HTTP_RESPONSE_STATUS_CODE_OK) {
|
|
27
|
+
return new Error(`Request Failed. Status Code: ${statusCode}`);
|
|
28
|
+
}
|
|
29
|
+
if (!contentType) {
|
|
30
|
+
return new Error('Missing Content-Type: expected application/zip');
|
|
31
|
+
}
|
|
32
|
+
if (!/^application\/zip/.test(contentType)) {
|
|
33
|
+
return new Error(`Wrong Content-Type: expected application/zip, received ${contentType}`);
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
const download = async (fromUrl, toFilePath) => {
|
|
38
|
+
const toDirPath = path.parse(toFilePath).dir;
|
|
39
|
+
await createDirPathIfDoesntExist(toDirPath);
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const request = getNetworkProtocolAdapter(fromUrl).get(fromUrl, (response) => {
|
|
42
|
+
const error = getResponseError(response);
|
|
43
|
+
if (error) {
|
|
44
|
+
reject(error);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const file = fs.createWriteStream(toFilePath);
|
|
48
|
+
file.on('error', reject);
|
|
49
|
+
file.on('finish', resolve);
|
|
50
|
+
response.pipe(file);
|
|
51
|
+
});
|
|
52
|
+
request.on('error', reject);
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
exports.download = download;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clean = exports.install = exports.unzip = exports.download = void 0;
|
|
4
|
+
var download_1 = require("./download");
|
|
5
|
+
Object.defineProperty(exports, "download", { enumerable: true, get: function () { return download_1.download; } });
|
|
6
|
+
var unzip_1 = require("./unzip");
|
|
7
|
+
Object.defineProperty(exports, "unzip", { enumerable: true, get: function () { return unzip_1.unzip; } });
|
|
8
|
+
var install_1 = require("./install");
|
|
9
|
+
Object.defineProperty(exports, "install", { enumerable: true, get: function () { return install_1.install; } });
|
|
10
|
+
var clean_1 = require("./clean");
|
|
11
|
+
Object.defineProperty(exports, "clean", { enumerable: true, get: function () { return clean_1.clean; } });
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.install = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const getInstalledPathFromOutput = (output) => {
|
|
6
|
+
var _a;
|
|
7
|
+
const prefix = 'INSTALL_PATH=';
|
|
8
|
+
const installedPathRow = output
|
|
9
|
+
.split('\n')
|
|
10
|
+
.map((outputRow) => outputRow.trim())
|
|
11
|
+
.filter((trimmedOutputRow) => trimmedOutputRow.startsWith(prefix));
|
|
12
|
+
return ((_a = installedPathRow[0]) === null || _a === void 0 ? void 0 : _a.replace(prefix, '')) || '';
|
|
13
|
+
};
|
|
14
|
+
const install = (installerDirPath, installToPath) => {
|
|
15
|
+
const installationScriptPath = `${installerDirPath}/install.js`;
|
|
16
|
+
const installationCommandWithoutCustomPath = `node ${installationScriptPath}`;
|
|
17
|
+
const installationCmdToBeUsed = installToPath
|
|
18
|
+
? `${installationCommandWithoutCustomPath} ${installToPath}`
|
|
19
|
+
: installationCommandWithoutCustomPath;
|
|
20
|
+
const execOptions = { maxBuffer: 5242880 };
|
|
21
|
+
const installationScriptOutput = (0, child_process_1.execSync)(installationCmdToBeUsed, execOptions).toString();
|
|
22
|
+
return getInstalledPathFromOutput(installationScriptOutput);
|
|
23
|
+
};
|
|
24
|
+
exports.install = install;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.unzip = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const unzip = (filePath) => {
|
|
6
|
+
const spawnOptions = { maxBuffer: 5242880 };
|
|
7
|
+
const unzipProcess = (0, child_process_1.spawnSync)('unzip', ['-n', filePath], spawnOptions);
|
|
8
|
+
if (unzipProcess.stderr.length) {
|
|
9
|
+
throw new Error(unzipProcess.stderr.toString());
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
exports.unzip = unzip;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { sdkInstaller } from './sdkInstaller';
|
package/packages/utils/README.md
CHANGED
|
@@ -21,6 +21,7 @@ import { generateUuid } from '@24i/bigscreen-sdk/utils/generateUuid';
|
|
|
21
21
|
- **[addClass](#addClass)** - safely adds class to reference.
|
|
22
22
|
- **[debounce](#debounce)** - debounced function is only executed again after a period of not being called, supports leading, trailing (default) and both executions.
|
|
23
23
|
- **[displayToggler](#displayToggler)** - exports `getDisplayToggler` that allows you to easily show/hide an element with guard preventing unnecessary DOM calls.
|
|
24
|
+
- **[elementUtils](#elementUtils)** - various utils above HTMLElement.
|
|
24
25
|
- **[forceReflow](#forceReflow)** - To force a DOM reflow on given DOM element.
|
|
25
26
|
- **[generateUuid](#generateUuid)** - function to get UUIDv4 according to RFC 4122.
|
|
26
27
|
- **[isMouseUsed](#isMouseUsed)** - **DEPRECATED** moved to `device` package.
|
|
@@ -95,6 +96,9 @@ And returns `IDisplayToggler` interface with 4 functions:
|
|
|
95
96
|
- `isVisible: () => boolean;` - returns true if the element is visible, false otherwise
|
|
96
97
|
- `isHidden: () => boolean;` - returns true if the element is hidden, false otherwise
|
|
97
98
|
|
|
99
|
+
### elementUtils
|
|
100
|
+
Various utils for HTMLElement such as visibility tests or element wrapping tests.
|
|
101
|
+
|
|
98
102
|
### forceReflow
|
|
99
103
|
To force a DOM reflow on given DOM element. It can be needed for transitions
|
|
100
104
|
or other purposes.
|
|
@@ -4,6 +4,12 @@ import { SCREEN_WIDTH } from './sizes';
|
|
|
4
4
|
|
|
5
5
|
const SAFE_TIMEOUT = 100;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Test if the element (or element in the reference) is wrapped visible. This means that the
|
|
9
|
+
* element does not have `display: none` or `visibility: hidden`.
|
|
10
|
+
* @param elementOrReference DOM element or reference to element on which we test wrapping
|
|
11
|
+
* @returns true if element is visible
|
|
12
|
+
*/
|
|
7
13
|
export const isElementVisible = (
|
|
8
14
|
elementOrReference: HTMLElement | Reference<HTMLElement>,
|
|
9
15
|
) => {
|
|
@@ -13,10 +19,18 @@ export const isElementVisible = (
|
|
|
13
19
|
return style.display !== 'none' && style.visibility !== 'hidden';
|
|
14
20
|
};
|
|
15
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Test if the element (or element in the reference) is wrapped fully visible. This means that the
|
|
24
|
+
* element does not have `display: none` or `visibility: hidden` and exists
|
|
25
|
+
* inside visible DOM tree, which exists in the document.body.
|
|
26
|
+
* @param elementOrReference DOM element or reference to element on which we test wrapping
|
|
27
|
+
* @returns true if element is visible
|
|
28
|
+
*/
|
|
16
29
|
export const isElementFullyVisible = (
|
|
17
30
|
elementOrReference: HTMLElement | Reference<HTMLElement>,
|
|
18
31
|
) => {
|
|
19
32
|
const element = unwrapReference(elementOrReference);
|
|
33
|
+
if (!element) return false;
|
|
20
34
|
if (isElementVisible(element)) {
|
|
21
35
|
let parent = element.parentElement;
|
|
22
36
|
while (parent) {
|
|
@@ -33,6 +47,16 @@ export const isElementFullyVisible = (
|
|
|
33
47
|
return false;
|
|
34
48
|
};
|
|
35
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Test if the element (or element in the reference) is wrapped by another element.
|
|
52
|
+
* @param elementOrReference DOM element or reference to element on which we test wrapping
|
|
53
|
+
* @param wrapperElementOrReferenceOrCondition wrapper element or certain condition (utils is
|
|
54
|
+
* running upwards in the DOM tree from first element till some element has some specific condition)
|
|
55
|
+
* @param includesElementItself if true, test will check also provided element itself.
|
|
56
|
+
* This is useful in some cases when we need to include also provided element as wrapper
|
|
57
|
+
* in one run of the util.
|
|
58
|
+
* @returns true if the element is wrapped by provided wrapper
|
|
59
|
+
*/
|
|
36
60
|
export const isElementWrappedBy = (
|
|
37
61
|
elementOrReference: HTMLElement | Reference<HTMLElement>,
|
|
38
62
|
wrapperElementOrReferenceOrCondition:
|
|
@@ -40,6 +64,7 @@ export const isElementWrappedBy = (
|
|
|
40
64
|
includesElementItself = false,
|
|
41
65
|
) => {
|
|
42
66
|
const element = unwrapReference(elementOrReference);
|
|
67
|
+
if (!element) return false;
|
|
43
68
|
let currentParent = includesElementItself ? element : element.parentElement;
|
|
44
69
|
let wrapperElement: HTMLElement | null = null;
|
|
45
70
|
let condition: ((currentWrapper: HTMLElement) => boolean) | null = null;
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { stopEvent } from '@24i/bigscreen-sdk/utils/stopEvent';
|
|
2
|
-
import { IFocusable } from '../../IFocusable';
|
|
3
|
-
import { getLayoutBase } from '../Base';
|
|
4
|
-
import { rootRef, refChildA, refChildB, refChildC, refChildD } from './shared';
|
|
5
|
-
|
|
6
|
-
jest.mock('@24i/bigscreen-sdk/utils/stopEvent');
|
|
7
|
-
|
|
8
|
-
const mockKeydown = jest.fn();
|
|
9
|
-
const mockEvent: any = {
|
|
10
|
-
target: null,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const Base = getLayoutBase(mockKeydown);
|
|
14
|
-
|
|
15
|
-
describe('Focus Layout Base', () => {
|
|
16
|
-
let base: any;
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
base = new Base({
|
|
19
|
-
...Base.defaultProps,
|
|
20
|
-
domRef: rootRef,
|
|
21
|
-
children: 'children',
|
|
22
|
-
focusableChildren: [refChildA, refChildB, refChildC],
|
|
23
|
-
onFocusChanged: jest.fn(),
|
|
24
|
-
onBeforeFocusChange: jest.fn(),
|
|
25
|
-
});
|
|
26
|
-
jest.clearAllMocks();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should throw without domRef on componentDidMount', () => {
|
|
30
|
-
// @ts-ignore - the point of the test is to test possible runtime problem
|
|
31
|
-
const invalidBase = new Base({ domRef: null, children: [], focusableChildren: [] });
|
|
32
|
-
expect(() => invalidBase.componentDidMount()).toThrow();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should throw without domRef on componentWillUnmount', () => {
|
|
36
|
-
// @ts-ignore - the point of the test is to test possible runtime problem
|
|
37
|
-
const invalidBase = new Base({ domRef: null, children: [], focusableChildren: [] });
|
|
38
|
-
expect(() => invalidBase.componentWillUnmount()).toThrow();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should not throw with valid domRef on componentDidMount', () => {
|
|
42
|
-
expect(() => base.componentDidMount()).not.toThrow();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should not throw with valid domRef on componentWillUnmount', () => {
|
|
46
|
-
expect(() => base.componentWillUnmount()).not.toThrow();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should render children', () => {
|
|
50
|
-
expect(base.render()).toBe('children');
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should return false when setting index out of range', () => {
|
|
54
|
-
expect(base.setIndex(4)).toBe(false);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should return true when setting index out of range', () => {
|
|
58
|
-
expect(base.setIndex(1)).toBe(true);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should increase index by one on forward', () => {
|
|
62
|
-
const oldIndex = base.index;
|
|
63
|
-
base.forward(mockEvent);
|
|
64
|
-
expect(base.index).toBe(oldIndex + 1);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('should deecrease index by one on backward', () => {
|
|
68
|
-
base.index = 2;
|
|
69
|
-
const oldIndex = base.index;
|
|
70
|
-
base.backward(mockEvent);
|
|
71
|
-
expect(base.index).toBe(oldIndex - 1);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should not increase index above focusableChildren.length - 1', () => {
|
|
75
|
-
base.index = base.props.focusableChildren.length - 1;
|
|
76
|
-
base.forward(mockEvent);
|
|
77
|
-
expect(base.index).toBe(base.props.focusableChildren.length - 1);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('should not decrease index below 0', () => {
|
|
81
|
-
base.index = 0;
|
|
82
|
-
base.backward(mockEvent);
|
|
83
|
-
expect(base.index).toBe(0);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should call focus on new element when going forward', () => {
|
|
87
|
-
base.forward(mockEvent);
|
|
88
|
-
expect(refChildB.current!.focus).toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should call focus on new element when going backward', () => {
|
|
92
|
-
base.index = 1;
|
|
93
|
-
base.backward(mockEvent);
|
|
94
|
-
expect(refChildA.current!.focus).toHaveBeenCalled();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should not call focus when can\'t go forward', () => {
|
|
98
|
-
base.index = 2;
|
|
99
|
-
base.forward(mockEvent);
|
|
100
|
-
expect(refChildC.current!.focus).not.toHaveBeenCalled();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should not call focus when can\'t go backward', () => {
|
|
104
|
-
base.backward(mockEvent);
|
|
105
|
-
expect(refChildA.current!.focus).not.toHaveBeenCalled();
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should call stopEvent when going forward', () => {
|
|
109
|
-
base.forward(mockEvent);
|
|
110
|
-
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should call stopEvent when going backward', () => {
|
|
114
|
-
base.index = 1;
|
|
115
|
-
base.backward(mockEvent);
|
|
116
|
-
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should not call stopEvent when can\'t go forward', () => {
|
|
120
|
-
base.index = 2;
|
|
121
|
-
base.forward(mockEvent);
|
|
122
|
-
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should not call stopEvent when can\'t go backward', () => {
|
|
126
|
-
base.backward(mockEvent);
|
|
127
|
-
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should call stopEvent when going forward and preventBlurOnLongPress is on', () => {
|
|
131
|
-
base.props.preventBlurOnLongPress.forward = true;
|
|
132
|
-
base.forward(mockEvent);
|
|
133
|
-
base.forward(mockEvent);
|
|
134
|
-
base.forward(mockEvent);
|
|
135
|
-
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should call stopEvent when going backward and preventBlurOnLongPress is on', () => {
|
|
139
|
-
base.index = 2;
|
|
140
|
-
base.props.preventBlurOnLongPress.backward = true;
|
|
141
|
-
base.backward(mockEvent);
|
|
142
|
-
base.backward(mockEvent);
|
|
143
|
-
base.backward(mockEvent);
|
|
144
|
-
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should call onFocusChanged when focusing current index', () => {
|
|
148
|
-
base.focusCurrentIndex();
|
|
149
|
-
expect(base.props.onFocusChanged).toHaveBeenCalled();
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('should call onBeforeFocusChange when focusing current index', () => {
|
|
153
|
-
base.focusCurrentIndex();
|
|
154
|
-
expect(base.props.onBeforeFocusChange).toHaveBeenCalled();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should focus current index on focus', () => {
|
|
158
|
-
base.index = 2;
|
|
159
|
-
base.focus();
|
|
160
|
-
expect(refChildC.current!.focus).toHaveBeenCalled();
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should call passed onKeyDown on onKeyDown', () => {
|
|
164
|
-
base.onKeyDown();
|
|
165
|
-
expect(mockKeydown).toHaveBeenCalled();
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('should throw in focusCurrentIndex when element does not implement focus', () => {
|
|
169
|
-
base.props.focusableChildren[2] = {};
|
|
170
|
-
base.index = 2;
|
|
171
|
-
expect(() => base.focusCurrentIndex()).toThrow();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('should take result of hasDom into account of determining current focus position', () => {
|
|
175
|
-
base = new Base({
|
|
176
|
-
...Base.defaultProps,
|
|
177
|
-
domRef: rootRef,
|
|
178
|
-
children: 'children',
|
|
179
|
-
focusableChildren: [refChildA, refChildB, refChildC, refChildD],
|
|
180
|
-
onFocusChanged: jest.fn(),
|
|
181
|
-
onBeforeFocusChange: jest.fn(),
|
|
182
|
-
});
|
|
183
|
-
((refChildD.current as IFocusable).hasDom as jest.MockedFn<
|
|
184
|
-
Exclude<IFocusable['hasDom'], undefined>
|
|
185
|
-
>).mockReturnValueOnce(true);
|
|
186
|
-
base.backward(mockEvent);
|
|
187
|
-
expect(base.index).toBe(2);
|
|
188
|
-
base.backward(mockEvent);
|
|
189
|
-
expect(base.index).toBe(1);
|
|
190
|
-
});
|
|
191
|
-
});
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { isRtl } from '@24i/bigscreen-sdk/i18n';
|
|
2
|
-
import { Horizontal } from '../Horizontal';
|
|
3
|
-
import { rootRef, refChildA, refChildB, refChildC } from './shared';
|
|
4
|
-
|
|
5
|
-
jest.mock('@24i/bigscreen-sdk/i18n', () => ({
|
|
6
|
-
...jest.requireActual('@24i/bigscreen-sdk/i18n'),
|
|
7
|
-
isRtl: jest.fn().mockReturnValue(false),
|
|
8
|
-
}));
|
|
9
|
-
const isRtlMock = isRtl as jest.MockedFn<typeof isRtl>;
|
|
10
|
-
|
|
11
|
-
const mockEvent = {
|
|
12
|
-
target: false,
|
|
13
|
-
};
|
|
14
|
-
const mockLEFTEvent = {
|
|
15
|
-
code: 'ArrowLeft',
|
|
16
|
-
...mockEvent,
|
|
17
|
-
};
|
|
18
|
-
const mockRIGHTEvent = {
|
|
19
|
-
code: 'ArrowRight',
|
|
20
|
-
...mockEvent,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
describe('Horizontal Focus Layout', () => {
|
|
24
|
-
let horizontal: any;
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
horizontal = new Horizontal({
|
|
27
|
-
domRef: rootRef,
|
|
28
|
-
children: 'children',
|
|
29
|
-
focusableChildren: [refChildA, refChildB, refChildC],
|
|
30
|
-
onFocusChanged: jest.fn(),
|
|
31
|
-
onBeforeFocusChange: jest.fn(),
|
|
32
|
-
});
|
|
33
|
-
horizontal.forward = jest.fn();
|
|
34
|
-
horizontal.backward = jest.fn();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should be a class component', () => {
|
|
38
|
-
expect(Horizontal.isClass).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should call forward on RIGHT', () => {
|
|
42
|
-
horizontal.onKeyDown(mockRIGHTEvent);
|
|
43
|
-
expect(horizontal.forward).toHaveBeenCalled();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should call backward on LEFT', () => {
|
|
47
|
-
horizontal.setIndex(1);
|
|
48
|
-
horizontal.onKeyDown(mockLEFTEvent);
|
|
49
|
-
expect(horizontal.backward).toHaveBeenCalled();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should call backward on RIGHT when RTL', () => {
|
|
53
|
-
isRtlMock.mockReturnValueOnce(true);
|
|
54
|
-
horizontal.setIndex(1);
|
|
55
|
-
horizontal.onKeyDown(mockRIGHTEvent);
|
|
56
|
-
expect(horizontal.backward).toHaveBeenCalled();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should call forward on LEFT when RTL', () => {
|
|
60
|
-
isRtlMock.mockReturnValueOnce(true);
|
|
61
|
-
horizontal.onKeyDown(mockLEFTEvent);
|
|
62
|
-
expect(horizontal.forward).toHaveBeenCalled();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should call backward on RIGHT when forced RTL', () => {
|
|
66
|
-
horizontal.props.dir = 'rtl';
|
|
67
|
-
horizontal.setIndex(1);
|
|
68
|
-
horizontal.onKeyDown(mockRIGHTEvent);
|
|
69
|
-
expect(horizontal.backward).toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should call forward on LEFT when forced RTL', () => {
|
|
73
|
-
horizontal.props.dir = 'rtl';
|
|
74
|
-
horizontal.onKeyDown(mockLEFTEvent);
|
|
75
|
-
expect(horizontal.forward).toHaveBeenCalled();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should call backward on LEFT when RTL and forced LTR', () => {
|
|
79
|
-
isRtlMock.mockReturnValueOnce(true);
|
|
80
|
-
horizontal.props.dir = 'ltr';
|
|
81
|
-
horizontal.setIndex(1);
|
|
82
|
-
horizontal.onKeyDown(mockLEFTEvent);
|
|
83
|
-
expect(horizontal.backward).toHaveBeenCalled();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('should call forward on RIGHT when RTL and forced LTR', () => {
|
|
87
|
-
isRtlMock.mockReturnValueOnce(true);
|
|
88
|
-
horizontal.props.dir = 'ltr';
|
|
89
|
-
horizontal.onKeyDown(mockRIGHTEvent);
|
|
90
|
-
expect(horizontal.forward).toHaveBeenCalled();
|
|
91
|
-
});
|
|
92
|
-
});
|
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
import { createRef } from '@24i/bigscreen-sdk/jsx';
|
|
2
|
-
import { stopEvent } from '@24i/bigscreen-sdk/utils/stopEvent';
|
|
3
|
-
import { LEFT, RIGHT, UP, DOWN, Key } from '@24i/bigscreen-sdk/device/keymap';
|
|
4
|
-
import { Matrix, CircularDirection } from '../Matrix';
|
|
5
|
-
|
|
6
|
-
jest.mock('@24i/bigscreen-sdk/utils/stopEvent');
|
|
7
|
-
|
|
8
|
-
const simulateKey = (key: Key): KeyboardEvent => ({
|
|
9
|
-
code: key.code,
|
|
10
|
-
} as unknown as KeyboardEvent);
|
|
11
|
-
|
|
12
|
-
const getFocusable = () => ({
|
|
13
|
-
focus: jest.fn(),
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const domRef = createRef(document.createElement('div'));
|
|
17
|
-
|
|
18
|
-
const A1 = createRef(getFocusable());
|
|
19
|
-
const A2 = createRef(getFocusable());
|
|
20
|
-
const A3 = createRef(getFocusable());
|
|
21
|
-
const B1 = createRef(getFocusable());
|
|
22
|
-
const C1 = createRef(getFocusable());
|
|
23
|
-
const C2 = createRef(getFocusable());
|
|
24
|
-
const C3 = createRef(getFocusable());
|
|
25
|
-
const D1 = createRef(getFocusable());
|
|
26
|
-
const D2 = createRef(getFocusable());
|
|
27
|
-
|
|
28
|
-
const MATRIX = [
|
|
29
|
-
[A1, B1, C1, D1],
|
|
30
|
-
[A2, B1, C2, C2],
|
|
31
|
-
[A3, B1, C3, D2],
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
describe('Matrix focus', () => {
|
|
35
|
-
let matrix: Matrix;
|
|
36
|
-
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
matrix = new Matrix({ ...Matrix.defaultProps, matrix: MATRIX, domRef, children: [] });
|
|
39
|
-
jest.clearAllMocks();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should have default index at 0, 0', () => {
|
|
43
|
-
expect(matrix.index).toStrictEqual({ x: 0, y: 0 });
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should go A1 -> RIGHT -> B1', () => {
|
|
47
|
-
matrix.index = { x: 0, y: 0 };
|
|
48
|
-
const event = simulateKey(RIGHT);
|
|
49
|
-
matrix.onKeyDown(event);
|
|
50
|
-
expect(B1.current!.focus).toHaveBeenCalled();
|
|
51
|
-
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
52
|
-
B1.current!.focus.mockClear();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should go A1 -> DOWN -> A2', () => {
|
|
56
|
-
matrix.index = { x: 0, y: 0 };
|
|
57
|
-
const event = simulateKey(DOWN);
|
|
58
|
-
matrix.onKeyDown(event);
|
|
59
|
-
expect(A2.current!.focus).toHaveBeenCalled();
|
|
60
|
-
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
61
|
-
A2.current!.focus.mockClear();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('should go D2 -> LEFT -> C3', () => {
|
|
65
|
-
matrix.index = { x: 3, y: 2 };
|
|
66
|
-
const event = simulateKey(LEFT);
|
|
67
|
-
matrix.onKeyDown(event);
|
|
68
|
-
expect(C3.current!.focus).toHaveBeenCalled();
|
|
69
|
-
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
70
|
-
C3.current!.focus.mockClear();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should go D2 -> UP -> C2', () => {
|
|
74
|
-
matrix.index = { x: 3, y: 2 };
|
|
75
|
-
const event = simulateKey(UP);
|
|
76
|
-
matrix.onKeyDown(event);
|
|
77
|
-
expect(C2.current!.focus).toHaveBeenCalled();
|
|
78
|
-
expect(stopEvent).toHaveBeenCalledTimes(1);
|
|
79
|
-
C2.current!.focus.mockClear();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should not leave left boundary', () => {
|
|
83
|
-
matrix.index = { x: 0, y: 0 };
|
|
84
|
-
const event = simulateKey(LEFT);
|
|
85
|
-
matrix.onKeyDown(event);
|
|
86
|
-
expect(matrix.index).toStrictEqual({ x: 0, y: 0 });
|
|
87
|
-
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should not leave top boundary', () => {
|
|
91
|
-
matrix.index = { x: 1, y: 2 };
|
|
92
|
-
const event = simulateKey(UP);
|
|
93
|
-
matrix.onKeyDown(event);
|
|
94
|
-
expect(matrix.index).toStrictEqual({ x: 1, y: 2 });
|
|
95
|
-
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should not leave right boundary', () => {
|
|
99
|
-
matrix.index = { x: 2, y: 1 };
|
|
100
|
-
const event = simulateKey(RIGHT);
|
|
101
|
-
matrix.onKeyDown(event);
|
|
102
|
-
expect(matrix.index).toStrictEqual({ x: 2, y: 1 });
|
|
103
|
-
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should not leave bottom boundary', () => {
|
|
107
|
-
matrix.index = { x: 3, y: 2 };
|
|
108
|
-
const event = simulateKey(DOWN);
|
|
109
|
-
matrix.onKeyDown(event);
|
|
110
|
-
expect(matrix.index).toStrictEqual({ x: 3, y: 2 });
|
|
111
|
-
expect(stopEvent).not.toHaveBeenCalledTimes(1);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('should call stopEvent when on left and preventBlurOnLongPress is on', () => {
|
|
115
|
-
matrix.props.preventBlurOnLongPress.left = true;
|
|
116
|
-
matrix.index = { x: 1, y: 0 };
|
|
117
|
-
const event = simulateKey(LEFT);
|
|
118
|
-
matrix.onKeyDown(event);
|
|
119
|
-
matrix.onKeyDown(event);
|
|
120
|
-
matrix.onKeyDown(event);
|
|
121
|
-
expect(matrix.index).toStrictEqual({ x: 0, y: 0 });
|
|
122
|
-
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should call stopEvent when on up and preventBlurOnLongPress is on', () => {
|
|
126
|
-
matrix.props.preventBlurOnLongPress.up = true;
|
|
127
|
-
matrix.index = { x: 0, y: 1 };
|
|
128
|
-
const event = simulateKey(UP);
|
|
129
|
-
matrix.onKeyDown(event);
|
|
130
|
-
matrix.onKeyDown(event);
|
|
131
|
-
matrix.onKeyDown(event);
|
|
132
|
-
expect(matrix.index).toStrictEqual({ x: 0, y: 0 });
|
|
133
|
-
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('should call stopEvent when on right and preventBlurOnLongPress is on', () => {
|
|
137
|
-
matrix.props.preventBlurOnLongPress.right = true;
|
|
138
|
-
matrix.index = { x: 2, y: 0 };
|
|
139
|
-
const event = simulateKey(RIGHT);
|
|
140
|
-
matrix.onKeyDown(event);
|
|
141
|
-
matrix.onKeyDown(event);
|
|
142
|
-
matrix.onKeyDown(event);
|
|
143
|
-
expect(matrix.index).toStrictEqual({ x: 3, y: 0 });
|
|
144
|
-
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should call stopEvent when on down and preventBlurOnLongPress is on', () => {
|
|
148
|
-
matrix.props.preventBlurOnLongPress.down = true;
|
|
149
|
-
matrix.index = { x: 0, y: 1 };
|
|
150
|
-
const event = simulateKey(DOWN);
|
|
151
|
-
matrix.onKeyDown(event);
|
|
152
|
-
matrix.onKeyDown(event);
|
|
153
|
-
matrix.onKeyDown(event);
|
|
154
|
-
expect(matrix.index).toStrictEqual({ x: 0, y: 2 });
|
|
155
|
-
expect(stopEvent).toHaveBeenCalledTimes(3);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should keep row (Ax -> RIGHT -> B1 -> RIGHT -> Cx)', () => {
|
|
159
|
-
const event = simulateKey(RIGHT);
|
|
160
|
-
matrix.index = { x: 0, y: 0 };
|
|
161
|
-
matrix.onKeyDown(event);
|
|
162
|
-
matrix.onKeyDown(event);
|
|
163
|
-
expect(C1.current!.focus).toHaveBeenCalled();
|
|
164
|
-
matrix.index = { x: 0, y: 1 };
|
|
165
|
-
matrix.onKeyDown(event);
|
|
166
|
-
matrix.onKeyDown(event);
|
|
167
|
-
expect(C2.current!.focus).toHaveBeenCalled();
|
|
168
|
-
matrix.index = { x: 0, y: 2 };
|
|
169
|
-
matrix.onKeyDown(event);
|
|
170
|
-
matrix.onKeyDown(event);
|
|
171
|
-
expect(C3.current!.focus).toHaveBeenCalled();
|
|
172
|
-
C1.current!.focus.mockClear();
|
|
173
|
-
C2.current!.focus.mockClear();
|
|
174
|
-
C3.current!.focus.mockClear();
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should keep column (C3/D2 -> UP -> C2 -> UP -> C1/D1)', () => {
|
|
178
|
-
const event = simulateKey(UP);
|
|
179
|
-
matrix.index = { x: 2, y: 2 };
|
|
180
|
-
matrix.onKeyDown(event);
|
|
181
|
-
matrix.onKeyDown(event);
|
|
182
|
-
expect(C1.current!.focus).toHaveBeenCalled();
|
|
183
|
-
matrix.index = { x: 3, y: 2 };
|
|
184
|
-
matrix.onKeyDown(event);
|
|
185
|
-
matrix.onKeyDown(event);
|
|
186
|
-
expect(D1.current!.focus).toHaveBeenCalled();
|
|
187
|
-
C1.current!.focus.mockClear();
|
|
188
|
-
D1.current!.focus.mockClear();
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('should skip same and focus next (C2 -> LEFT -> C2 -> B1)', () => {
|
|
192
|
-
matrix.index = { x: 3, y: 1 };
|
|
193
|
-
matrix.onKeyDown(simulateKey(LEFT));
|
|
194
|
-
expect(B1.current!.focus).toHaveBeenCalled();
|
|
195
|
-
B1.current!.focus.mockClear();
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
describe('circular focus', () => {
|
|
199
|
-
const commonProps = {
|
|
200
|
-
...Matrix.defaultProps,
|
|
201
|
-
matrix: MATRIX,
|
|
202
|
-
domRef,
|
|
203
|
-
children: null as any,
|
|
204
|
-
};
|
|
205
|
-
it('should circle to the left (A1 -> left -> D1)', () => {
|
|
206
|
-
matrix = new Matrix({ ...commonProps, circular: CircularDirection.HORIZONTAL });
|
|
207
|
-
matrix.index = { x: 0, y: 0 };
|
|
208
|
-
const event = simulateKey(LEFT);
|
|
209
|
-
matrix.onKeyDown(event);
|
|
210
|
-
expect(D1.current!.focus).toHaveBeenCalled();
|
|
211
|
-
expect(matrix.index).toEqual({ x: 3, y: 0 });
|
|
212
|
-
D1.current!.focus.mockClear();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('should circle to the right (A1 -> right -> D1)', () => {
|
|
216
|
-
matrix = new Matrix({ ...commonProps, circular: CircularDirection.HORIZONTAL });
|
|
217
|
-
matrix.index = { x: 3, y: 0 };
|
|
218
|
-
const event = simulateKey(RIGHT);
|
|
219
|
-
matrix.onKeyDown(event);
|
|
220
|
-
expect(A1.current!.focus).toHaveBeenCalled();
|
|
221
|
-
expect(matrix.index).toEqual({ x: 0, y: 0 });
|
|
222
|
-
A1.current!.focus.mockClear();
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should circle to upwards (A1 -> up -> A3)', () => {
|
|
226
|
-
matrix = new Matrix({ ...commonProps, circular: CircularDirection.VERTICAL });
|
|
227
|
-
matrix.index = { x: 0, y: 0 };
|
|
228
|
-
const event = simulateKey(UP);
|
|
229
|
-
matrix.onKeyDown(event);
|
|
230
|
-
expect(A3.current!.focus).toHaveBeenCalled();
|
|
231
|
-
expect(matrix.index).toEqual({ x: 0, y: 2 });
|
|
232
|
-
A3.current!.focus.mockClear();
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('should circle to downwards (A3 -> down -> A1)', () => {
|
|
236
|
-
matrix = new Matrix({ ...commonProps, circular: CircularDirection.VERTICAL });
|
|
237
|
-
matrix.index = { x: 0, y: 2 };
|
|
238
|
-
const event = simulateKey(DOWN);
|
|
239
|
-
matrix.onKeyDown(event);
|
|
240
|
-
expect(A1.current!.focus).toHaveBeenCalled();
|
|
241
|
-
expect(matrix.index).toEqual({ x: 0, y: 0 });
|
|
242
|
-
A1.current!.focus.mockClear();
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it('should circle horizontally and vertically (A3 -> down -> A1 -> left -> D1)', () => {
|
|
246
|
-
matrix = new Matrix({ ...commonProps, circular: CircularDirection.ALL });
|
|
247
|
-
matrix.index = { x: 0, y: 2 };
|
|
248
|
-
let event = simulateKey(DOWN);
|
|
249
|
-
matrix.onKeyDown(event);
|
|
250
|
-
expect(matrix.index).toEqual({ x: 0, y: 0 });
|
|
251
|
-
|
|
252
|
-
event = simulateKey(LEFT);
|
|
253
|
-
matrix.onKeyDown(event);
|
|
254
|
-
expect(matrix.index).toEqual({ x: 3, y: 0 });
|
|
255
|
-
A1.current!.focus.mockClear();
|
|
256
|
-
D1.current!.focus.mockClear();
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it('given vertical should not circle to horizontally (A1 -> left -> A1)', () => {
|
|
260
|
-
matrix = new Matrix({ ...commonProps, circular: CircularDirection.VERTICAL });
|
|
261
|
-
matrix.index = { x: 0, y: 0 };
|
|
262
|
-
const event = simulateKey(LEFT);
|
|
263
|
-
matrix.onKeyDown(event);
|
|
264
|
-
expect(matrix.index).toEqual({ x: 0, y: 0 });
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it('given horizontal should not circle to vertically (A1 -> up -> A1)', () => {
|
|
268
|
-
matrix = new Matrix({ ...commonProps, circular: CircularDirection.HORIZONTAL });
|
|
269
|
-
matrix.index = { x: 0, y: 0 };
|
|
270
|
-
const event = simulateKey(UP);
|
|
271
|
-
matrix.onKeyDown(event);
|
|
272
|
-
expect(matrix.index).toEqual({ x: 0, y: 0 });
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it('given 1 vertical key should not loop infinitely', () => {
|
|
276
|
-
matrix = new Matrix({ ...commonProps, circular: CircularDirection.VERTICAL });
|
|
277
|
-
matrix.index = { x: 1, y: 0 };
|
|
278
|
-
const event = simulateKey(UP);
|
|
279
|
-
matrix.onKeyDown(event);
|
|
280
|
-
expect(matrix.index).toEqual({ x: 1, y: 0 });
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
it('given 1 horizontal key should not loop infinitely', () => {
|
|
284
|
-
const keyMatrix = [
|
|
285
|
-
[A1, B1, C1, D1],
|
|
286
|
-
[C2, C2, C2, C2],
|
|
287
|
-
];
|
|
288
|
-
matrix = new Matrix({
|
|
289
|
-
...commonProps,
|
|
290
|
-
matrix: keyMatrix,
|
|
291
|
-
circular: CircularDirection.VERTICAL,
|
|
292
|
-
});
|
|
293
|
-
matrix.index = { x: 0, y: 1 };
|
|
294
|
-
const event = simulateKey(LEFT);
|
|
295
|
-
matrix.onKeyDown(event);
|
|
296
|
-
expect(matrix.index).toEqual({ x: 0, y: 1 });
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Vertical } from '../Vertical';
|
|
2
|
-
import { rootRef, refChildA, refChildB, refChildC } from './shared';
|
|
3
|
-
|
|
4
|
-
const mockEvent = {
|
|
5
|
-
target: false,
|
|
6
|
-
};
|
|
7
|
-
const mockUPEvent = {
|
|
8
|
-
code: 'ArrowUp',
|
|
9
|
-
...mockEvent,
|
|
10
|
-
};
|
|
11
|
-
const mockDOWNEvent = {
|
|
12
|
-
code: 'ArrowDown',
|
|
13
|
-
...mockEvent,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
describe('Vertical Focus Layout', () => {
|
|
17
|
-
let vertical: any;
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
vertical = new Vertical({
|
|
20
|
-
domRef: rootRef,
|
|
21
|
-
children: 'children',
|
|
22
|
-
focusableChildren: [refChildA, refChildB, refChildC],
|
|
23
|
-
onFocusChanged: jest.fn(),
|
|
24
|
-
onBeforeFocusChange: jest.fn(),
|
|
25
|
-
});
|
|
26
|
-
vertical.forward = jest.fn();
|
|
27
|
-
vertical.backward = jest.fn();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should be a class component', () => {
|
|
31
|
-
expect(Vertical.isClass).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should call forward on DOWN', () => {
|
|
35
|
-
vertical.onKeyDown(mockDOWNEvent);
|
|
36
|
-
expect(vertical.forward).toHaveBeenCalled();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should call forward on UP', () => {
|
|
40
|
-
vertical.setIndex(1);
|
|
41
|
-
vertical.onKeyDown(mockUPEvent);
|
|
42
|
-
expect(vertical.backward).toHaveBeenCalled();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import * as i18n from '@24i/bigscreen-sdk/i18n';
|
|
2
|
-
import { isRtl } from '../isRtl';
|
|
3
|
-
import { SharedProps } from '../types';
|
|
4
|
-
|
|
5
|
-
describe('isRtl', () => {
|
|
6
|
-
const mockIsRtl = jest.spyOn(i18n, 'isRtl');
|
|
7
|
-
|
|
8
|
-
it.each([
|
|
9
|
-
{ focusDir: 'ltr', langDir: 'ltr', expectedIsRtl: false },
|
|
10
|
-
{ focusDir: 'rtl', langDir: 'ltr', expectedIsRtl: true },
|
|
11
|
-
{ focusDir: 'auto', langDir: 'ltr', expectedIsRtl: false },
|
|
12
|
-
{ focusDir: undefined, langDir: 'ltr', expectedIsRtl: false },
|
|
13
|
-
{ focusDir: 'ltr', langDir: 'rtl', expectedIsRtl: false },
|
|
14
|
-
{ focusDir: 'rtl', langDir: 'rtl', expectedIsRtl: true },
|
|
15
|
-
{ focusDir: 'auto', langDir: 'rtl', expectedIsRtl: true },
|
|
16
|
-
{ focusDir: undefined, langDir: 'rtl', expectedIsRtl: true },
|
|
17
|
-
])('should for $focusDir focus and $langDir language return $expectedIsRtl', ({
|
|
18
|
-
focusDir, langDir, expectedIsRtl,
|
|
19
|
-
}) => {
|
|
20
|
-
mockIsRtl.mockReturnValue(langDir === 'rtl');
|
|
21
|
-
expect(isRtl(focusDir as SharedProps['dir'])).toBe(expectedIsRtl);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { createRef } from '@24i/bigscreen-sdk/jsx';
|
|
2
|
-
import { IFocusable } from '../../IFocusable';
|
|
3
|
-
|
|
4
|
-
class ChildMock {
|
|
5
|
-
focus = jest.fn();
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const root = document.createElement('div');
|
|
9
|
-
export const rootRef = createRef(root);
|
|
10
|
-
|
|
11
|
-
const childA = new ChildMock();
|
|
12
|
-
const childB = new ChildMock();
|
|
13
|
-
const childC = new ChildMock();
|
|
14
|
-
const childD = new ChildMock();
|
|
15
|
-
(childD as IFocusable).hasDom = jest.fn().mockReturnValue(false);
|
|
16
|
-
export const refChildA = createRef(childA);
|
|
17
|
-
export const refChildB = createRef(childB);
|
|
18
|
-
export const refChildC = createRef(childC);
|
|
19
|
-
export const refChildD = createRef(childD);
|