Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
c3c60e4
basic modal
jaredcunha Feb 1, 2021
aafd994
multiple modals proof of concept
jaredcunha Feb 2, 2021
1e739da
scrolling behavior
jaredcunha Feb 2, 2021
559258f
click overlay to close
jaredcunha Feb 2, 2021
e1ca2f0
fix scroll
jaredcunha Feb 2, 2021
ab11092
retarget close button
jaredcunha Feb 2, 2021
db73367
modal working
jaredcunha Feb 3, 2021
8822d6b
getting close
jaredcunha Feb 4, 2021
dcc5b70
remove unused function
jaredcunha Feb 4, 2021
7626043
use event lisetener once
jaredcunha Feb 4, 2021
4d66369
designed modal
jaredcunha Feb 4, 2021
1f43ab7
set timeout for safari
jaredcunha Feb 4, 2021
8cee53b
support multilple openers
jaredcunha Feb 5, 2021
ef3130a
protect multiple opener from escape key
jaredcunha Feb 5, 2021
b67ffe6
prevent the escape key listeners when closed
jaredcunha Feb 5, 2021
0fad114
clean up conditional stuff
jaredcunha Feb 5, 2021
ba7a8e2
fix some es lint errors
jaredcunha Feb 5, 2021
2184504
remove unused var
jaredcunha Feb 5, 2021
9492660
remove dupe id
jaredcunha Feb 5, 2021
49d9185
clean up js
jaredcunha Feb 5, 2021
9f0162a
update modal examples
jaredcunha Feb 5, 2021
a25686a
prevent error if no focusable items available
jaredcunha Feb 5, 2021
f014e1f
update the modal example
jaredcunha Feb 5, 2021
8b055b1
add scrollbar width utility to prevent content shift
jaredcunha Feb 5, 2021
9870431
fix padding on main nav, use consistent implementation
jaredcunha Feb 5, 2021
287bb80
fix focus trap and tab order issues
jaredcunha Feb 8, 2021
2326de6
use data attribute for closers
jaredcunha Feb 8, 2021
0118093
use overlay instead of scrim
jaredcunha Feb 8, 2021
47cf96e
button functions without javascript
jaredcunha Feb 8, 2021
a392ea8
prevent default using text links
jaredcunha Feb 8, 2021
c2033d9
IE11 forEach fix
jaredcunha Feb 8, 2021
f119ead
force user to take action
jaredcunha Feb 8, 2021
da303df
remove console log
jaredcunha Feb 8, 2021
681bd6c
allow select text in forced-action modals
jaredcunha Feb 8, 2021
791311d
accessibility updates
jaredcunha Feb 9, 2021
3575d89
better touch support
jaredcunha Feb 9, 2021
701771d
address safari issues with VoiceOver
jaredcunha Feb 9, 2021
c3196f4
use button element
jaredcunha Feb 9, 2021
e4efcd2
fix build errors
jaredcunha Feb 9, 2021
34555f4
remove duplicate id
jaredcunha Feb 9, 2021
135f1de
prevent flicker with uswds-init
jaredcunha Feb 10, 2021
9e1ddb1
modal examples
jaredcunha Feb 10, 2021
5e3b2e7
make tests happy
jaredcunha Feb 10, 2021
ac880cd
use modal footer instead of actions
jaredcunha Feb 10, 2021
eacae16
change data attribute for openers
jaredcunha Feb 10, 2021
f15cd06
cleanup unused attributes
jaredcunha Feb 10, 2021
5c1e220
replace link href on openers
jaredcunha Feb 11, 2021
f064130
use preventDefault instead href rewrite
jaredcunha Feb 11, 2021
4c8fa8d
remove unused margin
jaredcunha Feb 11, 2021
b0bbe65
add unit tests
jaredcunha Feb 11, 2021
d447b70
modal theme settings
jaredcunha Feb 11, 2021
e3abfb8
subtle fade in
jaredcunha Feb 11, 2021
216b47c
better naming convention
jaredcunha Feb 12, 2021
5ec62ff
remove test modal from docs page
jaredcunha Feb 12, 2021
1b006f4
fix safari button propagation issues
jaredcunha Feb 12, 2021
b0cbe47
x to close
jaredcunha Feb 16, 2021
aa18c7a
js cleanup
jaredcunha Feb 16, 2021
fd11dd9
for nav shift, handle existing padding
jaredcunha Feb 16, 2021
01ce50e
clean up
jaredcunha Feb 17, 2021
0086676
ran prettier
jaredcunha Feb 18, 2021
b82785f
update modal examples
jaredcunha Feb 18, 2021
72eccbe
modal example updates
jaredcunha Feb 19, 2021
f77c0cf
revert docs page
jaredcunha Feb 19, 2021
ee5140a
typefix - keep old if else statement because mobile safari
jaredcunha Feb 19, 2021
6374b17
punctuation and text fixes
jaredcunha Feb 19, 2021
061e223
spell with
jaredcunha Feb 19, 2021
974404a
Adjust test spacing and fix typo.
Mar 3, 2021
1d8ae19
Add modal package to styles.scss.
Mar 3, 2021
4489f35
Sort components alphabetically.
Mar 3, 2021
989e0bc
Sort modal settings alphabetically.
Mar 3, 2021
a07b605
Test: Add modal to kitchen sink template.
Mar 4, 2021
65b8d15
Merge branch 'uswds-2.11.0' into accelerator-3936/modal
thisisdano Mar 4, 2021
914b45e
Merge branch 'develop' into accelerator-3936/modal
Mar 5, 2021
04ef6e0
Be more clear that the button opens a modal window
thisisdano Mar 9, 2021
bf3dfaf
Use token for overlay background.
Mar 9, 2021
8d91167
Consolidate modal content styles and delete reverse class.
Mar 9, 2021
a21ce77
Add modal active class to prevent conflict with nav on resize.
Mar 9, 2021
d333a97
Remove star from selector.
Mar 10, 2021
95001a0
Use tokens for modal widths.
Mar 10, 2021
7e0a9d8
Use "top" for modal z-index.
Mar 10, 2021
c6bcc42
Templatize modal component.
Mar 10, 2021
36ef183
Merge branch 'uswds-2.11.0' into accelerator-3936/modal
thisisdano Mar 10, 2021
c000f04
Use explicit `example` IDs
thisisdano Mar 10, 2021
1470e9d
[see details]
thisisdano Mar 10, 2021
57ebd58
Remove offset from close button
thisisdano Mar 10, 2021
5c52b87
Properly format forced action modal
thisisdano Mar 10, 2021
fda5332
Add the modals to a better spot in the kitchen sink
thisisdano Mar 10, 2021
daa5d95
Use a darker overlay to improve contrast to ~AAA
thisisdano Mar 10, 2021
40fb2dd
Speed up project easing a bit
thisisdano Mar 10, 2021
babcbb9
Set text color explicitly so always shows proper text color on white bg
thisisdano Mar 11, 2021
f34606b
Darken nav overlay to match modal
thisisdano Mar 11, 2021
0a7baa5
Be more resilient with setting the modal text color
thisisdano Mar 11, 2021
8b93ce6
Set text color in card
thisisdano Mar 11, 2021
3eea534
Set explicit text color in footer
thisisdano Mar 11, 2021
a9e049c
Merge branch 'uswds-2.11.0' into accelerator-3936/modal
thisisdano Mar 11, 2021
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
113 changes: 113 additions & 0 deletions spec/unit/modal/modal.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const assert = require("assert");
const fs = require("fs");
const path = require("path");

const modal = require("../../../src/js/components/modal");

const TEMPLATE = fs.readFileSync(path.join(__dirname, "template.html"));

describe("Modal window", () => {
const { body } = document;

let closeButton;
let modalWrapper;
let modalWindow;
let openButton1;
let openButton2;
let overlay;

const isVisible = el => el.classList.contains("is-visible");

beforeEach(() => {
body.innerHTML = TEMPLATE;
modal.on();
closeButton = body.querySelector("#close-button");
modalWrapper = body.querySelector(".usa-modal-wrapper");
modalWindow = body.querySelector(".usa-modal");
overlay = body.querySelector(".usa-modal-overlay");
openButton1 = body.querySelector("#open-button1");
openButton2 = body.querySelector("#open-button2");
});

afterEach(() => {
body.innerHTML = "";
body.className = "";
modal.off();
});

describe("Builds the modal HTML", () => {

it('creates new parent elements', () => {
assert.ok(modalWindow, "creates inner div");
assert.ok(overlay, "creates the overlay");
assert.ok(modalWrapper, "creates the outer div");
});

it('adds role="dialog" to modal parent', () => {
assert.strictEqual(modalWrapper.getAttribute("role"), "dialog");
});

it('moves aria-lableledby, aria-describedby, and id to the parent', () => {
assert.strictEqual(modalWindow.hasAttribute("aria-describedby"), false);
assert.strictEqual(modalWindow.hasAttribute("aria-labelledby"), false);
assert.strictEqual(modalWindow.hasAttribute("id"), false);
assert.strictEqual(modalWrapper.hasAttribute("aria-describedby"), true);
assert.strictEqual(modalWrapper.hasAttribute("aria-labelledby"), true);
assert.strictEqual(modalWrapper.getAttribute("id"), "modal");
})

it('sets tabindex="-1" to the modal window', () => {
assert.strictEqual(modalWindow.getAttribute("tabindex"), "-1");
});

it('moves the modal to the bottom of the DOM', () => {
assert.strictEqual(body.lastElementChild.classList.contains("usa-modal-wrapper"), true);
});

it('adds role="button" to any <a> opener, but not <button>', () => {
assert.strictEqual(openButton1.getAttribute("role"), "button");
assert.strictEqual(openButton2.hasAttribute("role"), false);
})

it('adds aria-controls to each opener', () => {
assert.strictEqual(openButton1.getAttribute("aria-controls"), "modal");
assert.strictEqual(openButton2.getAttribute("aria-controls"), "modal");
})
});

describe("When opened", () => {
beforeEach(() => {
openButton1.click();
});

it("makes the modal visible", () => {
assert.strictEqual(isVisible(modalWrapper), true);
});

it("focuses the modal window when opened", () => {
assert.strictEqual(document.activeElement, modalWindow);
});
});

describe("When closing", () => {

beforeEach(() => {
openButton2.click();
});

it("hides the modal when close button is clicked", () => {
closeButton.click();
assert.strictEqual(isVisible(modalWrapper), false);
});

it("closes the modal when the overlay is clicked", () => {
overlay.click();
assert.strictEqual(isVisible(modalWrapper), false);
});

it("sends focus to the element that opened it", () => {
closeButton.click();
assert.strictEqual(document.activeElement, openButton2);
});
});
});
37 changes: 37 additions & 0 deletions spec/unit/modal/template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div aria-hidden="true" class="usa-sr-only" id="stays-hidden">
Needs to stay set to aria-hidden="true" when modals are toggled
</div>

<div id="other-content">
</div>

<div id="original-container">
<div>
<a id="open-button1" href="#modal" class="usa-button" aria-controls="modal" data-open-modal>Open modal</a>
<button id="open-button2" type="button" class="usa-button" aria-controls="modal" data-open-modal>Open modal</button>
<div class="usa-modal" id="modal" aria-labelledby="modalheading" aria-describedby="describe">
<div class="usa-modal__content">
<div class="usa-modal__main">
<h2 class="usa-modal__heading" id="modal-sm-heading">You have unsaved changes</h2>
<div id="describe" class="usa-prose">
<p>Your changes will be lost if you leave this page without saving. Are you sure you want to continue?</p>
</div>

<div class="usa-modal__footer">
<ul class="usa-button-group">
<li class="usa-button-group__item">
<button type="button" class="usa-button" data-close-modal>Continue</button>
</li>
</ul>
</div>
</div>
<button id="close-button" class="usa-button usa-modal__close" aria-label="close this window" data-close-modal>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="{{ uswds.path }}/img/sprite.svg#close"></use>
</svg>
Close
</button>
</div>
</div>
</div>
</div>
68 changes: 68 additions & 0 deletions src/components/modal/modal.config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
label: Modal
status: ready
preview: "@uswds-framed"

context:
id: "example-modal-1"
modifier: ""
describedby: "modal-1-description"
trigger:
label: Open default modal
header:
label: Are you sure you want to continue?
id: "modal-1-heading"
description:
label: |
<p id="modal-1-description">You have unsaved changes that will be lost.</p>
footer:
- action:
class: "usa-button"
label: Continue without saving
- action:
class: "usa-button usa-button--unstyled padding-105 text-center"
label: Go back
close_button:
aria_label: "Close this window"
label:
icon_type: close

variants:
- name: large modal
context:
id: "example-modal-2"
modifier: "usa-modal--lg"
describedby: "modal-2-description"
trigger:
label: Open large modal
header:
label: Are you sure you want to continue?
id: "modal-2-heading"
description:
label: |
<p id="modal-2-description">You have unsaved changes that will be lost.</p>
footer:
- action:
label: "Continue without saving"
- action:
class: "usa-button usa-button--unstyled padding-105 text-center"
label: "Go back"
- name: forced action
context:
id: "example-modal-3"
modifier:
forced: true
describedby: "modal-3-description"
trigger:
label: Open modal with forced action
header:
label: Your session will end soon.
id: "modal-3-heading"
description:
label: |
<p id="modal-3-description">You’ve been inactive for too long. Please choose to stay signed in or sign out. Otherwise, you’ll be signed out automatically in 5 minutes.</p>
footer:
- action:
label: "Yes, stay signed in"
- action:
class: "usa-button usa-button--unstyled padding-105 text-center"
label: "Sign out"
46 changes: 46 additions & 0 deletions src/components/modal/modal.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% set close_icon %}
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="{{ uswds.path }}/img/sprite.svg#{{ close_button.icon_type | default("close") }}"></use>
</svg>
{% endset %}

<div class="margin-y-3">
<a href="#{{ id }}" class="usa-button" aria-controls="{{ id }}" data-open-modal>{{ trigger.label }}</a>

<div class="usa-modal {{ modifier }}" id="{{ id }}" aria-labelledby="{{ header.label }}" aria-describedby="{{ describedby }}"{% if forced %} data-force-action{% endif -%}>
<div class="usa-modal__content">
<div class="usa-modal__main">
{% if header.label %}
<h2 class="usa-modal__heading" id="{{ header.id }}">
{{ header.label }}
</h2>
{% endif %}
{% if description.label %}
<div class="usa-prose">
{{ description.label | safe }}
</div>
{% endif %}
{% if footer %}
<div class="usa-modal__footer">
<ul class="usa-button-group">
{% for action in footer %}
<button type="button" class="{{ action.class | default("usa-button") }}" data-close-modal>
{{ action.label }}
</button>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
{%- if not forced -%}
<button class="usa-button usa-modal__close" aria-label="{{ close_button.aria_label }}" data-close-modal>
{% if close_button.label %}
{{ close_button.label }}
{% else %}
{{ close_icon | safe }}
{% endif %}
</button>
{%- endif -%}
</div>
</div>
</div>
6 changes: 5 additions & 1 deletion src/components/test/kitchen-sink.njk
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<div class="usa-section">
<div class="grid-container">
{% render '@breadcrumb--metadata' %}
<div class="grid-row grid-gap">
<div class="grid-row grid-gap margin-top-4">
<div class="desktop:grid-col-3 usa-layout-docs__sidenav">
{% render '@sidenav', {sidenav: sidenav} %}
</div>
Expand All @@ -19,6 +19,10 @@
<p class="usa-content">
{{ page.body | safe }}

{% render '@modal' %}
{% render '@modal--large-modal' %}
{% render '@modal--forced-action' %}

<ul>
{% for list in page.lists %}
<li>{{ list }}</li>
Expand Down
2 changes: 2 additions & 0 deletions src/js/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const comboBox = require("./combo-box");
const fileInput = require("./file-input");
const footer = require("./footer");
const inputPrefixSuffix = require("./input-prefix-suffix");
const modal = require("./modal");
const navigation = require("./navigation");
const password = require("./password");
const search = require("./search");
Expand All @@ -26,6 +27,7 @@ module.exports = {
fileInput,
footer,
inputPrefixSuffix,
modal,
navigation,
password,
search,
Expand Down
Loading