From 98926bcebf4fc2907ce89d4816085062bf066502 Mon Sep 17 00:00:00 2001 From: Ruslan Grabovyi Date: Fri, 28 Aug 2020 22:10:59 +0300 Subject: [PATCH] Blur the previous active element on focus --- .../@ember/test-helpers/dom/focus.ts | 9 ++++ tests/unit/dom/focus-test.js | 53 ++++++++++++++++--- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/addon-test-support/@ember/test-helpers/dom/focus.ts b/addon-test-support/@ember/test-helpers/dom/focus.ts index 1d5fe1a50..6b54747a2 100644 --- a/addon-test-support/@ember/test-helpers/dom/focus.ts +++ b/addon-test-support/@ember/test-helpers/dom/focus.ts @@ -6,6 +6,7 @@ import { nextTickPromise } from '../-utils'; import Target from './-target'; import { log } from '@ember/test-helpers/dom/-logging'; import { runHooks, registerHook } from '../-internal/helper-hooks'; +import { __blur__ } from './blur'; registerHook('focus', 'start', (target: Target) => { log('focus', target); @@ -22,6 +23,14 @@ export function __focus__(element: HTMLElement | Element | Document | SVGElement let browserIsNotFocused = document.hasFocus && !document.hasFocus(); + if ( + document.activeElement && + document.activeElement !== element && + isFocusable(document.activeElement) + ) { + __blur__(document.activeElement); + } + // makes `document.activeElement` be `element`. If the browser is focused, it also fires a focus event element.focus(); diff --git a/tests/unit/dom/focus-test.js b/tests/unit/dom/focus-test.js index c753eb093..c0f82a203 100644 --- a/tests/unit/dom/focus-test.js +++ b/tests/unit/dom/focus-test.js @@ -1,13 +1,17 @@ import { module, test } from 'qunit'; import { focus, setupContext, teardownContext, _registerHook } from '@ember/test-helpers'; -import { buildInstrumentedElement, insertElement } from '../../helpers/events'; -import { isIE11 } from '../../helpers/browser-detect'; +import { buildInstrumentedElement, insertElement, instrumentElement } from '../../helpers/events'; +import { isIE11, isEdge } from '../../helpers/browser-detect'; import hasEmberVersion from '@ember/test-helpers/has-ember-version'; let focusSteps = ['focus', 'focusin']; +let blurSteps = ['blur', 'focusout']; if (isIE11) { focusSteps = ['focusin', 'focus']; + blurSteps = ['focusout', 'blur']; +} else if (isEdge) { + blurSteps = ['focusout', 'blur']; } module('DOM Helper: focus', function (hooks) { @@ -95,6 +99,43 @@ module('DOM Helper: focus', function (hooks) { assert.verifySteps(focusSteps); }); + test('blurs the previous active element', async function (assert) { + element = buildInstrumentedElement('input', ['target']); + + const focusedElement = document.createElement('textarea'); + insertElement(focusedElement); + focusedElement.focus(); + instrumentElement(focusedElement, ['target']); + + await focus(element); + + assert.verifySteps([ + ...blurSteps.map(s => { + return `${s} [object HTMLTextAreaElement]`; + }), + ...focusSteps.map(s => { + return `${s} [object HTMLInputElement]`; + }), + ]); + }); + + test('does not attempt to blur the previous element if it is not focusable', async function (assert) { + element = buildInstrumentedElement('input', ['target']); + + const focusedElement = document.createElement('div'); + insertElement(focusedElement); + focusedElement.focus(); + instrumentElement(focusedElement, ['target']); + + await focus(element); + + assert.verifySteps([ + ...focusSteps.map(s => { + return `${s} [object HTMLInputElement]`; + }), + ]); + }); + test('rejects if selector is not found', async function (assert) { element = buildInstrumentedElement('div'); @@ -106,7 +147,7 @@ module('DOM Helper: focus', function (hooks) { ); }); - test('focusing a input via selector with context set', async function (assert) { + test('focusing an input via selector with context set', async function (assert) { element = buildInstrumentedElement('input'); await setupContext(context); @@ -116,7 +157,7 @@ module('DOM Helper: focus', function (hooks) { assert.strictEqual(document.activeElement, element, 'activeElement updated'); }); - test('focusing a input via element with context set', async function (assert) { + test('focusing an input via element with context set', async function (assert) { element = buildInstrumentedElement('input'); await setupContext(context); @@ -126,7 +167,7 @@ module('DOM Helper: focus', function (hooks) { assert.strictEqual(document.activeElement, element, 'activeElement updated'); }); - test('focusing a input via element without context set', async function (assert) { + test('focusing an input via element without context set', async function (assert) { element = buildInstrumentedElement('input'); await focus(element); @@ -135,7 +176,7 @@ module('DOM Helper: focus', function (hooks) { assert.strictEqual(document.activeElement, element, 'activeElement updated'); }); - test('focusing a input via selector without context set', async function (assert) { + test('focusing an input via selector without context set', async function (assert) { element = buildInstrumentedElement('input'); assert.rejects(