Skip to content

Commit e47ea7b

Browse files
authored
Merge pull request #1033 from helgablazhkun/click-helper-blur-fix
2 parents 77022fb + 1e0c6a4 commit e47ea7b

File tree

6 files changed

+200
-14
lines changed

6 files changed

+200
-14
lines changed

addon-test-support/@ember/test-helpers/dom/click.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { getWindowOrElement } from './-get-window-or-element';
33
import fireEvent from './fire-event';
44
import { __focus__ } from './focus';
55
import settled from '../settled';
6-
import isFocusable from './-is-focusable';
76
import { Promise } from '../-utils';
87
import isFormControl from './-is-form-control';
9-
import Target from './-target';
8+
import Target, { isWindow } from './-target';
109
import { log } from '@ember/test-helpers/dom/-logging';
1110
import { runHooks, registerHook } from '../-internal/helper-hooks';
11+
import { __blur__ } from './blur';
1212

1313
const PRIMARY_BUTTON = 1;
1414
const MAIN_BUTTON_PRESSED = 0;
@@ -34,7 +34,7 @@ export const DEFAULT_CLICK_OPTIONS = {
3434
export function __click__(element: Element | Document | Window, options: MouseEventInit): void {
3535
fireEvent(element, 'mousedown', options);
3636

37-
if (isFocusable(element)) {
37+
if (!isWindow(element)) {
3838
__focus__(element);
3939
}
4040

addon-test-support/@ember/test-helpers/dom/double-click.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import { getWindowOrElement } from './-get-window-or-element';
33
import fireEvent from './fire-event';
44
import { __focus__ } from './focus';
55
import settled from '../settled';
6-
import isFocusable from './-is-focusable';
76
import { Promise } from '../-utils';
87
import { DEFAULT_CLICK_OPTIONS } from './click';
9-
import Target from './-target';
8+
import Target, { isWindow } from './-target';
109
import { log } from '@ember/test-helpers/dom/-logging';
1110
import isFormControl from './-is-form-control';
1211
import { runHooks, registerHook } from '../-internal/helper-hooks';
@@ -26,7 +25,7 @@ export function __doubleClick__(
2625
): void {
2726
fireEvent(element, 'mousedown', options);
2827

29-
if (isFocusable(element)) {
28+
if (!isWindow(element)) {
3029
__focus__(element);
3130
}
3231

addon-test-support/@ember/test-helpers/dom/focus.ts

+20-8
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,29 @@ registerHook('focus', 'start', (target: Target) => {
1717
@param {Element} element the element to trigger events on
1818
*/
1919
export function __focus__(element: HTMLElement | Element | Document | SVGElement): void {
20+
const previousFocusedElement =
21+
document.activeElement &&
22+
document.activeElement !== element &&
23+
isFocusable(document.activeElement)
24+
? document.activeElement
25+
: null;
26+
27+
// fire __blur__ manually with the null relatedTarget when the target is not focusable
28+
// and there was a previously focused element
2029
if (!isFocusable(element)) {
21-
throw new Error(`${element} is not focusable`);
30+
if (previousFocusedElement) {
31+
__blur__(previousFocusedElement, null);
32+
}
33+
34+
return;
2235
}
2336

2437
let browserIsNotFocused = document.hasFocus && !document.hasFocus();
2538

2639
// fire __blur__ manually with the correct relatedTarget when the browser is not
2740
// already in focus and there was a previously focused element
28-
if (
29-
document.activeElement &&
30-
document.activeElement !== element &&
31-
isFocusable(document.activeElement) &&
32-
browserIsNotFocused
33-
) {
34-
__blur__(document.activeElement, element);
41+
if (previousFocusedElement && browserIsNotFocused) {
42+
__blur__(previousFocusedElement, element);
3543
}
3644

3745
// makes `document.activeElement` be `element`. If the browser is focused, it also fires a focus event
@@ -88,6 +96,10 @@ export default function focus(target: Target): Promise<void> {
8896
throw new Error(`Element not found when calling \`focus('${target}')\`.`);
8997
}
9098

99+
if (!isFocusable(element)) {
100+
throw new Error(`${element} is not focusable`);
101+
}
102+
91103
__focus__(element);
92104

93105
return settled();

tests/unit/dom/click-test.js

+41
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,47 @@ module('DOM Helper: click', function (hooks) {
233233
assert.verifySteps(['mousedown', 'mouseup', 'click']);
234234
});
235235
});
236+
237+
module('focusable and non-focusable elements interaction', function () {
238+
test('clicking on non-focusable element triggers blur on active element', async function (assert) {
239+
element = document.createElement('div');
240+
241+
insertElement(element);
242+
243+
const focusableElement = buildInstrumentedElement('input');
244+
245+
await click(focusableElement);
246+
await click(element);
247+
248+
assert.verifySteps(['mousedown', 'focus', 'focusin', 'mouseup', 'click', 'blur', 'focusout']);
249+
});
250+
251+
test('clicking on focusable element triggers blur on active element', async function (assert) {
252+
element = document.createElement('input');
253+
254+
insertElement(element);
255+
256+
const focusableElement = buildInstrumentedElement('input');
257+
258+
await click(focusableElement);
259+
await click(element);
260+
261+
assert.verifySteps(['mousedown', 'focus', 'focusin', 'mouseup', 'click', 'blur', 'focusout']);
262+
});
263+
264+
test('clicking on non-focusable element does not trigger blur on non-focusable active element', async function (assert) {
265+
element = document.createElement('div');
266+
267+
insertElement(element);
268+
269+
const nonFocusableElement = buildInstrumentedElement('div');
270+
271+
await click(nonFocusableElement);
272+
await click(element);
273+
274+
assert.verifySteps(['mousedown', 'mouseup', 'click']);
275+
});
276+
});
236277
});
237278

238279
module('DOM Helper: click with window', function () {

tests/unit/dom/double-click-test.js

+73
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,79 @@ module('DOM Helper: doubleClick', function (hooks) {
277277
]);
278278
});
279279
});
280+
281+
module('focusable and non-focusable elements interaction', function () {
282+
test('cdouble-licking on non-focusable element triggers blur on active element', async function (assert) {
283+
element = document.createElement('div');
284+
285+
insertElement(element);
286+
287+
const focusableElement = buildInstrumentedElement('input');
288+
289+
await doubleClick(focusableElement);
290+
await doubleClick(element);
291+
292+
assert.verifySteps([
293+
'mousedown',
294+
'focus',
295+
'focusin',
296+
'mouseup',
297+
'click',
298+
'mousedown',
299+
'mouseup',
300+
'click',
301+
'dblclick',
302+
'blur',
303+
'focusout',
304+
]);
305+
});
306+
307+
test('double-clicking on focusable element triggers blur on active element', async function (assert) {
308+
element = document.createElement('input');
309+
310+
insertElement(element);
311+
312+
const focusableElement = buildInstrumentedElement('input');
313+
314+
await doubleClick(focusableElement);
315+
await doubleClick(element);
316+
317+
assert.verifySteps([
318+
'mousedown',
319+
'focus',
320+
'focusin',
321+
'mouseup',
322+
'click',
323+
'mousedown',
324+
'mouseup',
325+
'click',
326+
'dblclick',
327+
'blur',
328+
'focusout',
329+
]);
330+
});
331+
332+
test('double-clicking on non-focusable element does not trigger blur on non-focusable active element', async function (assert) {
333+
element = document.createElement('div');
334+
335+
insertElement(element);
336+
337+
const nonFocusableElement = buildInstrumentedElement('div');
338+
339+
await doubleClick(nonFocusableElement);
340+
await doubleClick(element);
341+
342+
assert.verifySteps([
343+
'mousedown',
344+
'mouseup',
345+
'click',
346+
'mousedown',
347+
'mouseup',
348+
'click',
349+
'dblclick',
350+
]);
351+
});
352+
});
280353
});
281354

282355
module('DOM Helper: doubleClick with window', function () {

tests/unit/dom/tap-test.js

+61
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,65 @@ module('DOM Helper: tap', function (hooks) {
181181
assert.rejects(tap(element), new Error('Can not `tap` disabled [object HTMLInputElement]'));
182182
});
183183
});
184+
185+
module('focusable and non-focusable elements interaction', function () {
186+
test('tapping on non-focusable element triggers blur on active element', async function (assert) {
187+
element = document.createElement('div');
188+
189+
insertElement(element);
190+
191+
const focusableElement = buildInstrumentedElement('input');
192+
193+
await tap(focusableElement);
194+
await tap(element);
195+
196+
assert.verifySteps([
197+
'touchstart',
198+
'touchend',
199+
'mousedown',
200+
'focus',
201+
'focusin',
202+
'mouseup',
203+
'click',
204+
'blur',
205+
'focusout',
206+
]);
207+
});
208+
209+
test('tapping on focusable element triggers blur on active element', async function (assert) {
210+
element = document.createElement('input');
211+
212+
insertElement(element);
213+
214+
const focusableElement = buildInstrumentedElement('input');
215+
216+
await tap(focusableElement);
217+
await tap(element);
218+
219+
assert.verifySteps([
220+
'touchstart',
221+
'touchend',
222+
'mousedown',
223+
'focus',
224+
'focusin',
225+
'mouseup',
226+
'click',
227+
'blur',
228+
'focusout',
229+
]);
230+
});
231+
232+
test('tapping on non-focusable element does not trigger blur on non-focusable active element', async function (assert) {
233+
element = document.createElement('div');
234+
235+
insertElement(element);
236+
237+
const nonFocusableElement = buildInstrumentedElement('div');
238+
239+
await tap(nonFocusableElement);
240+
await tap(element);
241+
242+
assert.verifySteps(['touchstart', 'touchend', 'mousedown', 'mouseup', 'click']);
243+
});
244+
});
184245
});

0 commit comments

Comments
 (0)