UNPKG

29.6 kBMarkdownView Raw
1<!--docs:
2title: "Dialogs"
3layout: detail
4section: components
5excerpt: "Modal dialogs."
6iconId: dialog
7path: /catalog/dialogs/
8-->
9
10# Dialogs
11
12Dialogs inform users about a task and can contain critical information, require decisions, or involve multiple tasks.
13
14There are four types of dialogs:
15
161. [Alert](#alert-dialog)
171. [Simple](#simple-dialog)
181. [Confirmation](#confirmation-dialog)
191. [Full-screen](#full-screen-dialog)
20
21## Using dialogs
22
23A dialog is a type of modal window that appears in front of app content to provide critical information or ask for a decision. Dialogs disable all app functionality when they appear, and remain on screen until confirmed, dismissed, or a required action has been taken.
24
25Dialogs are purposefully interruptive, so they should be used sparingly.
26
27For additional guidance, refer to the [Material guidelines](https://material.io/go/design-dialogs).
28
29### Installation
30
31```
32npm install @material/dialog
33```
34
35### Styles
36
37```scss
38@use "@material/dialog";
39
40@include dialog.core-styles;
41```
42
43**Note: Styles for any components you intend to include within dialogs (e.g. List, Checkboxes, etc.) must also be imported.**
44
45### JavaScript Instantiation
46
47```js
48import {MDCDialog} from '@material/dialog';
49const dialog = new MDCDialog(document.querySelector('.mdc-dialog'));
50```
51
52**Note: See [Importing the JS component](../../docs/importing-js.md) for more information on how to import JavaScript.**
53
54MDC Dialog makes no assumptions about what will be added to the `mdc-dialog__content` element. Any list, checkboxes,
55etc. must also be instantiated. If your dialog contains any layout-sensitive components, you should wait until
56`MDCDialog:opened` is emitted to instantiate them (or call `layout` on them) so that the dialog's transition finishes
57first.
58
59For example, to instantiate an MDC List inside of a simple or confirmation dialog:
60
61```js
62import {MDCList} from '@material/list';
63const list = new MDCList(document.querySelector('.mdc-dialog .mdc-deprecated-list'));
64
65dialog.listen('MDCDialog:opened', () => {
66 list.layout();
67});
68```
69
70**Note: Mispositioned or incorrectly-sized elements (e.g. ripples, floating labels, notched outlines) are a strong
71indication that child components are being instantiated before the dialog has finished opening.**
72
73### Making dialogs accessible
74
75#### Using `aria-hidden` as a fallback for `aria-modal`
76
77`aria-modal` is part of the ARIA 1.1 specification, and indicates to screen readers that they should confine themselves to a single element. We recommend adding `aria-modal="true"` to the root element of its DOM structure.
78
79However, not all user agents and screen readers properly interpret this attribute.
80
81The fallback is to use `aria-hidden` using `aria-hidden="true"` to all static content beneath the dialog when the dialog is open. This will be easiest to achieve if all non-modal elements are under a single common ancestor under the body, so that `aria-hidden` can be applied to one element.
82
83```js
84dialog.listen('MDCDialog:opened', function() {
85 // Assuming contentElement references a common parent element with the rest of the page's content
86 contentElement.setAttribute('aria-hidden', 'true');
87});
88dialog.listen('MDCDialog:closing', function() {
89 contentElement.removeAttribute('aria-hidden');
90});
91```
92
93**Note: The example above intentionally listens to the opened (not opening) event and the closing (not closed) event in order to avoid additional jumping between elements by screen readers due to one element becoming hidden before others become visible.**
94
95## Alert dialog
96
97Alert dialogs interrupt users with urgent information, details, or actions.
98
99<img src="images/alert-dialog.png" alt="Alert dialog: discard" width="250px">
100
101### Alert dialog example
102
103```html
104<div class="mdc-dialog">
105 <div class="mdc-dialog__container">
106 <div class="mdc-dialog__surface"
107 role="alertdialog"
108 aria-modal="true"
109 aria-labelledby="my-dialog-title"
110 aria-describedby="my-dialog-content">
111 <div class="mdc-dialog__content" id="my-dialog-content">
112 Discard draft?
113 </div>
114 <div class="mdc-dialog__actions">
115 <button type="button" class="mdc-button mdc-dialog__button" data-mdc-dialog-action="cancel">
116 <div class="mdc-button__ripple"></div>
117 <span class="mdc-button__label">Cancel</span>
118 </button>
119 <button type="button" class="mdc-button mdc-dialog__button" data-mdc-dialog-action="discard">
120 <div class="mdc-button__ripple"></div>
121 <span class="mdc-button__label">Discard</span>
122 </button>
123 </div>
124 </div>
125 </div>
126 <div class="mdc-dialog__scrim"></div>
127</div>
128```
129
130## Simple dialog
131
132Simple dialogs can display items that are immediately actionable when selected. They don’t have text buttons.
133
134As simple dialogs are interruptive, they should be used sparingly. Alternatively, dropdown menus provide options in a non-modal, less disruptive way.
135
136<img src="images/simple-dialog.png" alt="Simple dialog: selection" width="250px">
137
138### Simple dialog example
139
140```html
141<div class="mdc-dialog">
142 <div class="mdc-dialog__container">
143 <div class="mdc-dialog__surface"
144 role="alertdialog"
145 aria-modal="true"
146 aria-labelledby="my-dialog-title"
147 aria-describedby="my-dialog-content">
148 <!-- Title cannot contain leading whitespace due to mdc-typography-baseline-top() -->
149 <h2 class="mdc-dialog__title" id="my-dialog-title"><!--
150 -->Choose a Ringtone<!--
151 --></h2>
152 <div class="mdc-dialog__content" id="my-dialog-content">
153 <ul class="mdc-deprecated-list mdc-deprecated-list--avatar-list">
154 <li class="mdc-deprecated-list-item" tabindex="0" data-mdc-dialog-action="none">
155 <span class="mdc-deprecated-list-item__text">None</span>
156 </li>
157 <li class="mdc-deprecated-list-item" data-mdc-dialog-action="callisto">
158 <span class="mdc-deprecated-list-item__text">Callisto</span>
159 </li>
160 <!-- ... -->
161 </ul>
162 </div>
163 </div>
164 </div>
165 <div class="mdc-dialog__scrim"></div>
166</div>
167```
168
169**Note: Note the inclusion of the `mdc-deprecated-list--avatar-list` class, which aligns with the Simple Dialog spec.**
170
171## Confirmation dialog
172
173Confirmation dialogs give users the ability to provide final confirmation of a choice before committing to it, so they have a chance to change their minds if necessary.
174
175If the user confirms a choice, it’s carried out. Otherwise, the user can dismiss the dialog. For example, users can listen to multiple ringtones but only make a final selection upon tapping “OK.”
176
177<img src="images/confirmation-dialog.png" alt="Confirmation dialog: selection confirmation" width="250px">
178
179### Confirmation dialog example
180
181```html
182<div class="mdc-dialog">
183 <div class="mdc-dialog__container">
184 <div class="mdc-dialog__surface"
185 role="alertdialog"
186 aria-modal="true"
187 aria-labelledby="my-dialog-title"
188 aria-describedby="my-dialog-content">
189 <!-- Title cannot contain leading whitespace due to mdc-typography-baseline-top() -->
190 <h2 class="mdc-dialog__title" id="my-dialog-title"><!--
191 -->Choose a Ringtone<!--
192 --></h2>
193 <div class="mdc-dialog__content" id="my-dialog-content">
194 <ul class="mdc-deprecated-list">
195 <li class="mdc-deprecated-list-item" tabindex="0">
196 <span class="mdc-deprecated-list-item__graphic">
197 <div class="mdc-radio">
198 <input class="mdc-radio__native-control"
199 type="radio"
200 id="test-dialog-baseline-confirmation-radio-1"
201 name="test-dialog-baseline-confirmation-radio-group"
202 checked>
203 <div class="mdc-radio__background">
204 <div class="mdc-radio__outer-circle"></div>
205 <div class="mdc-radio__inner-circle"></div>
206 </div>
207 </div>
208 </span>
209 <label id="test-dialog-baseline-confirmation-radio-1-label"
210 for="test-dialog-baseline-confirmation-radio-1"
211 class="mdc-deprecated-list-item__text">None</label>
212 </li>
213 <!-- ... -->
214 </ul>
215 </div>
216 <div class="mdc-dialog__actions">
217 <button type="button" class="mdc-button mdc-dialog__button" data-mdc-dialog-action="close">
218 <div class="mdc-button__ripple"></div>
219 <span class="mdc-button__label">Cancel</span>
220 </button>
221 <button type="button" class="mdc-button mdc-dialog__button" data-mdc-dialog-action="accept">
222 <div class="mdc-button__ripple"></div>
223 <span class="mdc-button__label">OK</span>
224 </button>
225 </div>
226 </div>
227 </div>
228 <div class="mdc-dialog__scrim"></div>
229</div>
230```
231
232**Note: In the example above, the Cancel button intentionally has the `close` action to align with the behavior of
233clicking the scrim or pressing the Escape key, allowing all interactions involving dismissal without taking an action
234to be detected the same way.**
235
236## Full-screen dialog
237
238Full-screen dialogs group a series of tasks, such as creating a calendar entry with the event title, date, location, and time.
239
240<img src="images/full-screen-dialog.png" alt="Full-screen dialog: event" width="250px">
241
242### Full-screen dialog example
243
244```html
245<div class="mdc-dialog mdc-dialog--open mdc-dialog--fullscreen">
246 <div class="mdc-dialog__container">
247 <div class="mdc-dialog__surface"
248 role="dialog"
249 aria-modal="true"
250 aria-labelledby="my-dialog-title"
251 aria-describedby="my-dialog-content">
252 <div class="mdc-dialog__header">
253 <h2 class="mdc-dialog__title" id="my-dialog-title">
254 Full-Screen Dialog Title
255 </h2>
256 <button class="mdc-icon-button material-icons mdc-dialog__close"
257 data-mdc-dialog-action="close">
258 close
259 </button>
260 </div>
261 <div class="mdc-dialog__content" id="my-dialog-content">
262 Lorem ipsum dolor sit amet, consectetur adipiscing elit.
263 Sed scelerisque metus dapibus, maximus massa pulvinar, commodo nunc.
264 Quisque vitae luctus lectus, ut tempus ipsum. Sed suscipit gravida scelerisque.
265 Aenean vulputate elementum est, quis consectetur orci consectetur ac.
266 Quisque accumsan vel nisi id dapibus. Suspendisse nec urna eu massa ornare rutrum.
267 Vivamus at nisi sit amet nulla pretium volutpat sit amet in justo. Donec mi metus,
268 interdum ac tincidunt at, vehicula vitae nisl. Morbi fermentum dapibus massa,
269 nec lobortis massa vestibulum eu.
270 </div>
271 <div class="mdc-dialog__actions">
272 <button type="button" class="mdc-button mdc-dialog__button"
273 data-mdc-dialog-action="ok">
274 <div class="mdc-button__ripple"></div>
275 <span class="mdc-button__label">OK</span>
276 </button>
277 </div>
278 </div>
279 </div>
280 <div class="mdc-dialog__scrim"></div>
281</div>
282```
283
284Note: Full-screen dialogs are intended for mobile/small-screen devices. The
285dialog's size will adapt to the screen size, and so becomes modal if used on
286larger screen sizes.
287
288## Floating sheet
289
290Floating sheets are dialogs with a close icon button. Clicking the close icon
291button closes the sheet. Having the close icon button is mutually exclusive with
292having action bar buttons (e.g. cancel and OK buttons). The icon button is
293absolutely positioned. Sheet content can have no default padding by using the
294`mdc-dialog--no-content-padding` class.
295
296### Floating sheet example
297
298```html
299<div class="mdc-dialog mdc-dialog--open test-dialog mdc-dialog--sheet mdc-dialog--no-content-padding"
300 aria-modal="true"
301 aria-labelledby="test-dialog__title--with-close-icon-button"
302 aria-describedby="test-dialog__content--with-close-icon-button"
303 id="test-dialog">
304 <div class="mdc-dialog__scrim" data-mdc-dialog-action="cancel"></div>
305 <div class="mdc-dialog__container">
306 <div class="mdc-dialog__surface">
307 <button class="mdc-icon-button material-icons mdc-dialog__close" data-mdc-dialog-action="close">
308 close
309 </button>
310 <div class="mdc-dialog__content test-dialog__content">
311 <div class="test-sheet__content">
312 <h3>Sheets</h3>
313 There are no action buttons. Any HTML content can go here. Title is also defined through content.
314 </div>
315 </div>
316 </div>
317 </div>
318</div>
319```
320
321## Additional Information
322
323### Dialog actions
324
325All dialog variants support the concept of dialog actions. Any element within a dialog may include the
326`data-mdc-dialog-action` attribute to indicate that interacting with it should close the dialog with the specified action.
327This action is then reflected via `event.detail.action` in the `MDCDialog:closing` and `MDCDialog:closed` events.
328
329Additionally, two interactions have defined actions by default:
330
331* Clicking on the scrim
332* Pressing the Escape key within the dialog
333
334Both of these map to the `close` action by default. This can be accessed and customized via the component's
335`scrimClickAction` and `escapeKeyAction` properties, respectively.
336
337Setting either of these properties to an empty string will result in that interaction being disabled (i.e. the dialog
338will no longer close in response to the interaction). Exercise caution when doing this - it should always be possible
339for a user to dismiss the dialog.
340
341Any action buttons within the dialog which equate strictly to a dismissal with no further action should also use the
342`close` action; this will make it easy to handle all such interactions consistently, while separately handling other
343actions.
344
345### Action button arrangement
346
347As indicated in the [Dialog design article](https://material.io/design/components/dialogs.html#anatomy), buttons within
348the `mdc-dialog__actions` element are arranged horizontally by default, with the confirming action _last_.
349
350In cases where the button text is too long for all buttons to fit on a single line, the buttons are stacked vertically,
351with the confirming action _first_.
352
353MDC Dialog detects and handles this automatically by default, reversing the buttons when applying the stacked layout.
354This automatic behavior can be disabled by setting `autoStackButtons` to `false` on the component instance:
355
356```js
357dialog.autoStackButtons = false;
358```
359
360This will also be disabled if the `mdc-dialog--stacked` modifier class is applied manually to the root element before the
361component is instantiated, but note that dialog action button labels are recommended to be short enough to fit on a
362single line if possible.
363
364### Default action button
365
366MDC Dialog supports indicating that one of its action buttons represents the default action, triggered by pressing the
367Enter key. This can be used e.g. for single-choice Confirmation Dialogs to accelerate the process of making a selection,
368avoiding the need to tab through to the appropriate button to confirm the choice.
369
370To indicate that a button represents the default action, add the `data-mdc-dialog-button-default` data attribute.
371For example:
372```html
373...
374<div class="mdc-dialog__actions">
375 <button type="button" class="mdc-button mdc-dialog__button" data-mdc-dialog-action="close">
376 <div class="mdc-button__ripple"></div>
377 <span class="mdc-button__label">Cancel</span>
378 </button>
379 <button type="button" class="mdc-button mdc-dialog__button" data-mdc-dialog-action="accept" data-mdc-dialog-button-default>
380 <div class="mdc-button__ripple"></div>
381 <span class="mdc-button__label">OK</span>
382 </button>
383</div>
384...
385```
386
387### Actions and selections
388
389Dialogs which require making a choice via selection controls should initially disable any button which performs an
390action if no choice is selected by default. MDC Dialog does not include built-in logic for this, since it aims to remain
391as unopinionated as possible regarding dialog contents, aside from relaying information on which action is taken.
392
393## Style customizations
394
395### CSS classes
396
397CSS Class | Description
398--- | ---
399`mdc-dialog` | Mandatory. The root DOM element containing the surface and the container.
400`mdc-dialog__scrim` | Mandatory. Semitransparent backdrop that displays behind a dialog.
401`mdc-dialog__container` | Mandatory. Wrapper element needed to ensure flexbox behavior in IE 11.
402`mdc-dialog__surface` | Mandatory. The bounding box for the dialog's content.
403`mdc-dialog__title` | Optional. Brief summary of the dialog's purpose.
404`mdc-dialog__content` | Optional. Primary content area. May contain a list, a form, or prose.
405`mdc-dialog__actions` | Optional. Footer area containing the dialog's action buttons.
406`mdc-dialog__button` | Optional. Individual action button. Typically paired with [`mdc-button`](../mdc-button).
407`mdc-dialog--open` | Optional. Indicates that the dialog is open and visible.
408`mdc-dialog--opening` | Optional. Applied automatically when the dialog is in the process of animating open.
409`mdc-dialog--closing` | Optional. Applied automatically when the dialog is in the process of animating closed.
410`mdc-dialog--scrollable` | Optional. Applied automatically when the dialog has overflowing content to warrant scrolling.
411`mdc-dialog--stacked` | Optional. Applied automatically when the dialog's action buttons can't fit on a single line and must be stacked.
412
413### Sass mixins
414
415Mixin | Description
416--- | ---
417`container-fill-color($color)` | Sets the fill color of the dialog.
418`scrim-color($color, $opacity)` | Sets the color of the scrim behind the dialog.
419`title-ink-color($color, $opacity)` | Sets the color of the dialog's title text.
420`content-ink-color($color, $opacity)` | Sets the color of the dialog's content text.
421`content-padding($padding-top, $padding-right, $padding-bottom, $padding-left)` | Sets the padding of the dialog's content.
422`scroll-divider-color($color, $opacity)` | Sets the color of the dividers which display around scrollable content.
423`shape-radius($radius, $rtl-reflexive)` | Sets the rounded shape to dialog surface with given radius size. Set `$rtl-reflexive` to true to flip radius values in RTL context, defaults to false.
424`min-width($min-width)` | Sets the minimum width of the dialog (defaults to 280px).
425`max-width($max-width, $margin)` | Sets the maximum width of the dialog (defaults to 560px max width and 16px margins).
426`max-height($max-height, $margin)` | Sets the maximum height of the dialog (defaults to no max height and 16px margins).
427
428**Note: The `max-width` and `max-height` mixins only apply their maximum when the viewport is large enough to accommodate the specified value when accounting for the specified margin on either side. When the viewport is smaller, the dialog is sized such that the given margin is retained around the edges.**
429
430## Other customizations
431
432Data Attributes | Description
433--- | ---
434`data-mdc-dialog-button-default` | Optional. Add to a button to indicate that it is the default action button (see Default Action Button section above).
435`data-mdc-dialog-initial-focus` | Optional. Add to an element to indicate that it is the element to initially focus on after the dialog has opened.
436
437## `MDCDialog` properties and methods
438
439Property | Value Type | Description
440--- | --- | ---
441`isOpen` | `boolean` (read-only) | Proxies to the foundation's `isOpen` method.
442`escapeKeyAction` | `string` | Proxies to the foundation's `getEscapeKeyAction` and `setEscapeKeyAction` methods.
443`scrimClickAction` | `string` | Proxies to the foundation's `getScrimClickAction` and `setScrimClickAction` methods.
444`autoStackButtons` | `boolean` | Proxies to the foundation's `getAutoStackButtons` and `setAutoStackButtons` methods.
445
446Method Signature | Description
447--- | ---
448`layout() => void` | Recalculates layout and automatically adds/removes modifier classes like `--scrollable`.
449`open() => void` | Opens the dialog.
450`close(action: string?) => void` | Closes the dialog, optionally with the specified action indicating why it was closed.
451
452### Events
453
454Event Name | `event.detail` | Description
455--- | --- | ---
456`MDCDialog:opening` | `{}` | Indicates when the dialog begins its opening animation.
457`MDCDialog:opened` | `{}` | Indicates when the dialog finishes its opening animation.
458`MDCDialog:closing` | `{action: string?}` | Indicates when the dialog begins its closing animation. `action` represents the action which closed the dialog.
459`MDCDialog:closed` | `{action: string?}` | Indicates when the dialog finishes its closing animation. `action` represents the action which closed the dialog.
460
461## Usage within web frameworks
462
463If you are using a JavaScript framework, such as React or Angular, you can create a Dialog for your framework. Depending on your needs, you can use the _Simple Approach: Wrapping MDC Web Vanilla Components_, or the _Advanced Approach: Using Foundations and Adapters_. Please follow the instructions [here](../../docs/integrating-into-frameworks.md).
464
465### `MDCDialogAdapter`
466
467Method Signature | Description
468--- | ---
469`addClass(className: string) => void` | Adds a class to the root element.
470`removeClass(className: string) => void` | Removes a class from the root element.
471`hasClass(className: string) => boolean` | Returns whether the given class exists on the root element.
472`addBodyClass(className: string) => void` | Adds a class to the `<body>`.
473`removeBodyClass(className: string) => void` | Removes a class from the `<body>`.
474`eventTargetMatches(target: EventTarget \| null, selector: string) => void` | Returns `true` if the target element matches the given CSS selector, otherwise `false`.
475`trapFocus(initialFocusEl: HTMLElement \| null) => void` | Sets up the DOM such that keyboard navigation is restricted to focusable elements within the dialog surface (see [Handling Focus Trapping](#handling-focus-trapping) below for more details). Moves focus to `initialFocusEl`, if set.
476`releaseFocus() => void` | Removes any effects of focus trapping on the dialog surface (see [Handling Focus Trapping](#handling-focus-trapping) below for more details).
477`getInitialFocusEl() => HTMLElement \| null` | Returns the `data-mdc-dialog-initial-focus` element to add focus to after the dialog has opened.
478`isContentScrollable() => boolean` | Returns `true` if `mdc-dialog__content` can be scrolled by the user, otherwise `false`.
479`areButtonsStacked() => boolean` | Returns `true` if `mdc-dialog__action` buttons (`mdc-dialog__button`) are stacked vertically, otherwise `false` if they are side-by-side.
480`getActionFromEvent(event: Event) => string \| null` | Retrieves the value of the `data-mdc-dialog-action` attribute from the given event's target, or an ancestor of the target.
481`clickDefaultButton() => void` | Invokes `click()` on the `data-mdc-dialog-button-default` element, if one exists in the dialog.
482`reverseButtons() => void` | Reverses the order of action buttons in the `mdc-dialog__actions` element. Used when switching between stacked and unstacked button layouts.
483`notifyOpening() => void` | Broadcasts an event denoting that the dialog has just started to open.
484`notifyOpened() => void` | Broadcasts an event denoting that the dialog has finished opening.
485`notifyClosing(action: string) {}` | Broadcasts an event denoting that the dialog has just started closing. If a non-empty `action` is passed, the event's `detail` object should include its value in the `action` property.
486`notifyClosed(action: string) {}` | Broadcasts an event denoting that the dialog has finished closing. If a non-empty `action` is passed, the event's `detail` object should include its value in the `action` property.
487
488### `MDCDialogFoundation`
489
490Method Signature | Description
491--- | ---
492`open()` | Opens the dialog.
493`close(action: string)` | Closes the dialog, optionally with the specified action indicating why it was closed.
494`isOpen() => boolean` | Returns whether the dialog is open.
495`layout()` | Recalculates layout and automatically adds/removes modifier classes e.g. `--scrollable`.
496`getEscapeKeyAction() => string` | Returns the action reflected when the Escape key is pressed.
497`setEscapeKeyAction(action: string)` | Sets the action reflected when the Escape key is pressed. Setting to `''` disables closing the dialog via Escape key.
498`getScrimClickAction() => string` | Returns the action reflected when the scrim is clicked.
499`setScrimClickAction(action: string)` | Sets the action reflected when the scrim is clicked. Setting to `''` disables closing the dialog via scrim click.
500`getAutoStackButtons() => boolean` | Returns whether stacked/unstacked action button layout is automatically handled during layout logic.
501`setAutoStackButtons(autoStack: boolean) => void` | Sets whether stacked/unstacked action button layout is automatically handled during layout logic.
502`handleClick(event: MouseEvent)` | Handles `click` events on or within the dialog's root element.
503`handleKeydown(event: KeyboardEvent)` | Handles `keydown` events on or within the dialog's root element.
504`handleDocumentKeydown(event: Event)` | Handles `keydown` events on or within the document while the dialog is open.
505`getSuppressDefaultPressSelector() => string` | Returns the selector string for elements that suppress the default dialog press action, such as pressing enter in a textarea.
506`setSuppressDefaultPressSelector(selector: string)` | Customize the selector string to suppress the default dialog press action. An empty string indicates that no elements should suppress the default action.
507
508#### Event handlers
509
510When wrapping the Dialog foundation, the following events must be bound to the indicated foundation methods:
511
512Event | Target | Foundation Handler | Register | Deregister
513--- | --- | --- | --- | ---
514`click` | `.mdc-dialog` (root) | `handleClick` | During initialization | During destruction
515`keydown` | `.mdc-dialog` (root) | `handleKeydown` | During initialization | During destruction
516`keydown` | `document` | `handleDocumentKeydown` | On `MDCDialog:opening` | On `MDCDialog:closing`
517`resize` | `window` | `layout` | On `MDCDialog:opening` | On `MDCDialog:closing`
518`orientationchange` | `window` | `layout` | On `MDCDialog:opening` | On `MDCDialog:closing`
519
520### The `util` API
521
522External frameworks and libraries can use the following utility methods from the `util` module when implementing their own component.
523
524Method Signature | Description
525--- | ---
526`createFocusTrapInstance(surfaceEl: Element, focusTrapFactory: function(): !FocusTrap, initialFocusEl: ?Element) => !FocusTrap` | Creates a properly configured [focus-trap][] instance.
527`isScrollable(el: Element \| null) => boolean` | Determines if the given element is scrollable.
528`areTopsMisaligned(els: Element[]) => boolean` | Determines if two or more of the given elements have different `offsetTop` values.
529
530### Handling focus trapping
531
532In order for dialogs to be fully accessible, they must conform to the guidelines outlined in:
533
534* https://www.w3.org/TR/wai-aria-practices/#dialog_modal
535* https://www.w3.org/TR/wai-aria-practices-1.1/examples/dialog-modal/dialog.html
536* https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_dialog_role
537
538The main implication of these guidelines is that the only focusable elements are those contained within a dialog
539surface.
540
541Trapping focus correctly for a modal dialog requires a complex set of events and interaction
542patterns that we feel is best not duplicated within the logic of this component. Furthermore,
543frameworks and libraries may have their own ways of trapping focus that framework authors may want
544to make use of. For this reason, we have two methods on the adapter that should be used to handle
545focus trapping:
546
547* `trapFocus()` is called when the dialog is open and should set up focus trapping adhering
548 to the ARIA practices in the link above.
549* `releaseFocus()` is called when the dialog is closed and should tear down any focus
550 trapping set up when the dialog was open.
551
552The `MDCDialog` component uses the [focus-trap][] package to handle this.
553**You can use `util.createFocusTrapInstance()` (see below) to easily create
554a focus trapping solution for your component code.**
555
556[focus-trap]: https://github.com/davidtheclark/focus-trap
557
558**Note: iOS platform doesn't seem to register currently focused element via `document.activeElement` which causes releasing focus to last focused element fail.**
559
560#### `createFocusTrapInstance()`
561
562```js
563const {activate, deactivate} =
564 util.createFocusTrapInstance(surfaceEl, focusTrapFactory, initialFocusEl);
565```
566
567Given a dialog surface element an optional `focusTrap` factory function, and an optional initial element to focus,
568such that:
569
570* The focus is trapped within the `surfaceEl`
571* The `initialFocusEl` receives focus when the focus trap is activated
572 - If omitted, defaults to the first focusable element in `surfaceEl`
573* Closing the dialog in any way (including pressing Escape or clicking outside the dialog) deactivates focus trapping
574* Focus is returned to the previously focused element before the focus trap was activated
575
576This focus trap instance can be used to implement the `trapFocus` and `releaseFocus` adapter methods by calling
577`instance.activate()` and `instance.deactivate()` respectively within those methods.
578
579The `focusTrapFactory` can be used to override the `focus-trap` function used to create the focus trap. Its API is the
580same as focus-trap's [createFocusTrap](https://github.com/davidtheclark/focus-trap#focustrap--createfocustrapelement-createoptions)
581(which is what it defaults to). You can pass in a custom function for mocking out the actual function within tests,
582or to modify the arguments passed to the function before it's called.