Skip to content

Commit 94be72c

Browse files
authored
fix(ComposedModal): fix focus trap and refocus to trigger element when closed (#11418)
* fix(ComposedModal): fix focus trap and refocus to trigger when closed * fix(ComposedModal): add style and updated snapshot
1 parent ecaf9b3 commit 94be72c

File tree

5 files changed

+58
-35
lines changed

5 files changed

+58
-35
lines changed

packages/components/src/components/modal/_modal.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@
106106
@include carbon--breakpoint(xlg) {
107107
width: 48%;
108108
}
109+
110+
.#{$prefix}--modal-container-body {
111+
display: contents;
112+
}
109113
}
110114

111115
// -----------------------------

packages/react/src/components/ComposedModal/ComposedModal-story.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import React, { useState } from 'react';
8+
import React, { useState, useRef } from 'react';
99
import ReactDOM from 'react-dom';
1010
import { action } from '@storybook/addon-actions';
1111
import {
@@ -275,6 +275,7 @@ export const PassiveModal = () => {
275275
};
276276

277277
export const WithStateManager = () => {
278+
const closeButton = useRef();
278279
/**
279280
* Simple state manager for modals.
280281
*/
@@ -298,10 +299,19 @@ export const WithStateManager = () => {
298299
return (
299300
<ModalStateManager
300301
renderLauncher={({ setOpen }) => (
301-
<Button onClick={() => setOpen(true)}>Launch composed modal</Button>
302+
<Button ref={closeButton} onClick={() => setOpen(true)}>
303+
Launch composed modal
304+
</Button>
302305
)}>
303306
{({ open, setOpen }) => (
304-
<ComposedModal open={open} onClose={() => setOpen(false)}>
307+
<ComposedModal
308+
open={open}
309+
onClose={() => {
310+
setOpen(false);
311+
setTimeout(() => {
312+
closeButton.current.focus();
313+
});
314+
}}>
305315
<ModalHeader label="Account resources" title="Add a custom domain" />
306316
<ModalBody>
307317
<p style={{ marginBottom: '1rem' }}>

packages/react/src/components/ComposedModal/ComposedModal.js

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -286,36 +286,38 @@ export default class ComposedModal extends Component {
286286
}
287287
}
288288
}}
289+
aria-hidden={!open}
289290
onBlur={this.handleBlur}
290291
onClick={this.handleClick}
291292
onKeyDown={this.handleKeyDown}
292293
onTransitionEnd={open ? this.handleTransitionEnd : undefined}
293294
className={modalClass}>
294-
{/* Non-translatable: Focus-wrap code makes this `<span>` not actually read by screen readers */}
295-
<span
296-
ref={this.startSentinel}
297-
tabIndex="0"
298-
role="link"
299-
className={`${prefix}--visually-hidden`}>
300-
Focus sentinel
301-
</span>
302295
<div
303-
ref={this.innerModal}
304296
className={containerClass}
305297
role="dialog"
306298
aria-modal="true"
307299
aria-label={ariaLabel ? ariaLabel : generatedAriaLabel}
308300
aria-labelledby={ariaLabelledBy}>
309-
{childrenWithProps}
301+
{/* Non-translatable: Focus-wrap code makes this `<span>` not actually read by screen readers */}
302+
<button
303+
type="button"
304+
ref={this.startSentinel}
305+
className={`${prefix}--visually-hidden`}>
306+
Focus sentinel
307+
</button>
308+
<div
309+
ref={this.innerModal}
310+
className={`${prefix}--modal-container-body`}>
311+
{childrenWithProps}
312+
</div>
313+
{/* Non-translatable: Focus-wrap code makes this `<button>` not actually read by screen readers */}
314+
<button
315+
type="button"
316+
ref={this.endSentinel}
317+
className={`${prefix}--visually-hidden`}>
318+
Focus sentinel
319+
</button>
310320
</div>
311-
{/* Non-translatable: Focus-wrap code makes this `<span>` not actually read by screen readers */}
312-
<span
313-
ref={this.endSentinel}
314-
tabIndex="0"
315-
role="link"
316-
className={`${prefix}--visually-hidden`}>
317-
Focus sentinel
318-
</span>
319321
</div>
320322
);
321323
}

packages/react/src/components/ComposedModal/__snapshots__/ComposedModal-test.js.snap

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ exports[`<ComposedModal /> renders 1`] = `
77
selectorPrimaryFocus="[data-modal-primary-focus]"
88
>
99
<div
10+
aria-hidden={false}
1011
className="bx--modal is-visible"
1112
onBlur={[Function]}
1213
onClick={[Function]}
@@ -15,25 +16,27 @@ exports[`<ComposedModal /> renders 1`] = `
1516
open={true}
1617
role="presentation"
1718
>
18-
<span
19-
className="bx--visually-hidden"
20-
role="link"
21-
tabIndex="0"
22-
>
23-
Focus sentinel
24-
</span>
2519
<div
2620
aria-modal="true"
2721
className="bx--modal-container"
2822
role="dialog"
29-
/>
30-
<span
31-
className="bx--visually-hidden"
32-
role="link"
33-
tabIndex="0"
3423
>
35-
Focus sentinel
36-
</span>
24+
<button
25+
className="bx--visually-hidden"
26+
type="button"
27+
>
28+
Focus sentinel
29+
</button>
30+
<div
31+
className="bx--modal-container-body"
32+
/>
33+
<button
34+
className="bx--visually-hidden"
35+
type="button"
36+
>
37+
Focus sentinel
38+
</button>
39+
</div>
3740
</div>
3841
</ComposedModal>
3942
`;

packages/styles/scss/components/modal/_modal.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@
108108
@include breakpoint(xlg) {
109109
width: 48%;
110110
}
111+
112+
.#{$prefix}--modal-container-body {
113+
display: contents;
114+
}
111115
}
112116

113117
// -----------------------------

0 commit comments

Comments
 (0)