Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 0 additions & 51 deletions app/javascript/app/components/focus-trap-proxy.js

This file was deleted.

10 changes: 2 additions & 8 deletions app/javascript/app/components/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import focusTrapProxy from './focus-trap-proxy';
import modal from './modal';
import Modal from './modal';

window.LoginGov = window.LoginGov || {};
const { LoginGov } = window;
const trapModal = modal(focusTrapProxy);

LoginGov.Modal = trapModal;

export { trapModal as Modal };
window.LoginGov.Modal = Modal;
69 changes: 34 additions & 35 deletions app/javascript/app/components/modal.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,47 @@
import 'classlist.js';
import { createFocusTrap } from 'focus-trap';
import Events from '../utils/events';

const STATES = {
HIDE: 'hide',
SHOW: 'show',
};

function modal(focusTrap) {
return class extends Events {
constructor(options) {
super();
class Modal extends Events {
constructor(options) {
super();

this.el = document.querySelector(options.el);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all indentation changes. Suggest to review with "Hide whitespace changes":

image

this.shown = false;
this.trap = focusTrap(this.el, { escapeDeactivates: false });
}

toggle() {
if (this.shown) {
this.hide();
} else {
this.show();
}
}

show(target) {
this.setElementVisibility(target, true);
this.emit(STATES.SHOW);
}

hide(target) {
this.setElementVisibility(target, false);
this.emit(STATES.HIDE);
}

setElementVisibility(target = null, showing) {
const el = target || this.el;
this.el = document.querySelector(options.el);
this.shown = false;
this.trap = createFocusTrap(this.el, { escapeDeactivates: false });
}

this.shown = showing;
el.classList[showing ? 'remove' : 'add']('display-none');
document.body.classList[showing ? 'add' : 'remove']('modal-open');
this.trap[showing ? 'activate' : 'deactivate']();
toggle() {
if (this.shown) {
this.hide();
} else {
this.show();
}
};
}

show(target) {
this.setElementVisibility(target, true);
this.emit(STATES.SHOW);
}

hide(target) {
this.setElementVisibility(target, false);
this.emit(STATES.HIDE);
}

setElementVisibility(target = null, showing) {
const el = target || this.el;

this.shown = showing;
el.classList[showing ? 'remove' : 'add']('display-none');
document.body.classList[showing ? 'add' : 'remove']('modal-open');
this.trap[showing ? 'activate' : 'deactivate']();
}
}

export default modal;
export default Modal;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useRef, useEffect, useCallback } from 'react';
import createFocusTrap from 'focus-trap';
import { createFocusTrap } from 'focus-trap';
import useI18n from '../hooks/use-i18n';
import useAsset from '../hooks/use-asset';

Expand Down
2 changes: 1 addition & 1 deletion app/javascript/packages/document-capture/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"version": "1.0.0",
"dependencies": {
"focus-trap": "^2.3.0",
"focus-trap": "^6.0.1",
"react": "^16.13.1"
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"classlist.js": "^1.1.20150312",
"cleave.js": "^1.5.3",
"clipboard": "^1.6.1",
"focus-trap": "^2.3.0",
"focus-trap": "^6.0.1",
"hint.css": "^2.3.2",
"identity-style-guide": "^2.1.5",
"intl-tel-input": "^16.0.7",
Expand Down
37 changes: 0 additions & 37 deletions spec/javascripts/app/components/focus-trap-proxy_spec.js

This file was deleted.

90 changes: 90 additions & 0 deletions spec/javascripts/app/components/modal-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { waitFor } from '@testing-library/dom';
import BaseModal from '../../../../app/javascript/app/components/modal';
import { useCleanDOM } from '../../support/dom';
import { useSandbox } from '../../support/sinon';

describe('components/modal', () => {
useCleanDOM();
const sandbox = useSandbox();

class Modal extends BaseModal {
static instances = [];

constructor(...args) {
super(...args);
Modal.instances.push(this);
}
}

function createModalContainer(id = 'modal') {
const container = document.createElement('div');
container.id = id;
container.className = 'modal display-none';
container.innerHTML = `
<div class="modal-backdrop">
<div class="px2 py4 modal" role="dialog">
<div class="modal-center">
<div class="modal-content">
<p>Do action?</p>
<button type="button">Yes</button>
<button type="button">No</button>
</div>
</div>
</div>
</div>
`;

return container;
}

beforeEach(() => {
document.body.appendChild(createModalContainer());
});

afterEach(() => {
Modal.instances.forEach((instance) => {
instance.off();
if (instance.shown) {
instance.hide();
}
});
});

it('shows with initial focus', async () => {
const onShow = sandbox.stub();
const modal = new Modal({ el: '#modal' });
modal.on('show', onShow);
modal.show();

await waitFor(() => expect(document.activeElement.nodeName).to.equal('BUTTON'));
expect(onShow.called).to.be.true();
expect(document.activeElement.textContent).to.equal('Yes');
const container = document.activeElement.closest('#modal');
expect(container.classList.contains('display-none')).to.be.false();
expect(document.body.classList.contains('modal-open')).to.be.true();
});

it('allows interaction in most recently activated focus trap', async () => {
document.body.appendChild(createModalContainer('modal2'));
const modal = new Modal({ el: '#modal' });
const modal2 = new Modal({ el: '#modal2' });

modal.show();

await waitFor(() => expect(document.activeElement.closest('#modal')).to.be.ok());

modal2.show();

await waitFor(() => expect(document.activeElement.closest('#modal2')).to.be.ok());

await new Promise((resolve) => {
document.activeElement.addEventListener('click', (event) => {
if (!event.defaultPrevented) {
resolve();
}
});

document.activeElement.click();
});
});
});
2 changes: 2 additions & 0 deletions spec/javascripts/spec_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ global.window = dom.window;
global.window.fetch = () => Promise.reject(new Error('Fetch must be stubbed'));
global.navigator = window.navigator;
global.document = window.document;
global.Document = window.Document;
global.Element = window.Element;
global.getComputedStyle = window.getComputedStyle;
global.self = window;

Expand Down
18 changes: 9 additions & 9 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4206,12 +4206,12 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"

focus-trap@^2.3.0:
version "2.4.6"
resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-2.4.6.tgz#332b475b317cec6a4a129f5307ce7ebc0da90b40"
integrity sha512-vWZTPtBU6pBoyWZDRZJHkXsyP2ZCZBHE3DRVXnSVdQKH/mcDtu9S5Kz8CUDyIqpfZfLEyI9rjKJLnc4Y40BRBg==
focus-trap@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-6.0.1.tgz#f90725e4bb62ddab16e685b02b43b823858a4c0a"
integrity sha512-BOksLMPK/jlXD389jYPlZHAqiDdy9W63EBQfVIouME8s3UZsCEmq3NA53qt30S4i72sRcDjc1FHtast0TmqhRA==
dependencies:
tabbable "^1.0.3"
tabbable "^5.0.0"

follow-redirects@^1.0.0:
version "1.12.1"
Expand Down Expand Up @@ -8897,10 +8897,10 @@ symbol-tree@^3.2.4:
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==

tabbable@^1.0.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.3.tgz#0e4ee376f3631e42d7977a074dbd2b3827843081"
integrity sha512-nOWwx35/JuDI4ONuF0ZTo6lYvI0fY0tZCH1ErzY2EXfu4az50ZyiUX8X073FLiZtmWUVlkRnuXsehjJgCw9tYg==
tabbable@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.0.0.tgz#862b6f33a625da45d7c648cff0262dab453d8b0c"
integrity sha512-+TJTMpkHRCWkMGczHHVEfzBYCsVOiBjd3vle55AT4H299BhdJDLFqcYmr7S6kt5EGhT8gAywSC5gPUBDNvtl7w==

table@^5.2.3:
version "5.4.6"
Expand Down