Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 952d440

Browse files
authored
Merge pull request #276 from ckeditor/t/ckeditor5/1599
Feature: Made `FocusTracker#focusedElement` observable to bring support for multi-root editors (see ckeditor/ckeditor5#1599).
2 parents 78107a4 + dbce45e commit 952d440

File tree

2 files changed

+45
-8
lines changed

2 files changed

+45
-8
lines changed

src/focustracker.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,17 @@ export default class FocusTracker {
3939
this.set( 'isFocused', false );
4040

4141
/**
42-
* Currently focused element.
42+
* The currently focused element.
43+
*
44+
* While {@link #isFocused `isFocused`} remains `true`, the focus can
45+
* move between different UI elements. This property tracks those
46+
* elements and tells which one is currently focused.
4347
*
4448
* @readonly
45-
* @member {HTMLElement}
49+
* @observable
50+
* @member {HTMLElement|null}
4651
*/
47-
this.focusedElement = null;
52+
this.set( 'focusedElement', null );
4853

4954
/**
5055
* List of registered elements.

tests/focustracker.js

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import FocusTracker from '../src/focustracker';
99
import CKEditorError from '../src/ckeditorerror';
1010
import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
11+
import global from '@ckeditor/ckeditor5-utils/src/dom/global';
1112

1213
describe( 'FocusTracker', () => {
1314
let focusTracker, container, containerFirstInput, containerSecondInput;
@@ -43,6 +44,22 @@ describe( 'FocusTracker', () => {
4344
expect( observableSpy.calledOnce ).to.true;
4445
} );
4546
} );
47+
48+
describe( 'focusedElement', () => {
49+
it( 'should be null at default', () => {
50+
expect( focusTracker.focusedElement ).to.be.null;
51+
} );
52+
53+
it( 'should be observable', () => {
54+
const observableSpy = testUtils.sinon.spy();
55+
56+
focusTracker.listenTo( focusTracker, 'change:focusedElement', observableSpy );
57+
58+
focusTracker.focusedElement = global.document.body;
59+
60+
expect( observableSpy.calledOnce ).to.true;
61+
} );
62+
} );
4663
} );
4764

4865
describe( 'add', () => {
@@ -63,16 +80,20 @@ describe( 'FocusTracker', () => {
6380
containerFirstInput.dispatchEvent( new Event( 'focus' ) );
6481

6582
expect( focusTracker.isFocused ).to.true;
83+
expect( focusTracker.focusedElement ).to.equal( containerFirstInput );
6684
} );
6785

6886
it( 'should start listening on element blur and update `isFocused` property', () => {
6987
focusTracker.add( containerFirstInput );
70-
focusTracker.isFocused = true;
88+
containerFirstInput.dispatchEvent( new Event( 'focus' ) );
89+
90+
expect( focusTracker.focusedElement ).to.equal( containerFirstInput );
7191

7292
containerFirstInput.dispatchEvent( new Event( 'blur' ) );
7393
testUtils.sinon.clock.tick( 0 );
7494

7595
expect( focusTracker.isFocused ).to.false;
96+
expect( focusTracker.focusedElement ).to.be.null;
7697
} );
7798
} );
7899

@@ -85,16 +106,20 @@ describe( 'FocusTracker', () => {
85106
containerFirstInput.dispatchEvent( new Event( 'focus' ) );
86107

87108
expect( focusTracker.isFocused ).to.true;
109+
expect( focusTracker.focusedElement ).to.equal( container );
88110
} );
89111

90112
it( 'should start listening on element blur using event capturing and update `isFocused` property', () => {
91113
focusTracker.add( container );
92-
focusTracker.isFocused = true;
114+
containerFirstInput.dispatchEvent( new Event( 'focus' ) );
115+
116+
expect( focusTracker.focusedElement ).to.equal( container );
93117

94118
containerFirstInput.dispatchEvent( new Event( 'blur' ) );
95119
testUtils.sinon.clock.tick( 0 );
96120

97121
expect( focusTracker.isFocused ).to.false;
122+
expect( focusTracker.focusedElement ).to.be.null;
98123
} );
99124

100125
it( 'should not change `isFocused` property when focus is going between child elements', () => {
@@ -103,30 +128,34 @@ describe( 'FocusTracker', () => {
103128
focusTracker.add( container );
104129

105130
containerFirstInput.dispatchEvent( new Event( 'focus' ) );
131+
expect( focusTracker.focusedElement ).to.equal( container );
132+
expect( focusTracker.isFocused ).to.true;
106133

107134
focusTracker.listenTo( focusTracker, 'change:isFocused', changeSpy );
108135

109-
expect( focusTracker.isFocused ).to.true;
110-
111136
containerFirstInput.dispatchEvent( new Event( 'blur' ) );
112137
containerSecondInput.dispatchEvent( new Event( 'focus' ) );
113138
testUtils.sinon.clock.tick( 0 );
114139

140+
expect( focusTracker.focusedElement ).to.equal( container );
115141
expect( focusTracker.isFocused ).to.true;
116142
expect( changeSpy.notCalled ).to.true;
117143
} );
118144

119145
// https://github.com/ckeditor/ckeditor5-utils/issues/159
120146
it( 'should keep `isFocused` synced when multiple blur events are followed by the focus', () => {
121147
focusTracker.add( container );
122-
focusTracker.isFocused = true;
148+
container.dispatchEvent( new Event( 'focus' ) );
149+
150+
expect( focusTracker.focusedElement ).to.equal( container );
123151

124152
container.dispatchEvent( new Event( 'blur' ) );
125153
containerFirstInput.dispatchEvent( new Event( 'blur' ) );
126154
containerSecondInput.dispatchEvent( new Event( 'focus' ) );
127155
testUtils.sinon.clock.tick( 0 );
128156

129157
expect( focusTracker.isFocused ).to.be.true;
158+
expect( focusTracker.focusedElement ).to.equal( container );
130159
} );
131160
} );
132161
} );
@@ -145,6 +174,7 @@ describe( 'FocusTracker', () => {
145174
containerFirstInput.dispatchEvent( new Event( 'focus' ) );
146175

147176
expect( focusTracker.isFocused ).to.false;
177+
expect( focusTracker.focusedElement ).to.be.null;
148178
} );
149179

150180
it( 'should stop listening on element blur', () => {
@@ -161,13 +191,15 @@ describe( 'FocusTracker', () => {
161191
it( 'should blur element before removing when is focused', () => {
162192
focusTracker.add( containerFirstInput );
163193
containerFirstInput.dispatchEvent( new Event( 'focus' ) );
194+
expect( focusTracker.focusedElement ).to.equal( containerFirstInput );
164195

165196
expect( focusTracker.isFocused ).to.true;
166197

167198
focusTracker.remove( containerFirstInput );
168199
testUtils.sinon.clock.tick( 0 );
169200

170201
expect( focusTracker.isFocused ).to.false;
202+
expect( focusTracker.focusedElement ).to.be.null;
171203
} );
172204
} );
173205

0 commit comments

Comments
 (0)