@24i/bigscreen-sdk 1.0.25-alpha.2440 → 1.0.25
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/focus/README.md +1 -0
- package/packages/focus/src/Layout/Base.tsx +12 -2
- 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
package/packages/focus/README.md
CHANGED
|
@@ -40,6 +40,7 @@ to the focused element and its index. Ideal place to change other elements
|
|
|
40
40
|
- onBeforeFocusChange: Optional callback just before focus change. It receives a reference
|
|
41
41
|
to the focused element and its index. Ideal place for preparing for the change
|
|
42
42
|
(e.g. moving the container to fit new element in the screen)
|
|
43
|
+
- circular: makes focus circular, e.g. when pressing left at leftmost element focus will go to rightmost element
|
|
43
44
|
- dir: Optional forceful setting of the component direction. Defaults to `'auto'` which follows
|
|
44
45
|
the direction of the app.
|
|
45
46
|
`'ltr'` always forces left-to-right layout,
|
|
@@ -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
|
}
|
|
@@ -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);
|