From d58e3de9b28a36df081559e4d8f1efe16a70b4ce Mon Sep 17 00:00:00 2001 From: Gabor Babicz Date: Mon, 3 Jan 2022 08:46:05 +0100 Subject: [PATCH] Add option to disable focus trap --- README.md | 12 ++++ addon/components/modal.js | 8 ++- addon/services/modals.js | 1 + .../configuration/focus-trap-test.js | 58 +++++++++++++++++++ 4 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 tests/integration/configuration/focus-trap-test.js diff --git a/README.md b/README.md index cafcb4a4..7550029f 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,18 @@ EPM will ensure to [focus the first "tabbable element" by default](https://www.w If no focusable element is present, focus will be applied on the currently visible auto-generated container for the current modal. +To disable focus trap completely, override the default `Modals` service by +extending it, place it to `app/services/modals.js`, then create a property +called `disableFocusTrap` set to `true`: + +```js +import BaseModalsService from 'ember-promise-modals/services/modals'; + +export default class ModalsService extends BaseModalsService { + disableFocusTrap = true; +} +``` + ## Testing This addon provides a test helper function that reduces the timing for the CSS transitions to near zero to speed up your tests. diff --git a/addon/components/modal.js b/addon/components/modal.js index f145b4f1..8ee97513 100644 --- a/addon/components/modal.js +++ b/addon/components/modal.js @@ -30,7 +30,7 @@ export default Component.extend({ didInsertElement() { this._super(...arguments); - let { clickOutsideDeactivates } = this.modals; + let { clickOutsideDeactivates, disableFocusTrap } = this.modals; let element = document.getElementById(this.modalElementId); let options = { clickOutsideDeactivates, @@ -44,8 +44,10 @@ export default Component.extend({ }, }; - this.focusTrap = createFocusTrap(element, options); - this.focusTrap.activate(); + if (!disableFocusTrap) { + this.focusTrap = createFocusTrap(element, options); + this.focusTrap.activate(); + } this.fadeOutEnd = ({ target, animationName }) => { this.modals._onModalAnimationEnd(); diff --git a/addon/services/modals.js b/addon/services/modals.js index d078f56b..af7f3f46 100644 --- a/addon/services/modals.js +++ b/addon/services/modals.js @@ -12,6 +12,7 @@ export default Service.extend({ top: alias('_stack.lastObject'), clickOutsideDeactivates: true, + disableFocusTrap: false, init() { this._super(...arguments); diff --git a/tests/integration/configuration/focus-trap-test.js b/tests/integration/configuration/focus-trap-test.js new file mode 100644 index 00000000..ba96216f --- /dev/null +++ b/tests/integration/configuration/focus-trap-test.js @@ -0,0 +1,58 @@ +import { render, settled } from '@ember/test-helpers'; +import focus from '@ember/test-helpers/dom/focus'; +import { setupRenderingTest } from 'ember-qunit'; +import { module, test } from 'qunit'; + +import Component from '@ember/component'; + +import hbs from 'htmlbars-inline-precompile'; + +import { setupPromiseModals } from 'ember-promise-modals/test-support'; + +module('Configuration | focus trap', function (hooks) { + setupRenderingTest(hooks); + setupPromiseModals(hooks); + + let renderAndOpenModal = async context => { + await render(hbs` + + + `); + + let modals = context.owner.lookup('service:modals'); + modals.open('foo'); + + await settled(); + }; + + hooks.beforeEach(function () { + this.owner.register( + 'component:foo', + Component.extend({ + tagName: '', + layout: hbs``, + }), + ); + }); + + test('focus trap is enabled', async function (assert) { + await renderAndOpenModal(this); + + await focus('[data-test-outside-button]'); + + assert.dom('[data-test-outside-button]').isNotFocused(); + assert.dom('[data-test-inside-button]').isFocused(); + }); + + test('focus trap is disabled', async function (assert) { + let modals = this.owner.lookup('service:modals'); + modals.set('disableFocusTrap', true); + + await renderAndOpenModal(this); + + await focus('[data-test-outside-button]'); + + assert.dom('[data-test-outside-button]').isFocused(); + assert.dom('[data-test-inside-button]').isNotFocused(); + }); +});