Skip to content

Commit

Permalink
fix(modal): make header close button optional
Browse files Browse the repository at this point in the history
  • Loading branch information
AlasdairSwan committed Apr 19, 2018
1 parent 2cdac1a commit 3833c75
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 32 deletions.
79 changes: 64 additions & 15 deletions .storybook/__snapshots__/Storyshots.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -15688,7 +15688,7 @@ exports[`Storyshots Modal modal with element body 1`] = `

exports[`Storyshots Modal modal with warning variant 1`] = `
<div
aria-labelledby="id10"
aria-labelledby="id11"
aria-modal={true}
className="modal modal-open modal-backdrop show"
role="dialog"
Expand All @@ -15704,7 +15704,7 @@ exports[`Storyshots Modal modal with warning variant 1`] = `
>
<h2
className="modal-title"
id="id10"
id="id11"
>
Warning Modal
</h2>
Expand Down Expand Up @@ -15750,7 +15750,7 @@ exports[`Storyshots Modal modal with warning variant 1`] = `
<span
aria-hidden={true}
className="fa fa-exclamation-triangle fa-3x text-warning"
id="Modal-WARNING11"
id="Modal-WARNING12"
/>
</div>
</div>
Expand Down Expand Up @@ -15784,6 +15784,55 @@ exports[`Storyshots Modal modal with warning variant 1`] = `
</div>
`;

exports[`Storyshots Modal modal without a close button in the header 1`] = `
<div
aria-labelledby="id10"
aria-modal={true}
className="modal modal-open modal-backdrop show"
role="dialog"
tabIndex="-1"
>
<div
className="modal-dialog"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id10"
>
Modal title.
</h2>
</div>
<div
className="modal-body"
>
<p>
Modal body.
</p>
</div>
<div
className="modal-footer"
>
<button
className="btn btn-outline-primary"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Close
</button>
</div>
</div>
</div>
</div>
`;

exports[`Storyshots Paragon Welcome 1`] = `
<div
className="css-jail"
Expand Down Expand Up @@ -20026,10 +20075,10 @@ exports[`Storyshots Tabs basic usage 1`] = `
>
<li
className="nav-item"
id="tab-label-tabInterface12-0"
id="tab-label-tabInterface13-0"
>
<a
aria-controls="tab-panel-tabInterface12-0"
aria-controls="tab-panel-tabInterface13-0"
aria-selected={true}
className="nav-link active"
onClick={[Function]}
Expand All @@ -20041,10 +20090,10 @@ exports[`Storyshots Tabs basic usage 1`] = `
</li>
<li
className="nav-item"
id="tab-label-tabInterface12-1"
id="tab-label-tabInterface13-1"
>
<a
aria-controls="tab-panel-tabInterface12-1"
aria-controls="tab-panel-tabInterface13-1"
aria-selected={false}
className="nav-link"
onClick={[Function]}
Expand All @@ -20056,10 +20105,10 @@ exports[`Storyshots Tabs basic usage 1`] = `
</li>
<li
className="nav-item"
id="tab-label-tabInterface12-2"
id="tab-label-tabInterface13-2"
>
<a
aria-controls="tab-panel-tabInterface12-2"
aria-controls="tab-panel-tabInterface13-2"
aria-selected={false}
className="nav-link"
onClick={[Function]}
Expand All @@ -20075,9 +20124,9 @@ exports[`Storyshots Tabs basic usage 1`] = `
>
<div
aria-hidden={false}
aria-labelledby="tab-label-tabInterface12-0"
aria-labelledby="tab-label-tabInterface13-0"
className="tab-pane active"
id="tab-panel-tabInterface12-0"
id="tab-panel-tabInterface13-0"
role="tabpanel"
>
<div>
Expand All @@ -20086,9 +20135,9 @@ exports[`Storyshots Tabs basic usage 1`] = `
</div>
<div
aria-hidden={true}
aria-labelledby="tab-label-tabInterface12-1"
aria-labelledby="tab-label-tabInterface13-1"
className="tab-pane"
id="tab-panel-tabInterface12-1"
id="tab-panel-tabInterface13-1"
role="tabpanel"
>
<div>
Expand All @@ -20097,9 +20146,9 @@ exports[`Storyshots Tabs basic usage 1`] = `
</div>
<div
aria-hidden={true}
aria-labelledby="tab-label-tabInterface12-2"
aria-labelledby="tab-label-tabInterface13-2"
className="tab-pane"
id="tab-panel-tabInterface12-2"
id="tab-panel-tabInterface13-2"
role="tabpanel"
>
<div>
Expand Down
8 changes: 7 additions & 1 deletion src/Modal/Modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
@import "~bootstrap/scss/_utilities";

.modal-open {
display: block;
display: block;

&:focus {
.modal-dialog {
box-shadow: $btn-focus-box-shadow;
}
}
}

.modal-backdrop {
Expand Down
9 changes: 9 additions & 0 deletions src/Modal/Modal.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ storiesOf('Modal', module)
onClose={() => {}}
/>
))
.add('modal without a close button in the header', () => (
<Modal
open
title="Modal title."
body="Modal body."
onClose={() => {}}
renderHeaderCloseButton={false}
/>
))
.add('modal with warning variant', () => (
<Modal
open
Expand Down
10 changes: 10 additions & 0 deletions src/Modal/Modal.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ describe('<Modal />', () => {
expect(modalBody.text()).toEqual(body);
expect(wrapper.find('button')).toHaveLength(2);
});

it('render of the header close button is optional', () => {
wrapper = mount(<Modal {...defaultProps} renderHeaderCloseButton={false} />);
const modalHeader = wrapper.find('.modal-header');
const modalFooter = wrapper.find('.modal-footer');

expect(modalHeader.find('button')).toHaveLength(0);
expect(modalFooter.find('button')).toHaveLength(1);
expect(wrapper.find('button')).toHaveLength(1);
});
});

describe('props received correctly', () => {
Expand Down
3 changes: 3 additions & 0 deletions src/Modal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ Provides a basic modal component with customizable title, body, and footer butto

### `onClose` (function; required)
`onClose` is a function that is called on close. It can be used to perform actions upon closing of the modal, such as restoring focus to the previous logical focusable element.

### `renderHeaderCloseButton` (boolean; optional)
`renderHeaderCloseButton` specifies whether a close button is rendered in the modal header. It defaults to true.
39 changes: 23 additions & 16 deletions src/Modal/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Modal extends React.Component {

this.close = this.close.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.setXButton = this.setXButton.bind(this);
this.setFirstFocusableElement = this.setFirstFocusableElement.bind(this);
this.setCloseButton = this.setCloseButton.bind(this);

this.headerId = newId();
Expand All @@ -26,8 +26,8 @@ class Modal extends React.Component {
}

componentDidMount() {
if (this.xButton) {
this.xButton.focus();
if (this.firstFocusableElement) {
this.firstFocusableElement.focus();
}
}

Expand All @@ -39,12 +39,12 @@ class Modal extends React.Component {

componentDidUpdate(prevState) {
if (this.state.open && !prevState.open) {
this.xButton.focus();
this.firstFocusableElement.focus();
}
}

setXButton(input) {
this.xButton = input;
setFirstFocusableElement(input) {
this.firstFocusableElement = input;
}

setCloseButton(input) {
Expand Down Expand Up @@ -105,13 +105,13 @@ class Modal extends React.Component {
this.close();
} else if (e.key === 'Tab') {
if (e.shiftKey) {
if (e.target === this.xButton) {
if (e.target === this.firstFocusableElement) {
e.preventDefault();
this.closeButton.focus();
}
} else if (e.target === this.closeButton) {
e.preventDefault();
this.xButton.focus();
this.firstFocusableElement.focus();
}
}
}
Expand Down Expand Up @@ -152,6 +152,7 @@ class Modal extends React.Component {

render() {
const { open } = this.state;
const { renderHeaderCloseButton } = this.props;

return (
<div
Expand All @@ -167,19 +168,23 @@ class Modal extends React.Component {
role="dialog"
aria-modal
aria-labelledby={this.headerId}
{...(!renderHeaderCloseButton ? { tabIndex: '-1' } : {})}
{...(!renderHeaderCloseButton ? { ref: this.setFirstFocusableElement } : {})}
>
<div className={styles['modal-dialog']}>
<div className={styles['modal-content']}>
<div className={styles['modal-header']}>
<h2 className={styles['modal-title']} id={this.headerId}>{this.props.title}</h2>
<Button
label={<Icon className={['fa', 'fa-times']} />}
className={['p-1']}
aria-label={this.props.closeText}
onClick={this.close}
inputRef={this.setXButton}
onKeyDown={this.handleKeyDown}
/>
{ renderHeaderCloseButton &&
<Button
label={<Icon className={['fa', 'fa-times']} />}
className={['p-1']}
aria-label={this.props.closeText}
onClick={this.close}
inputRef={this.setFirstFocusableElement}
onKeyDown={this.handleKeyDown}
/>
}
</div>
<div className={styles['modal-body']}>
{this.renderBody()}
Expand Down Expand Up @@ -214,13 +219,15 @@ Modal.propTypes = {
variant: PropTypes.shape({
status: PropTypes.string,
}),
renderHeaderCloseButton: PropTypes.bool,
};

Modal.defaultProps = {
open: false,
buttons: [],
closeText: 'Close',
variant: {},
renderHeaderCloseButton: true,
};


Expand Down

0 comments on commit 3833c75

Please sign in to comment.