From a64a2cb30d91f3d87d27354204c5d22ddf26135e Mon Sep 17 00:00:00 2001 From: Patrick Riley Date: Mon, 13 Nov 2017 13:23:57 -0500 Subject: [PATCH] feat(modal): adds the pf modal patterns --- src/components/Modal/CustomModalDialog.js | 87 +++++++++++ src/components/Modal/Modal.js | 1 + src/components/Modal/Modal.stories.js | 63 ++++++++ .../Modal/__mocks__/mockAboutModalManager.js | 133 ++++++++++++++++ .../Modal/__mocks__/mockModalManager.js | 146 ++++++++++++++++++ src/components/Modal/index.js | 2 + src/index.js | 1 + 7 files changed, 433 insertions(+) create mode 100644 src/components/Modal/CustomModalDialog.js create mode 100644 src/components/Modal/Modal.js create mode 100644 src/components/Modal/Modal.stories.js create mode 100644 src/components/Modal/__mocks__/mockAboutModalManager.js create mode 100644 src/components/Modal/__mocks__/mockModalManager.js create mode 100644 src/components/Modal/index.js diff --git a/src/components/Modal/CustomModalDialog.js b/src/components/Modal/CustomModalDialog.js new file mode 100644 index 00000000000..e52c867e67c --- /dev/null +++ b/src/components/Modal/CustomModalDialog.js @@ -0,0 +1,87 @@ +/** + * CustomModalDialog creates custom ReactBootstrap ModalDialog + * https://github.com/react-bootstrap/react-bootstrap/blob/master/src/ModalDialog.js + * + * Adds contentClassName prop for setting `modal-content` div's class + */ +import classNames from 'classnames'; +import React from 'react'; +import PropTypes from 'prop-types'; + +import { utils } from 'react-bootstrap'; + +const bsClass = utils.bootstrapUtils.bsClass; +const bsSizes = utils.bootstrapUtils.bsSizes; +const getClassSet = utils.bootstrapUtils.getClassSet; +const prefix = utils.bootstrapUtils.prefix; +const splitBsProps = utils.bootstrapUtils.splitBsProps; + +// React Bootstrap utils/StyleConfig Size is currently not exported +const Size = { + LARGE: 'large', + SMALL: 'small', + XSMALL: 'xsmall', +}; + +const propTypes = { + /** A css class to apply to the Modal dialog DOM node. */ + dialogClassName: PropTypes.string, + /** custom modal-content class added to the content DOM node */ + contentClassName: PropTypes.string, + /** base modal class name */ + className: PropTypes.string, + /** additional modal styles */ + style: PropTypes.object, + /** Children nodes */ + children: PropTypes.node, +}; + +class CustomModalDialog extends React.Component { + render() { + const { + dialogClassName, + contentClassName, + className, + style, + children, + ...props + } = this.props; + const [bsProps, elementProps] = splitBsProps(props); + + const bsClassName = prefix(bsProps); + + const modalStyle = { display: 'block', ...style }; + + const dialogClasses = { + ...getClassSet(bsProps), + [bsClassName]: false, + [prefix(bsProps, 'dialog')]: true, + }; + + return ( +
+
+
+ {children} +
+
+
+ ); + } +} + +CustomModalDialog.propTypes = propTypes; + +export default bsClass( + 'modal', + bsSizes([Size.LARGE, Size.SMALL], CustomModalDialog), +); diff --git a/src/components/Modal/Modal.js b/src/components/Modal/Modal.js new file mode 100644 index 00000000000..3d2fe9c8c83 --- /dev/null +++ b/src/components/Modal/Modal.js @@ -0,0 +1 @@ +export { Modal as default } from 'react-bootstrap'; diff --git a/src/components/Modal/Modal.stories.js b/src/components/Modal/Modal.stories.js new file mode 100644 index 00000000000..79f7c75fb4e --- /dev/null +++ b/src/components/Modal/Modal.stories.js @@ -0,0 +1,63 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { withInfo } from '@storybook/addon-info'; +import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; + +import { + MockModalManager, + basicExampleSource, +} from './__mocks__/mockModalManager'; + +import { + MockAboutModalManager, + aboutExampleSource, +} from './__mocks__/mockAboutModalManager'; + +const stories = storiesOf('Modal Overlay', module); + +const description = ( +

+ This component is based on React Bootstrap Modal component. See{' '} + + React Bootstrap Docs + {' '} + for complete Modal component documentation. +

+); + +stories.addDecorator( + defaultTemplate({ + title: 'Modal Overlay', + documentationLink: + 'http://www.patternfly.org/pattern-library/forms-and-controls/modal-overlay/', + description: description, + }), +); + +stories.add( + 'Basic example', + withInfo({ + source: false, + propTablesExclude: [MockModalManager], + text: ( +
+

Story Source

+
{basicExampleSource}
+
+ ), + })(() => ), +); + +stories.add( + 'About Modal', + withInfo({ + source: false, + propTablesExclude: [MockAboutModalManager], + text: ( +
+

Story Source

+
{aboutExampleSource}
+
+ ), + })(() => ), +); diff --git a/src/components/Modal/__mocks__/mockAboutModalManager.js b/src/components/Modal/__mocks__/mockAboutModalManager.js new file mode 100644 index 00000000000..2ceef8fb483 --- /dev/null +++ b/src/components/Modal/__mocks__/mockAboutModalManager.js @@ -0,0 +1,133 @@ +import React from 'react'; +import { Button } from '../../Button'; +import { Icon } from '../../Icon'; +import { CustomModalDialog, Modal } from '../index'; +import logo from 'patternfly/dist/img/logo-alt.svg'; + +export class MockAboutModalManager extends React.Component { + constructor() { + super(); + this.state = { showModal: false }; + this.open = this.open.bind(this); + this.close = this.close.bind(this); + } + open() { + this.setState({ showModal: true }); + } + close() { + this.setState({ showModal: false }); + } + render() { + return ( +
+ + + + + + + +

Product Title

+
+
    +
  • + Label Version +
  • +
  • + Label Version +
  • +
  • + Label Version +
  • +
  • + Label Version +
  • +
  • + Label Version +
  • +
  • + Label Version +
  • +
+
+
+ Trademark and Copyright Information +
+
+ + Patternfly Logo + +
+
+ ); + } +} + +export const aboutExampleSource = ` + + + + + + + +

Product Title

+
+
    +
  • + Label Version +
  • +
  • + Label Version +
  • +
  • + Label Version +
  • +
  • + Label Version +
  • +
  • + Label Version +
  • +
  • + Label Version +
  • +
+
+
Trademark and Copyright Information
+
+ + Patternfly Logo + +
+`; diff --git a/src/components/Modal/__mocks__/mockModalManager.js b/src/components/Modal/__mocks__/mockModalManager.js new file mode 100644 index 00000000000..928fabc885f --- /dev/null +++ b/src/components/Modal/__mocks__/mockModalManager.js @@ -0,0 +1,146 @@ +import React from 'react'; +import { Button } from '../../Button'; +import { Icon } from '../../Icon'; +import { Modal } from '../index'; + +export class MockModalManager extends React.Component { + constructor() { + super(); + this.state = { showModal: false }; + this.open = this.open.bind(this); + this.close = this.close.bind(this); + } + open() { + this.setState({ showModal: true }); + } + close() { + this.setState({ showModal: false }); + } + render() { + return ( +
+ + + + + + Modal Overlay Title + + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + +
+
+ ); + } +} + +export const basicExampleSource = ` + + + + + + Modal Overlay Title + + +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + +
+`; diff --git a/src/components/Modal/index.js b/src/components/Modal/index.js new file mode 100644 index 00000000000..40a3f87509b --- /dev/null +++ b/src/components/Modal/index.js @@ -0,0 +1,2 @@ +export { default as CustomModalDialog } from './CustomModalDialog'; +export { default as Modal } from './Modal'; diff --git a/src/index.js b/src/index.js index e41a0fd8f69..29e139903c1 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ export * from './components/Icon'; export * from './components/ListGroup'; export * from './components/ListView'; export * from './components/MenuItem'; +export * from './components/Modal'; export * from './components/OverlayTrigger'; export * from './components/Popover'; export * from './components/ToastNotification';