From 67ee9f55ad7231635635dd56c0d499c399dacd5e Mon Sep 17 00:00:00 2001 From: Bruno Dias Date: Mon, 26 Jun 2017 23:35:00 -0300 Subject: [PATCH] [added] allow users to pass aria-* attribute. This patch allows users to pass custom aria attributes to be added to the modal content. It accepts an object where the keys are the name of the attributes without the prefix aria-*. --- .eslintrc | 1 + README.md | 22 ++++++++++++++++++++++ docs/README.md | 7 +++++++ examples/basic/app.js | 9 ++++++++- specs/Modal.spec.js | 10 +++++++++- src/components/Modal.js | 1 + src/components/ModalPortal.js | 9 ++++++++- 7 files changed, 56 insertions(+), 3 deletions(-) diff --git a/.eslintrc b/.eslintrc index a7119795..06dc1920 100644 --- a/.eslintrc +++ b/.eslintrc @@ -28,6 +28,7 @@ "react/no-find-dom-node": [0], "react/jsx-closing-bracket-location": [0], "react/jsx-filename-extension": ["error", {"extensions": [".js"]}], + "react/forbid-prop-types": [1, {"forbid": ["any"]}], "react/require-default-props": 0 } } diff --git a/README.md b/README.md index 41046df8..a42c30e7 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,28 @@ Modal.setAppElement(appElement); Modal.setAppElement('#your-app-element'); ``` +### Additional Aria Attributes + +Use the property `aria` to pass any additional aria attributes. It accepts +an object where the keys are the names of the attributes without the prefix +`aria-`. + +Example: + +```js + +

H1

+
+

Description goes here.

+
+
+``` + ## Styles Styles are passed as an object with 2 keys, 'overlay' and 'content' like so diff --git a/docs/README.md b/docs/README.md index 36d77d9e..1094f86b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -82,6 +82,13 @@ import ReactModal from 'react-modal'; Function that will be called to get the parent element that the modal will be attached to. */ parentSelector={() => document.body} + /* + Additional aria attributes (optional). + */ + aria={{ + labelledby: "heading", + describedby: "full_description" + }} /> ``` diff --git a/examples/basic/app.js b/examples/basic/app.js index 96fdad35..323f4d90 100644 --- a/examples/basic/app.js +++ b/examples/basic/app.js @@ -69,12 +69,19 @@ class App extends Component { {}} onRequestClose={this.toggleModal_2}> -

test

+

This is the modal 2!

+
+

This is a description of what it does: nothing :)

+
); diff --git a/specs/Modal.spec.js b/specs/Modal.spec.js index 867ba41b..9a407851 100644 --- a/specs/Modal.spec.js +++ b/specs/Modal.spec.js @@ -239,7 +239,7 @@ describe('State', () => { unmountModal(); expect(!isBodyWithReactModalOpenClass()).toBeTruthy(); }); - + it('should not add classes to document.body for unopened modals', () => { renderModal({ isOpen: true }); expect(isBodyWithReactModalOpenClass()).toBeTruthy(); @@ -259,6 +259,14 @@ describe('State', () => { expect(isBodyWithReactModalOpenClass()).toBeTruthy(); }); + it('additional aria attributes', () => { + const modal = renderModal({ isOpen: true, aria: { labelledby: "a" }}, 'hello'); + expect( + mcontent(modal).getAttribute('aria-labelledby') + ).toEqual("a"); + unmountModal(); + }); + it('adding/removing aria-hidden without an appElement will try to fallback to document.body', () => { ariaAppHider.documentNotReadyOrSSRTesting(); const node = document.createElement('div'); diff --git a/src/components/Modal.js b/src/components/Modal.js index 9f8b1eee..db3077d1 100644 --- a/src/components/Modal.js +++ b/src/components/Modal.js @@ -53,6 +53,7 @@ export default class Modal extends Component { ariaHideApp: PropTypes.bool, shouldCloseOnOverlayClick: PropTypes.bool, parentSelector: PropTypes.func, + aria: PropTypes.object, role: PropTypes.string, contentLabel: PropTypes.string.isRequired }; diff --git a/src/components/ModalPortal.js b/src/components/ModalPortal.js index 066317d2..2dc16163 100644 --- a/src/components/ModalPortal.js +++ b/src/components/ModalPortal.js @@ -51,6 +51,7 @@ export default class ModalPortal extends Component { shouldCloseOnOverlayClick: PropTypes.bool, role: PropTypes.string, contentLabel: PropTypes.string, + aria: PropTypes.object, children: PropTypes.node }; @@ -254,6 +255,11 @@ export default class ModalPortal extends Component { `${className} ${additional}` : className; } + ariaAttributes = items => Object.keys(items).reduce((acc, name) => { + acc[`aria-${name}`] = items[name]; + return acc; + }, {}); + render() { const { className, overlayClassName, defaultStyles } = this.props; const contentStyles = className ? {} : defaultStyles.content; @@ -273,7 +279,8 @@ export default class ModalPortal extends Component { onKeyDown={this.handleKeyDown} onClick={this.handleContentOnClick} role={this.props.role} - aria-label={this.props.contentLabel}> + aria-label={this.props.contentLabel} + {...this.ariaAttributes(this.props.aria || {})}> {this.props.children}