diff --git a/components.json b/components.json index 38f7a29..fad5e44 100644 --- a/components.json +++ b/components.json @@ -35,6 +35,7 @@ { "name": "Design System UI", "components": [ + "BannerNotification", "BannerNotifications" ] }, diff --git a/src/components/BannerNotification/README.md b/src/components/BannerNotification/README.md new file mode 100644 index 0000000..8870ad9 --- /dev/null +++ b/src/components/BannerNotification/README.md @@ -0,0 +1,9 @@ +By default its rendered without close button: +```js + +``` + +But it can be rendered with close buttton: +```js + alert('Click')} /> +``` diff --git a/src/components/BannerNotifications/Notification/__snapshots__/index.spec.js.snap b/src/components/BannerNotification/__snapshots__/index.spec.js.snap similarity index 50% rename from src/components/BannerNotifications/Notification/__snapshots__/index.spec.js.snap rename to src/components/BannerNotification/__snapshots__/index.spec.js.snap index 4a3c02f..be601bb 100644 --- a/src/components/BannerNotifications/Notification/__snapshots__/index.spec.js.snap +++ b/src/components/BannerNotification/__snapshots__/index.spec.js.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Notification renders correctly 1`] = ` +exports[`BannerNotification renders correctly 1`] = `
`; -exports[`Notification renders correctly 2`] = ` +exports[`BannerNotification renders correctly 2`] = `
`; -exports[`Notification renders correctly 3`] = ` +exports[`BannerNotification renders correctly 3`] = `
`; -exports[`Notification renders correctly 4`] = ` +exports[`BannerNotification renders correctly 4`] = `
`; + +exports[`BannerNotification renders correctly without action 1`] = ` +
+
+ + + +
+ + lorem ipsum - messge + +
+`; + +exports[`BannerNotification renders correctly without action 2`] = ` +
+
+ + + +
+ + lorem ipsum - success + +
+`; + +exports[`BannerNotification renders correctly without action 3`] = ` +
+
+ + + +
+ + lorem ipsum - warning + +
+`; + +exports[`BannerNotification renders correctly without action 4`] = ` +
+
+ + + +
+ + lorem ipsum - alert + +
+`; diff --git a/src/components/BannerNotifications/Notification/index.js b/src/components/BannerNotification/index.js similarity index 58% rename from src/components/BannerNotifications/Notification/index.js rename to src/components/BannerNotification/index.js index d4d1ed9..819c8f1 100644 --- a/src/components/BannerNotifications/Notification/index.js +++ b/src/components/BannerNotification/index.js @@ -27,24 +27,35 @@ function getClassName(type) { } } -const Notification = ({type, message, onClose}) => ( -
+/** + * This is a single component used in `BannerNotifications` component. + */ +const BannerNotification = ({className, type, text, onClose}) => ( +
- {message} - - - + {text} + {onClose && ( + + + + )}
); -Notification.propTypes = { +BannerNotification.propTypes = { + className: PropTypes.string, type: PropTypes.oneOf(['alert', 'warning', 'success', 'message']).isRequired, - message: PropTypes.string.isRequired, - onClose: PropTypes.func.isRequired, + text: PropTypes.string.isRequired, + onClose: PropTypes.func, +}; + +BannerNotification.defaultProps = { + className: '', + onClose: null, }; -export default Notification; +export default BannerNotification; diff --git a/src/components/BannerNotification/index.spec.js b/src/components/BannerNotification/index.spec.js new file mode 100644 index 0000000..d85951e --- /dev/null +++ b/src/components/BannerNotification/index.spec.js @@ -0,0 +1,64 @@ +import React from 'react'; +import renderer from 'react-test-renderer'; +import {shallow} from 'enzyme'; +import sinon from 'sinon'; + +import BannerNotification from './index'; + +const noop = () => {}; + +/* eslint-disable no-alert */ +test('BannerNotification renders correctly', () => { + let component = renderer.create( + , + ); + expect(component.toJSON()).toMatchSnapshot(); + + component = renderer.create( + , + ); + expect(component.toJSON()).toMatchSnapshot(); + + component = renderer.create( + , + ); + expect(component.toJSON()).toMatchSnapshot(); + + component = renderer.create( + , + ); + expect(component.toJSON()).toMatchSnapshot(); +}); + +test('BannerNotification renders correctly without action', () => { + let component = renderer.create( + , + ); + expect(component.toJSON()).toMatchSnapshot(); + + component = renderer.create( + , + ); + expect(component.toJSON()).toMatchSnapshot(); + + component = renderer.create( + , + ); + expect(component.toJSON()).toMatchSnapshot(); + + component = renderer.create( + , + ); + expect(component.toJSON()).toMatchSnapshot(); +}); + +test('BannerNotification onClose hander is invoked', () => { + const mockOnClick = sinon.spy(); + const wrapper = shallow( + + ); + + wrapper.find('.wds-banner-notification__close').simulate('click'); + + expect(mockOnClick.calledOnce).toBe(true); +}); diff --git a/src/components/BannerNotifications/Notification/index.spec.js b/src/components/BannerNotifications/Notification/index.spec.js deleted file mode 100644 index 3c2b546..0000000 --- a/src/components/BannerNotifications/Notification/index.spec.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import renderer from 'react-test-renderer'; -import {shallow} from 'enzyme'; -import sinon from 'sinon'; - -import Notification from './index'; - -const noop = () => {}; - -/* eslint-disable no-alert */ -test('Notification renders correctly', () => { - let component = renderer.create( - , - ); - expect(component.toJSON()).toMatchSnapshot(); - - component = renderer.create( - , - ); - expect(component.toJSON()).toMatchSnapshot(); - - component = renderer.create( - , - ); - expect(component.toJSON()).toMatchSnapshot(); - - component = renderer.create( - , - ); - expect(component.toJSON()).toMatchSnapshot(); -}); - -test('Notification onClose hander is invoked', () => { - const mockOnClick = sinon.spy(); - const wrapper = shallow( - - ); - - wrapper.find('.wds-banner-notification__close').simulate('click'); - - expect(mockOnClick.calledOnce).toBe(true); -}); diff --git a/src/components/BannerNotifications/README.md b/src/components/BannerNotifications/README.md index 0420006..213c500 100644 --- a/src/components/BannerNotifications/README.md +++ b/src/components/BannerNotifications/README.md @@ -8,22 +8,23 @@ But with proper data it can display all the messages: const messages4 = [ { type: 'message', - message: 'this is a message', + text: 'this is a permanent message', id: '1', + permanent: true, }, { type: 'success', - message: 'this is a success', + text: 'this is a success', id: '2', }, { type: 'warning', - message: 'this is a warning', + text: 'this is a warning', id: '3', }, { type: 'alert', - message: 'this is an alert', + text: 'this is an alert', id: '4', }, ]; {}} /> diff --git a/src/components/BannerNotifications/__snapshots__/index.spec.js.snap b/src/components/BannerNotifications/__snapshots__/index.spec.js.snap index e5ac106..6ddb13c 100644 --- a/src/components/BannerNotifications/__snapshots__/index.spec.js.snap +++ b/src/components/BannerNotifications/__snapshots__/index.spec.js.snap @@ -4,10 +4,10 @@ exports[`BannerNotifications renders correctly 1`] = `null`; exports[`BannerNotifications renders correctly 2`] = `
- this is an alert + this is a permanent alert - - -
`; diff --git a/src/components/BannerNotifications/bannerNotificationsMessageType.js b/src/components/BannerNotifications/bannerNotificationsMessageType.js new file mode 100644 index 0000000..1a16673 --- /dev/null +++ b/src/components/BannerNotifications/bannerNotificationsMessageType.js @@ -0,0 +1,8 @@ +import PropTypes from 'prop-types'; + +export default PropTypes.shape({ + text: PropTypes.string.isRequired, + type: PropTypes.oneOf(['alert', 'warning', 'success', 'message']).isRequired, + id: PropTypes.string.isRequired, + permanent: PropTypes.bool, +}); diff --git a/src/components/BannerNotifications/index.js b/src/components/BannerNotifications/index.js index de66303..baaf610 100644 --- a/src/components/BannerNotifications/index.js +++ b/src/components/BannerNotifications/index.js @@ -1,34 +1,84 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Notification from './Notification'; +import BannerNotification from '../BannerNotification'; -const BannerNotifications = ({messages, onClose}) => { - if (messages.length === 0) { - return null; +import messageType from './bannerNotificationsMessageType'; + +/** + * Component used to create notifications. For full functionality it needs some + * app logic to handle the array of messages - adding/removing. + * + * See the following: + * - https://github.com/Wikia/f2/blob/master/frontend/react-app/curationTools/containers/Notifications.jsx + * - https://github.com/Wikia/f2/tree/master/frontend/react-app/curationTools/reducers/notifications + * + * The `messages` prop is an array of `bannerNotificationsMessageType` objects with the following props: + * - `id`: unique string that's send as the param of the `onClose` function + * - `type`: one of: `'alert'`, `'warning'`, `'success'` or `'message'`. + * - `text`: text that is going to be displayed on the notification + * - `permanent`: a boolean flag - if present the close button won't be displayed on the notification + * + * `bannerNotificationsMessageType` is exported along with `BannerNotification` + */ +class BannerNotifications extends React.Component { + constructor(props) { + super(props); + + this.onClose = this.onClose.bind(this); } - return ( -
- {messages.map(({message, type, id}) => ( - { onClose(id); }} - /> - ))} -
- ); -}; + onClose(id) { + this.props.onClose(id); + } + + renderNotification({text, type, id, permanent}) { + const props = { + key: id, + type, + text, + }; + + if (permanent) { + return ; + } + return this.onClose(id)} />; + } + + render() { + const {className, messages} = this.props; + + if (messages.length === 0) { + return null; + } + + return ( +
+ {messages.map(message => this.renderNotification(message))} +
+ ); + } +} BannerNotifications.propTypes = { - messages: PropTypes.arrayOf(PropTypes.shape({ - message: PropTypes.string.isRequired, - type: PropTypes.oneOf(['alert', 'warning', 'success', 'message']).isRequired, - id: PropTypes.string.isRequired, - })).isRequired, + /** + * An additional class name + */ + className: PropTypes.string, + /** + * An array of `bannerNotificationsMessageType` objects + * @type {bannerNotificationsMessageType} + */ + messages: PropTypes.arrayOf(messageType).isRequired, + /** + * Action invoked when close button is clicked + * @type {[type]} + */ onClose: PropTypes.func.isRequired, }; +BannerNotifications.defaultProps = { + className: '', +}; + export default BannerNotifications; diff --git a/src/components/BannerNotifications/index.spec.js b/src/components/BannerNotifications/index.spec.js index cf7d0b1..e4c8d70 100644 --- a/src/components/BannerNotifications/index.spec.js +++ b/src/components/BannerNotifications/index.spec.js @@ -9,30 +9,31 @@ const noop = () => {}; const messages1 = [ { type: 'message', - message: 'this is a single message', + text: 'this is a single message', id: '1', }, ]; const messages4 = [ { type: 'message', - message: 'this is a message', + text: 'this is a message', id: '1', }, { type: 'success', - message: 'this is a success', + text: 'this is a success', id: '2', }, { type: 'warning', - message: 'this is a warning', + text: 'this is a warning', id: '3', }, { type: 'alert', - message: 'this is an alert', + text: 'this is a permanent alert', id: '4', + permanent: true, }, ]; diff --git a/src/index.js b/src/index.js index 236158d..3eea102 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,9 @@ export {default as Input} from './components/Input'; export {default as Fieldset} from './components/Fieldset'; export {default as Spinner} from './components/Spinner'; // Design System UI +export {default as BannerNotification} from './components/BannerNotification'; export {default as BannerNotifications} from './components/BannerNotifications'; + // Icons export {default as VideoPlayIcon} from './components/VideoPlayIcon'; // Usefull flow components @@ -13,3 +15,7 @@ export {default as FandomContentWell} from './components/FandomContentWell'; // Other UI export {default as ExpandableText} from './components/ExpandableText'; export {default as Vignette} from './components/Vignette'; + +// custom types +export {default as bannerNotificationsMessageType} + from './components/BannerNotifications/bannerNotificationsMessageType'; diff --git a/styleguide/INSTALLATION.md b/styleguide/INSTALLATION.md index 4484b59..70176e1 100644 --- a/styleguide/INSTALLATION.md +++ b/styleguide/INSTALLATION.md @@ -5,7 +5,7 @@ ### 2. Use it in the code ```js static -import {Button, VideoPlayIcon} from '@Wikia/react-design-system'; +import {Button, VideoPlayIcon} from '@wikia/react-design-system'; ``` ## Requirements