diff --git a/docs.md b/docs.md index 2ed3b073..ec638928 100644 --- a/docs.md +++ b/docs.md @@ -584,18 +584,18 @@ A component passed using `previewComponent` will receive the following props: ### Parameters -- `input` **[Object][156]** A `redux-form` [input][157] object -- `meta` **[Object][156]** A `redux-form` [meta][158] object -- `readFiles` **[Function][150]** A callback that is fired with new files and is expected to return an array of file objects with the `url` key set to the "read" value. This can be either a data URL or the public URL from a 3rd party API (optional, default `readFilesAsDataUrls`) -- `multiple` **[Boolean][151]** A flag indicating whether or not to accept multiple files (optional, default `false`) -- `accept` **[String][149]?** Value that defines the file types the file input should accept (e.g., ".doc,.docx"). More info: [https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept][166] -- `capture` **(`"user"` \| `"environment"`)?** Value that specifies which camera to use, if the accept attribute indicates the input type of image or video. This is not available for all devices (e.g., desktops). More info: [https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#capture][167] -- `onRemove` **[Function][150]** A callback fired when a file is removed (optional, default `noop`) -- `previewComponent` **[Function][150]** A custom component that is used to display a preview of each attached file (optional, default `RenderPreview`) -- `removeComponent` **[Function][150]** A custom component that receives `value` and `onRemove` props (optional, default `RemoveButton`) -- `thumbnail` **[String][149]?** A placeholder image to display before the file is loaded -- `hidePreview` **[Boolean][151]** A flag indicating whether or not to hide the file preview (optional, default `false`) -- `selectText` **[String][149]?** An override for customizing the text that is displayed on the input's label. Defaults to 'Select File' or 'Select File(s)' depending on the `multiple` prop value +- `input` **[Object][153]** A `redux-form` [input][154] object +- `meta` **[Object][153]** A `redux-form` [meta][155] object +- `readFiles` **[Function][147]** A callback that is fired with new files and is expected to return an array of file objects with the `url` key set to the "read" value. This can be either a data URL or the public URL from a 3rd party API (optional, default `readFilesAsDataUrls`) +- `multiple` **[Boolean][148]** A flag indicating whether or not to accept multiple files (optional, default `false`) +- `accept` **[String][146]?** Value that defines the file types the file input should accept (e.g., ".doc,.docx"). More info: [https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept][163] +- `capture` **(`"user"` \| `"environment"`)?** Value that specifies which camera to use, if the accept attribute indicates the input type of image or video. This is not available for all devices (e.g., desktops). More info: [https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#capture][164] +- `onRemove` **[Function][147]** A callback fired when a file is removed (optional, default `noop`) +- `previewComponent` **[Function][147]** A custom component that is used to display a preview of each attached file (optional, default `RenderPreview`) +- `removeComponent` **[Function][147]** A custom component that receives `value` and `onRemove` props (optional, default `RemoveButton`) +- `thumbnail` **[String][146]?** A placeholder image to display before the file is loaded +- `hidePreview` **[Boolean][148]** A flag indicating whether or not to hide the file preview (optional, default `false`) +- `selectText` **[String][146]?** An override for customizing the text that is displayed on the input's label. Defaults to 'Select File' or 'Select File(s)' depending on the `multiple` prop value ### Examples @@ -1739,7 +1739,8 @@ Note: this component requires custom styles. These styles can be imported from t ### Parameters - `onClose` **[Function][147]** A handler for closing the modal. May be triggered via the close button, and outside click, or a key press. -- `hideCloseButton` **[Boolean][148]?** A flag for hiding the default close button. +- `isOpen` **[Boolean][148]** A flag for showing the modal. (optional, default `true`) +- `preventClose` **[Boolean][148]** A flag for preventing the modal from being closed (close button, escape, or overlay click). (optional, default `false`) ### Examples diff --git a/migration-guides/v6.0.0.md b/migration-guides/v6.0.0.md index 790fef37..92a66034 100644 --- a/migration-guides/v6.0.0.md +++ b/migration-guides/v6.0.0.md @@ -12,6 +12,7 @@ This version contains the following breaking changes: 9. The `previewComponent` for a file input no longer receives a `value` prop and `file` is a file object (with the url) 10. The tag on `` now uses the class `spinner` in place of an id and supports additional classes 11. `` now expects both `options` and `value` as required props. +12. `` no longer accepts the `hideCloseButton` prop The required changes for each item are detailed below. @@ -238,6 +239,7 @@ Replace any styling rules that target `#spinner` with `.spinner`. } } ``` + ## 11. `` now expects both `options` and `value` as required props. Make sure that any instances of `` in your application are already sending these two props. @@ -248,3 +250,23 @@ Make sure that any instances of `` in your application are already sen value={currentTab} /> ``` + +## 12. `` no longer accepts the `hideCloseButton` prop. +Replace `hideCloseButton` with `preventClose`. If you still need the modal to close on escape and/or by clicking the overlay, you can manually set those props. By default, `shouldCloseOnEsc` and `shouldCloseOnOverlayClick` will be set to the opposite of `preventClose` (default `false`). + +```jsx +// Before + + Modal content + + +// After + + Modal content + + +// With prop override + + Modal content + +``` \ No newline at end of file diff --git a/src/modal.js b/src/modal.js index 2ef84e7d..9c41091b 100644 --- a/src/modal.js +++ b/src/modal.js @@ -13,7 +13,8 @@ import { isServer } from './utils' * @name Modal * @type Function * @param {Function} onClose - A handler for closing the modal. May be triggered via the close button, and outside click, or a key press. - * @param {Boolean} [hideCloseButton] - A flag for hiding the default close button. + * @param {Boolean} [isOpen=true] - A flag for showing the modal. + * @param {Boolean} [preventClose=false] - A flag for preventing the modal from being closed (close button, escape, or overlay click). * * @example * @@ -40,12 +41,14 @@ import { isServer } from './utils' const propTypes = { onClose: PropTypes.func.isRequired, - hideCloseButton: PropTypes.bool, + isOpen: PropTypes.bool, + preventClose: PropTypes.bool, children: PropTypes.node, } const defaultProps = { - hideCloseButton: false, + isOpen: true, + preventClose: false, } function getRootElement() { @@ -57,10 +60,11 @@ function getRootElement() { // A wrapper for react-modal that adds some styles and a close button. // See https://github.com/reactjs/react-modal for usage. -function Modal({ onClose, hideCloseButton, children, ...rest }) { +function Modal({ isOpen, onClose, preventClose, children, ...rest }) { + const canClose = !preventClose return (
{children}
- {!!onClose && !hideCloseButton && ( - <> - - + {canClose && ( + )}
) diff --git a/stories/modal.story.js b/stories/modal.story.js index bae8110a..746cd949 100644 --- a/stories/modal.story.js +++ b/stories/modal.story.js @@ -16,12 +16,12 @@ storiesOf('Modal', module) ) }) - .add('without close button', () => { + .add('with close prevented', () => { const [modalShown, setModalShown] = useState(true) return (
{modalShown && ( - setModalShown(false)} hideCloseButton> + setModalShown(false)} preventClose={true}> This is the modal content! )} diff --git a/test/modal.test.js b/test/modal.test.js index 4c5b1e6f..af60a159 100644 --- a/test/modal.test.js +++ b/test/modal.test.js @@ -3,24 +3,51 @@ import { mount } from 'enzyme' import { Modal } from '../src/' import { noop } from 'lodash' -test('Modal is shown by default', () => { - const wrapper = mount() - expect(wrapper.find('.modal-content').exists()).toEqual(true) -}) +describe('Modal', () => { + test('is shown by default', () => { + const wrapper = mount() + expect(wrapper.find('.modal-content').exists()).toEqual(true) + }) + + test('can be hidden/animated by manually passing isOpen', () => { + const wrapper = mount() + expect(wrapper.find('.modal-content').exists()).toEqual(false) + }) + + test('calls close handler when close button is clicked', () => { + const onClose = jest.fn() + const wrapper = mount() + wrapper.find('.modal-close').simulate('click') + expect(onClose).toHaveBeenCalled() + }) -test('Modal can be hidden/animated by manually passing isOpen', () => { - const wrapper = mount() - expect(wrapper.find('.modal-content').exists()).toEqual(false) -}) + describe('when preventClose=true', () => { + test('hides close button', () => { + const wrapper = mount() + expect(wrapper.find('.modal-close').exists()).toEqual(false) + }) -test('Modal calls close handler when close button is clicked', () => { - const onClose = jest.fn() - const wrapper = mount() - wrapper.find('.modal-close').simulate('click') - expect(onClose).toHaveBeenCalled() -}) + test('does not close by escape key', () => { + const onClose = jest.fn() + const wrapper = mount() + wrapper.find('.modal-content').simulate('keydown', { keyCode: 27 }) + expect(onClose).not.toHaveBeenCalled() + }) + + test('does not close by overlay click', () => { + const onClose = jest.fn() + const overlayClass = 'test-overlay' + const wrapper = mount() + wrapper.find('.' + overlayClass).simulate('click') + expect(onClose).not.toHaveBeenCalled() + expect(wrapper.find('.modal-content').exists()).toEqual(true) + }) -test('Modal hides close button when hideCloseButton=true', () => { - const wrapper = mount() - expect(wrapper.find('.modal-close').exists()).toEqual(false) + test('allows individual prop overrides', () => { + const onClose = jest.fn() + const wrapper = mount() + wrapper.find('.modal-content').simulate('keydown', { keyCode: 27 }) + expect(onClose).toHaveBeenCalled() + }) + }) })