Skip to content

Commit

Permalink
Merge pull request #1033 from helgablazhkun/click-helper-blur-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
rwjblue authored May 30, 2021
2 parents 77022fb + 1e0c6a4 commit e47ea7b
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 14 deletions.
6 changes: 3 additions & 3 deletions addon-test-support/@ember/test-helpers/dom/click.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { getWindowOrElement } from './-get-window-or-element';
import fireEvent from './fire-event';
import { __focus__ } from './focus';
import settled from '../settled';
import isFocusable from './-is-focusable';
import { Promise } from '../-utils';
import isFormControl from './-is-form-control';
import Target from './-target';
import Target, { isWindow } from './-target';
import { log } from '@ember/test-helpers/dom/-logging';
import { runHooks, registerHook } from '../-internal/helper-hooks';
import { __blur__ } from './blur';

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

if (isFocusable(element)) {
if (!isWindow(element)) {
__focus__(element);
}

Expand Down
5 changes: 2 additions & 3 deletions addon-test-support/@ember/test-helpers/dom/double-click.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { getWindowOrElement } from './-get-window-or-element';
import fireEvent from './fire-event';
import { __focus__ } from './focus';
import settled from '../settled';
import isFocusable from './-is-focusable';
import { Promise } from '../-utils';
import { DEFAULT_CLICK_OPTIONS } from './click';
import Target from './-target';
import Target, { isWindow } from './-target';
import { log } from '@ember/test-helpers/dom/-logging';
import isFormControl from './-is-form-control';
import { runHooks, registerHook } from '../-internal/helper-hooks';
Expand All @@ -26,7 +25,7 @@ export function __doubleClick__(
): void {
fireEvent(element, 'mousedown', options);

if (isFocusable(element)) {
if (!isWindow(element)) {
__focus__(element);
}

Expand Down
28 changes: 20 additions & 8 deletions addon-test-support/@ember/test-helpers/dom/focus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,29 @@ registerHook('focus', 'start', (target: Target) => {
@param {Element} element the element to trigger events on
*/
export function __focus__(element: HTMLElement | Element | Document | SVGElement): void {
const previousFocusedElement =
document.activeElement &&
document.activeElement !== element &&
isFocusable(document.activeElement)
? document.activeElement
: null;

// fire __blur__ manually with the null relatedTarget when the target is not focusable
// and there was a previously focused element
if (!isFocusable(element)) {
throw new Error(`${element} is not focusable`);
if (previousFocusedElement) {
__blur__(previousFocusedElement, null);
}

return;
}

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

// fire __blur__ manually with the correct relatedTarget when the browser is not
// already in focus and there was a previously focused element
if (
document.activeElement &&
document.activeElement !== element &&
isFocusable(document.activeElement) &&
browserIsNotFocused
) {
__blur__(document.activeElement, element);
if (previousFocusedElement && browserIsNotFocused) {
__blur__(previousFocusedElement, element);
}

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

if (!isFocusable(element)) {
throw new Error(`${element} is not focusable`);
}

__focus__(element);

return settled();
Expand Down
41 changes: 41 additions & 0 deletions tests/unit/dom/click-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,47 @@ module('DOM Helper: click', function (hooks) {
assert.verifySteps(['mousedown', 'mouseup', 'click']);
});
});

module('focusable and non-focusable elements interaction', function () {
test('clicking on non-focusable element triggers blur on active element', async function (assert) {
element = document.createElement('div');

insertElement(element);

const focusableElement = buildInstrumentedElement('input');

await click(focusableElement);
await click(element);

assert.verifySteps(['mousedown', 'focus', 'focusin', 'mouseup', 'click', 'blur', 'focusout']);
});

test('clicking on focusable element triggers blur on active element', async function (assert) {
element = document.createElement('input');

insertElement(element);

const focusableElement = buildInstrumentedElement('input');

await click(focusableElement);
await click(element);

assert.verifySteps(['mousedown', 'focus', 'focusin', 'mouseup', 'click', 'blur', 'focusout']);
});

test('clicking on non-focusable element does not trigger blur on non-focusable active element', async function (assert) {
element = document.createElement('div');

insertElement(element);

const nonFocusableElement = buildInstrumentedElement('div');

await click(nonFocusableElement);
await click(element);

assert.verifySteps(['mousedown', 'mouseup', 'click']);
});
});
});

module('DOM Helper: click with window', function () {
Expand Down
73 changes: 73 additions & 0 deletions tests/unit/dom/double-click-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,79 @@ module('DOM Helper: doubleClick', function (hooks) {
]);
});
});

module('focusable and non-focusable elements interaction', function () {
test('cdouble-licking on non-focusable element triggers blur on active element', async function (assert) {
element = document.createElement('div');

insertElement(element);

const focusableElement = buildInstrumentedElement('input');

await doubleClick(focusableElement);
await doubleClick(element);

assert.verifySteps([
'mousedown',
'focus',
'focusin',
'mouseup',
'click',
'mousedown',
'mouseup',
'click',
'dblclick',
'blur',
'focusout',
]);
});

test('double-clicking on focusable element triggers blur on active element', async function (assert) {
element = document.createElement('input');

insertElement(element);

const focusableElement = buildInstrumentedElement('input');

await doubleClick(focusableElement);
await doubleClick(element);

assert.verifySteps([
'mousedown',
'focus',
'focusin',
'mouseup',
'click',
'mousedown',
'mouseup',
'click',
'dblclick',
'blur',
'focusout',
]);
});

test('double-clicking on non-focusable element does not trigger blur on non-focusable active element', async function (assert) {
element = document.createElement('div');

insertElement(element);

const nonFocusableElement = buildInstrumentedElement('div');

await doubleClick(nonFocusableElement);
await doubleClick(element);

assert.verifySteps([
'mousedown',
'mouseup',
'click',
'mousedown',
'mouseup',
'click',
'dblclick',
]);
});
});
});

module('DOM Helper: doubleClick with window', function () {
Expand Down
61 changes: 61 additions & 0 deletions tests/unit/dom/tap-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,65 @@ module('DOM Helper: tap', function (hooks) {
assert.rejects(tap(element), new Error('Can not `tap` disabled [object HTMLInputElement]'));
});
});

module('focusable and non-focusable elements interaction', function () {
test('tapping on non-focusable element triggers blur on active element', async function (assert) {
element = document.createElement('div');

insertElement(element);

const focusableElement = buildInstrumentedElement('input');

await tap(focusableElement);
await tap(element);

assert.verifySteps([
'touchstart',
'touchend',
'mousedown',
'focus',
'focusin',
'mouseup',
'click',
'blur',
'focusout',
]);
});

test('tapping on focusable element triggers blur on active element', async function (assert) {
element = document.createElement('input');

insertElement(element);

const focusableElement = buildInstrumentedElement('input');

await tap(focusableElement);
await tap(element);

assert.verifySteps([
'touchstart',
'touchend',
'mousedown',
'focus',
'focusin',
'mouseup',
'click',
'blur',
'focusout',
]);
});

test('tapping on non-focusable element does not trigger blur on non-focusable active element', async function (assert) {
element = document.createElement('div');

insertElement(element);

const nonFocusableElement = buildInstrumentedElement('div');

await tap(nonFocusableElement);
await tap(element);

assert.verifySteps(['touchstart', 'touchend', 'mousedown', 'mouseup', 'click']);
});
});
});

0 comments on commit e47ea7b

Please sign in to comment.