From 82d672293aabcb29208bbeb0742c410edc283353 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Wed, 27 Sep 2017 17:29:04 +0200 Subject: [PATCH 1/8] First WIP version of FlashMessages & Alert components --- .../FlashMessages/components/Alert.jsx | 83 +++++++++++++++++++ .../src/components/FlashMessages/index.jsx | 39 +++++++++ 2 files changed, 122 insertions(+) create mode 100644 app/javascript/src/components/FlashMessages/components/Alert.jsx create mode 100644 app/javascript/src/components/FlashMessages/index.jsx diff --git a/app/javascript/src/components/FlashMessages/components/Alert.jsx b/app/javascript/src/components/FlashMessages/components/Alert.jsx new file mode 100644 index 000000000..523461956 --- /dev/null +++ b/app/javascript/src/components/FlashMessages/components/Alert.jsx @@ -0,0 +1,83 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; + +class Alert extends Component { + + static alertClass (type) { + const classes = { + error: "alert-danger", + alert: "alert-warning", + notice: "alert-info", + success: "alert-success" + }; + return classes[type] || classes.success; + } + + static glyphiconClass (type) { + const classes = { + error: "glyphicon-exclamation-sign", + alert: "glyphicon-exclamation-sign", + notice: "glyphicon-info-sign", + success: "glyphicon-ok-sign" + }; + return classes[type] || classes.success; + } + + componentDidMount() { + this.timer = setTimeout( + this.props.onClose, + this.props.timeout + ); + } + + componentWillUnmount() { + clearTimeout(this.timer); + } + + render() { + const message = this.props.message; + const alertClassName = + `alert + ${this.alertClass(message.type)} + alert-dismissable + alert-floating + fade in`; + const glyphiconClassName = + `glyphicon + ${this.glyphiconClass(message.type)}`; + + return( +
+
+ + + {message.text} +
+
+ ); + } +} + +Alert.propTypes = { + onClose: PropTypes.func, + timeout: PropTypes.number, + message: PropTypes.shape({ + id: PropTypes.number, + type: PropTypes.string.isRequired, + text: PropTypes.string.isRequired + }).isRequired +}; + +Alert.defaultProps = { + onClose: null, + timeout: 3000 +}; + +export default Alert; \ No newline at end of file diff --git a/app/javascript/src/components/FlashMessages/index.jsx b/app/javascript/src/components/FlashMessages/index.jsx new file mode 100644 index 000000000..ce4064156 --- /dev/null +++ b/app/javascript/src/components/FlashMessages/index.jsx @@ -0,0 +1,39 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import Alert from "./components/Alert"; + +class FlashMessages extends Component { + + constructor(props) { + super(props); + this.state = { + messages: props.messages + }; + } + + render () { + return( +
+ {this.state.messages.map(message => + ) + } +
+ ); + } +} + +FlashMessages.propTypes = { + messages: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.number.isRequired, + type: PropTypes.string.isRequired, + text: PropTypes.string.isRequired + }) + ) +}; + +FlashMessages.defaultProps = { + messages: [] +} + +export default FlashMessages; \ No newline at end of file From a46f50af210064111f17f16fc391be9b25936e25 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Wed, 27 Sep 2017 17:34:45 +0200 Subject: [PATCH 2/8] Remove FlashMessages component --- .../components/Alert.jsx => Alert/index.jsx} | 0 .../src/components/FlashMessages/index.jsx | 39 ------------------- 2 files changed, 39 deletions(-) rename app/javascript/src/components/{FlashMessages/components/Alert.jsx => Alert/index.jsx} (100%) delete mode 100644 app/javascript/src/components/FlashMessages/index.jsx diff --git a/app/javascript/src/components/FlashMessages/components/Alert.jsx b/app/javascript/src/components/Alert/index.jsx similarity index 100% rename from app/javascript/src/components/FlashMessages/components/Alert.jsx rename to app/javascript/src/components/Alert/index.jsx diff --git a/app/javascript/src/components/FlashMessages/index.jsx b/app/javascript/src/components/FlashMessages/index.jsx deleted file mode 100644 index ce4064156..000000000 --- a/app/javascript/src/components/FlashMessages/index.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import Alert from "./components/Alert"; - -class FlashMessages extends Component { - - constructor(props) { - super(props); - this.state = { - messages: props.messages - }; - } - - render () { - return( -
- {this.state.messages.map(message => - ) - } -
- ); - } -} - -FlashMessages.propTypes = { - messages: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number.isRequired, - type: PropTypes.string.isRequired, - text: PropTypes.string.isRequired - }) - ) -}; - -FlashMessages.defaultProps = { - messages: [] -} - -export default FlashMessages; \ No newline at end of file From f6c7e854ad5dfe4db38619876853a193a48f0dd5 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Wed, 27 Sep 2017 17:59:04 +0200 Subject: [PATCH 3/8] Update main application, slight refactor --- app/javascript/src/components/Alert/index.jsx | 11 ++-- .../src/components/Navigation/index.jsx | 1 + app/javascript/src/index.jsx | 50 +++++++++++++------ 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/app/javascript/src/components/Alert/index.jsx b/app/javascript/src/components/Alert/index.jsx index 523461956..7093982be 100644 --- a/app/javascript/src/components/Alert/index.jsx +++ b/app/javascript/src/components/Alert/index.jsx @@ -3,7 +3,7 @@ import PropTypes from "prop-types"; class Alert extends Component { - static alertClass (type) { + static alertClass(type) { const classes = { error: "alert-danger", alert: "alert-warning", @@ -13,7 +13,7 @@ class Alert extends Component { return classes[type] || classes.success; } - static glyphiconClass (type) { + static glyphiconClass(type) { const classes = { error: "glyphicon-exclamation-sign", alert: "glyphicon-exclamation-sign", @@ -38,13 +38,13 @@ class Alert extends Component { const message = this.props.message; const alertClassName = `alert - ${this.alertClass(message.type)} + ${Alert.alertClass(message.type)} alert-dismissable alert-floating fade in`; const glyphiconClassName = `glyphicon - ${this.glyphiconClass(message.type)}`; + ${Alert.glyphiconClass(message.type)}`; return(
@@ -58,7 +58,7 @@ class Alert extends Component { - {message.text} +  {message.text}
); @@ -69,7 +69,6 @@ Alert.propTypes = { onClose: PropTypes.func, timeout: PropTypes.number, message: PropTypes.shape({ - id: PropTypes.number, type: PropTypes.string.isRequired, text: PropTypes.string.isRequired }).isRequired diff --git a/app/javascript/src/components/Navigation/index.jsx b/app/javascript/src/components/Navigation/index.jsx index 6fe32f058..5cc2c5e9a 100644 --- a/app/javascript/src/components/Navigation/index.jsx +++ b/app/javascript/src/components/Navigation/index.jsx @@ -20,6 +20,7 @@ import UserAccountDropdown from "./components/UserAccountDropdown"; const StyledNavbar = styled(Navbar)` background-color: ${WHITE_COLOR}; border-color: ${BORDER_GRAY_COLOR}; + margin-bottom: 0; `; const StyledBrand = styled.a` diff --git a/app/javascript/src/index.jsx b/app/javascript/src/index.jsx index 23fbf1cb5..a103018c3 100644 --- a/app/javascript/src/index.jsx +++ b/app/javascript/src/index.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { Component } from "react"; import { BrowserRouter } from "react-router-dom"; import { Provider } from "react-redux"; import { IntlProvider, addLocaleData } from "react-intl"; @@ -8,6 +8,7 @@ import messages from "./config/locales/messages"; import store from "./config/store"; import Spinner from "./components/Spinner"; +import Alert from "./components/Alert"; import ModalsContainer from "./components/ModalsContainer"; import SettingsPage from "./scenes/SettingsPage"; import Navigation from "./components/Navigation"; @@ -15,19 +16,36 @@ import Navigation from "./components/Navigation"; addLocaleData([...enLocaleData]); const locale = "en-US"; -export default () => - - -
- -
- - -
-
+class ScinoteApp extends Component { + constructor(props) { + super(props); + const a = 5; + } - - -
-
-
; + render() { + return ( + + +
+ +
+ +
+ +
+ +
+
+ + + +
+
+
+ ); + } +} + +export default ScinoteApp; From 6951755a9227239aecd95a5f1b5afdfa37dc7a39 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Thu, 28 Sep 2017 19:39:17 +0200 Subject: [PATCH 4/8] Add animation, multiple alerts, make them float --- .../components/Alert.jsx} | 37 ++++----- .../src/components/AlertsContainer/index.jsx | 79 +++++++++++++++++++ app/javascript/src/index.jsx | 55 ++++++------- app/javascript/src/styles/animations.scss | 27 +++++++ app/javascript/src/styles/main.scss | 1 + package.json | 1 + yarn.lock | 15 ++++ 7 files changed, 167 insertions(+), 48 deletions(-) rename app/javascript/src/components/{Alert/index.jsx => AlertsContainer/components/Alert.jsx} (70%) create mode 100644 app/javascript/src/components/AlertsContainer/index.jsx create mode 100644 app/javascript/src/styles/animations.scss diff --git a/app/javascript/src/components/Alert/index.jsx b/app/javascript/src/components/AlertsContainer/components/Alert.jsx similarity index 70% rename from app/javascript/src/components/Alert/index.jsx rename to app/javascript/src/components/AlertsContainer/components/Alert.jsx index 7093982be..54d26d4df 100644 --- a/app/javascript/src/components/Alert/index.jsx +++ b/app/javascript/src/components/AlertsContainer/components/Alert.jsx @@ -1,8 +1,13 @@ import React, { Component } from "react"; +import styled from "styled-components"; import PropTypes from "prop-types"; +import { Grid, Row, Col } from "react-bootstrap"; + +const Wrapper = styled.div` + margin-bottom: 0; +`; class Alert extends Component { - static alertClass(type) { const classes = { error: "alert-danger", @@ -35,20 +40,18 @@ class Alert extends Component { } render() { - const message = this.props.message; const alertClassName = `alert - ${Alert.alertClass(message.type)} + ${Alert.alertClass(this.props.type)} alert-dismissable - alert-floating - fade in`; + alert-floating`; const glyphiconClassName = `glyphicon - ${Alert.glyphiconClass(message.type)}`; + ${Alert.glyphiconClass(this.props.type)}`; return( -
-
+ + -  {message.text} -
-
+  {this.props.message} + + ); } } Alert.propTypes = { - onClose: PropTypes.func, + message: PropTypes.string.isRequired, + type: PropTypes.string.isRequired, timeout: PropTypes.number, - message: PropTypes.shape({ - type: PropTypes.string.isRequired, - text: PropTypes.string.isRequired - }).isRequired + onClose: PropTypes.func }; Alert.defaultProps = { - onClose: null, - timeout: 3000 + timeout: 5000, + onClose: undefined }; export default Alert; \ No newline at end of file diff --git a/app/javascript/src/components/AlertsContainer/index.jsx b/app/javascript/src/components/AlertsContainer/index.jsx new file mode 100644 index 000000000..9e2dae033 --- /dev/null +++ b/app/javascript/src/components/AlertsContainer/index.jsx @@ -0,0 +1,79 @@ +import React, { Component } from "react"; +import styled from "styled-components"; +import update from "immutability-helper"; +import TransitionGroup from 'react-transition-group/TransitionGroup'; +import CSSTransition from 'react-transition-group/CSSTransition'; +import Alert from "./components/Alert"; + +const Wrapper = styled.div` + position: absolute; + z-index: 1000; + width: 100%; +`; + +class AlertsContainer extends Component { + constructor(props) { + super(props); + + this.state = { + alerts: [] + }; + + this.add = this.add.bind(this); + this.clearAll = this.clearAll.bind(this); + this.renderAlert = this.renderAlert.bind(this); + + // Bind self to global namespace + window.alerts = this; + } + + add(message, type, timeout) { + this.setState( + update( + this.state, + { + alerts: { $push: [{ message, type, timeout }] } + } + ) + ); + } + + clearAll() { + this.setState({ alerts: [] }); + } + + clear(alert) { + const index = this.state.alerts.indexOf(alert); + const alerts = update(this.state.alerts, { $splice: [[index, 1]] }); + this.setState({ alerts: alerts }); + } + + renderAlert(alert) { + return ( + this.clear(alert)} + /> + ); + } + + render() { + return ( + + + {this.state.alerts.map((alert, index) => + + {this.renderAlert(alert, index)} + + )} + + + ); + } +} + +export default AlertsContainer; \ No newline at end of file diff --git a/app/javascript/src/index.jsx b/app/javascript/src/index.jsx index a103018c3..f92d4ce7a 100644 --- a/app/javascript/src/index.jsx +++ b/app/javascript/src/index.jsx @@ -1,14 +1,15 @@ -import React, { Component } from "react"; +import React from "react"; import { BrowserRouter } from "react-router-dom"; import { Provider } from "react-redux"; import { IntlProvider, addLocaleData } from "react-intl"; import enLocaleData from "react-intl/locale-data/en"; +import styled from "styled-components"; import { flattenMessages } from "./config/locales/utils"; import messages from "./config/locales/messages"; import store from "./config/store"; import Spinner from "./components/Spinner"; -import Alert from "./components/Alert"; +import AlertsContainer from "./components/AlertsContainer"; import ModalsContainer from "./components/ModalsContainer"; import SettingsPage from "./scenes/SettingsPage"; import Navigation from "./components/Navigation"; @@ -16,36 +17,30 @@ import Navigation from "./components/Navigation"; addLocaleData([...enLocaleData]); const locale = "en-US"; -class ScinoteApp extends Component { - constructor(props) { - super(props); - const a = 5; - } +const ContentWrapper = styled.div` + margin-top: 15px; +`; - render() { - return ( - - +const ScinoteApp = () => + + +
+
- -
- -
- -
- -
-
- - - + + + + +
- - - ); - } -} +
+ + + +
+
+
; export default ScinoteApp; diff --git a/app/javascript/src/styles/animations.scss b/app/javascript/src/styles/animations.scss new file mode 100644 index 000000000..782710e56 --- /dev/null +++ b/app/javascript/src/styles/animations.scss @@ -0,0 +1,27 @@ +.alert-animated-enter { + opacity: 0.01; +} +.alert-animated-enter.alert-animated-enter-active { + opacity: 1; + transition: opacity 150ms ease-in; +} +.alert-animated-exit { + opacity: 1; + padding-bottom: 15px; + padding-top: 15px; + margin-bottom: 20px; + height: 50px; +} +.alert-animated-exit.alert-animated-exit-active { + overflow: hidden; + opacity: 0.01; + padding-top: 0px; + padding-bottom: 0px; + margin-bottom: 0px; + height: 0px; + transition: opacity 300ms ease-in, + padding-top 500ms ease-in, + padding-bottom 500ms ease-in, + margin-bottom 500ms ease-in, + height 500ms ease-in; +} \ No newline at end of file diff --git a/app/javascript/src/styles/main.scss b/app/javascript/src/styles/main.scss index 434b3392b..005b68e10 100644 --- a/app/javascript/src/styles/main.scss +++ b/app/javascript/src/styles/main.scss @@ -1,4 +1,5 @@ @import 'constants'; +@import 'animations'; @import 'react-bootstrap-timezone-picker/dist/react-bootstrap-timezone-picker.min.css'; @import '~react-bootstrap-table/dist/react-bootstrap-table.min'; @import 'react-tagsinput/react-tagsinput.css'; diff --git a/package.json b/package.json index 8f15e51df..ef8f1c710 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "react-bootstrap-timezone-picker": "^1.0.11", "react-data-grid": "^2.0.2", "react-tagsinput": "^3.17.0", + "react-transition-group": "^2.2.0", "react-dom": "^15.6.1", "react-intl": "^2.3.0", "react-intl-redux": "^0.6.0", diff --git a/yarn.lock b/yarn.lock index fe7343bfb..959663dcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1156,6 +1156,10 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" +chain-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc" + chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -4825,6 +4829,17 @@ react-timezone@^0.2.0: dependencies: classnames "^2.2.1" +react-transition-group@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.0.tgz#793bf8cb15bfe91b3101b24bce1c1d2891659575" + dependencies: + chain-function "^1.0.0" + classnames "^2.2.5" + dom-helpers "^3.2.0" + loose-envify "^1.3.1" + prop-types "^15.5.8" + warning "^3.0.0" + react@^15.6.1: version "15.6.1" resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df" From a0db8fcf9d2cbf97a4baeb7321d4bf98c32c656e Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Thu, 28 Sep 2017 19:49:25 +0200 Subject: [PATCH 5/8] Some codestyle fixes --- .../AlertsContainer/components/Alert.jsx | 29 ++++++++++--------- .../src/components/AlertsContainer/index.jsx | 8 ++--- app/javascript/src/index.jsx | 3 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/javascript/src/components/AlertsContainer/components/Alert.jsx b/app/javascript/src/components/AlertsContainer/components/Alert.jsx index 54d26d4df..944e4560c 100644 --- a/app/javascript/src/components/AlertsContainer/components/Alert.jsx +++ b/app/javascript/src/components/AlertsContainer/components/Alert.jsx @@ -49,20 +49,23 @@ class Alert extends Component { `glyphicon ${Alert.glyphiconClass(this.props.type)}`; - return( + return ( - - - -  {this.props.message} - + + + + + +  {this.props.message} + + + ); } diff --git a/app/javascript/src/components/AlertsContainer/index.jsx b/app/javascript/src/components/AlertsContainer/index.jsx index 9e2dae033..51a0b81d2 100644 --- a/app/javascript/src/components/AlertsContainer/index.jsx +++ b/app/javascript/src/components/AlertsContainer/index.jsx @@ -21,6 +21,7 @@ class AlertsContainer extends Component { this.add = this.add.bind(this); this.clearAll = this.clearAll.bind(this); + this.clear = this.clear.bind(this); this.renderAlert = this.renderAlert.bind(this); // Bind self to global namespace @@ -31,9 +32,7 @@ class AlertsContainer extends Component { this.setState( update( this.state, - { - alerts: { $push: [{ message, type, timeout }] } - } + { alerts: { $push: [{ message, type, timeout }] } } ) ); } @@ -65,8 +64,7 @@ class AlertsContainer extends Component { {this.state.alerts.map((alert, index) => + classNames="alert-animated"> {this.renderAlert(alert, index)} )} diff --git a/app/javascript/src/index.jsx b/app/javascript/src/index.jsx index f92d4ce7a..1328b9fca 100644 --- a/app/javascript/src/index.jsx +++ b/app/javascript/src/index.jsx @@ -24,8 +24,7 @@ const ContentWrapper = styled.div` const ScinoteApp = () => + messages={flattenMessages(messages[locale])}>
From cbf2e3cc3f08def6ea85b3f6f86b2ff3a864f571 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Thu, 28 Sep 2017 20:07:11 +0200 Subject: [PATCH 6/8] Hound is love, Hound is life --- .../src/components/AlertsContainer/index.jsx | 2 +- app/javascript/src/styles/animations.scss | 37 ++++++++++--------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/app/javascript/src/components/AlertsContainer/index.jsx b/app/javascript/src/components/AlertsContainer/index.jsx index 51a0b81d2..b31c76c0d 100644 --- a/app/javascript/src/components/AlertsContainer/index.jsx +++ b/app/javascript/src/components/AlertsContainer/index.jsx @@ -44,7 +44,7 @@ class AlertsContainer extends Component { clear(alert) { const index = this.state.alerts.indexOf(alert); const alerts = update(this.state.alerts, { $splice: [[index, 1]] }); - this.setState({ alerts: alerts }); + this.setState({ alerts }); } renderAlert(alert) { diff --git a/app/javascript/src/styles/animations.scss b/app/javascript/src/styles/animations.scss index 782710e56..78d26dd64 100644 --- a/app/javascript/src/styles/animations.scss +++ b/app/javascript/src/styles/animations.scss @@ -1,27 +1,30 @@ .alert-animated-enter { opacity: 0.01; + + &.alert-animated-enter-active { + opacity: 1; + transition: opacity 150ms ease-in; + } } -.alert-animated-enter.alert-animated-enter-active { - opacity: 1; - transition: opacity 150ms ease-in; -} + .alert-animated-exit { opacity: 1; padding-bottom: 15px; padding-top: 15px; margin-bottom: 20px; height: 50px; + + &.alert-animated-exit-active { + overflow: hidden; + opacity: 0.01; + padding-top: 0; + padding-bottom: 0; + margin-bottom: 0; + height: 0; + transition: opacity 300ms ease-in, + padding-top 500ms ease-in, + padding-bottom 500ms ease-in, + margin-bottom 500ms ease-in, + height 500ms ease-in; + } } -.alert-animated-exit.alert-animated-exit-active { - overflow: hidden; - opacity: 0.01; - padding-top: 0px; - padding-bottom: 0px; - margin-bottom: 0px; - height: 0px; - transition: opacity 300ms ease-in, - padding-top 500ms ease-in, - padding-bottom 500ms ease-in, - margin-bottom 500ms ease-in, - height 500ms ease-in; -} \ No newline at end of file From 8ba7ed5884b576b9efa733a1a53bee1848ee3390 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Mon, 2 Oct 2017 19:25:04 +0200 Subject: [PATCH 7/8] Change the generation to be done using Redux (instead of this.window...) --- .../AlertsContainer/components/Alert.jsx | 9 +-- .../src/components/AlertsContainer/index.jsx | 64 +++++++++---------- .../src/components/actions/AlertsActions.js | 29 +++++++++ .../src/components/reducers/AlertsReducers.js | 31 +++++++++ app/javascript/src/config/action_types.js | 5 ++ app/javascript/src/config/reducers.js | 4 +- package.json | 1 + yarn.lock | 4 ++ 8 files changed, 104 insertions(+), 43 deletions(-) create mode 100644 app/javascript/src/components/actions/AlertsActions.js create mode 100644 app/javascript/src/components/reducers/AlertsReducers.js diff --git a/app/javascript/src/components/AlertsContainer/components/Alert.jsx b/app/javascript/src/components/AlertsContainer/components/Alert.jsx index 944e4560c..e8da50db0 100644 --- a/app/javascript/src/components/AlertsContainer/components/Alert.jsx +++ b/app/javascript/src/components/AlertsContainer/components/Alert.jsx @@ -74,13 +74,8 @@ class Alert extends Component { Alert.propTypes = { message: PropTypes.string.isRequired, type: PropTypes.string.isRequired, - timeout: PropTypes.number, - onClose: PropTypes.func -}; - -Alert.defaultProps = { - timeout: 5000, - onClose: undefined + timeout: PropTypes.number.isRequired, + onClose: PropTypes.func.isRequired }; export default Alert; \ No newline at end of file diff --git a/app/javascript/src/components/AlertsContainer/index.jsx b/app/javascript/src/components/AlertsContainer/index.jsx index b31c76c0d..274521423 100644 --- a/app/javascript/src/components/AlertsContainer/index.jsx +++ b/app/javascript/src/components/AlertsContainer/index.jsx @@ -1,8 +1,10 @@ import React, { Component } from "react"; +import { connect } from "react-redux"; import styled from "styled-components"; -import update from "immutability-helper"; import TransitionGroup from 'react-transition-group/TransitionGroup'; import CSSTransition from 'react-transition-group/CSSTransition'; +import PropTypes from "prop-types"; +import { clearAlert } from "../actions/AlertsActions"; import Alert from "./components/Alert"; const Wrapper = styled.div` @@ -15,36 +17,7 @@ class AlertsContainer extends Component { constructor(props) { super(props); - this.state = { - alerts: [] - }; - - this.add = this.add.bind(this); - this.clearAll = this.clearAll.bind(this); - this.clear = this.clear.bind(this); this.renderAlert = this.renderAlert.bind(this); - - // Bind self to global namespace - window.alerts = this; - } - - add(message, type, timeout) { - this.setState( - update( - this.state, - { alerts: { $push: [{ message, type, timeout }] } } - ) - ); - } - - clearAll() { - this.setState({ alerts: [] }); - } - - clear(alert) { - const index = this.state.alerts.indexOf(alert); - const alerts = update(this.state.alerts, { $splice: [[index, 1]] }); - this.setState({ alerts }); } renderAlert(alert) { @@ -52,7 +25,7 @@ class AlertsContainer extends Component { this.clear(alert)} + onClose={() => this.props.onAlertClose(alert.id)} /> ); } @@ -61,11 +34,11 @@ class AlertsContainer extends Component { return ( - {this.state.alerts.map((alert, index) => - + - {this.renderAlert(alert, index)} + {this.renderAlert(alert)} )} @@ -74,4 +47,25 @@ class AlertsContainer extends Component { } } -export default AlertsContainer; \ No newline at end of file +AlertsContainer.propTypes = { + alerts: PropTypes.arrayOf( + PropTypes.shape({ + message: PropTypes.string.isRequired, + type: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, + timeout: PropTypes.number, + onClose: PropTypes.func + }).isRequired + ).isRequired, + onAlertClose: PropTypes.func.isRequired +} + +const mapStateToProps = ({ alerts }) => ({ alerts }); + +const mapDispatchToProps = dispatch => ({ + onAlertClose(id) { + dispatch(clearAlert(id)); + } +}); + +export default connect(mapStateToProps, mapDispatchToProps)(AlertsContainer); \ No newline at end of file diff --git a/app/javascript/src/components/actions/AlertsActions.js b/app/javascript/src/components/actions/AlertsActions.js new file mode 100644 index 000000000..f6ff5b175 --- /dev/null +++ b/app/javascript/src/components/actions/AlertsActions.js @@ -0,0 +1,29 @@ +import shortid from "shortid"; +import { + ADD_ALERT, + CLEAR_ALERT, + CLEAR_ALL_ALERTS +} from "../../config/action_types"; + +export function addAlert(message, + type, + id = shortid.generate(), + timeout = 5000) { + return { + payload: { + message, + type, + id, + timeout + }, + type: ADD_ALERT + }; +} + +export function clearAlert(id) { + return { payload: id, type: CLEAR_ALERT } +} + +export function clearAllAlerts() { + return { type: CLEAR_ALL_ALERTS }; +} \ No newline at end of file diff --git a/app/javascript/src/components/reducers/AlertsReducers.js b/app/javascript/src/components/reducers/AlertsReducers.js new file mode 100644 index 000000000..098459ca6 --- /dev/null +++ b/app/javascript/src/components/reducers/AlertsReducers.js @@ -0,0 +1,31 @@ +import { + ADD_ALERT, + CLEAR_ALERT, + CLEAR_ALL_ALERTS +} from "../../config/action_types"; + +export const alerts = ( + state = [], + action +) => { + switch(action.type) { + case ADD_ALERT: + return [ + ...state, + { + message: action.payload.message, + type: action.payload.type, + id: action.payload.id, + timeout: action.payload.timeout + } + ]; + case CLEAR_ALERT: + return state.filter((alert) => ( + alert.id !== action.payload + )); + case CLEAR_ALL_ALERTS: + return []; + default: + return state; + } +}; \ No newline at end of file diff --git a/app/javascript/src/config/action_types.js b/app/javascript/src/config/action_types.js index b2d3a7443..732a81db4 100644 --- a/app/javascript/src/config/action_types.js +++ b/app/javascript/src/config/action_types.js @@ -35,3 +35,8 @@ export const UPDATE_TEAM_DESCRIPTION_MODAL = "UPDATE_TEAM_DESCRIPTION_MODAL"; // spinner export const SPINNER_ON = "SPINNER_ON"; export const SPINNER_OFF = "SPINNER_OFF"; + +// alerts +export const ADD_ALERT = "ADD_ALERT"; +export const CLEAR_ALERT = "CLEAR_ALERT"; +export const CLEAR_ALL_ALERTS = "CLEAR_ALL_ALERTS"; \ No newline at end of file diff --git a/app/javascript/src/config/reducers.js b/app/javascript/src/config/reducers.js index 2f00797ea..5561d4cb3 100644 --- a/app/javascript/src/config/reducers.js +++ b/app/javascript/src/config/reducers.js @@ -6,11 +6,13 @@ import { } from "../components/reducers/TeamReducers"; import { globalActivities } from "../components/reducers/ActivitiesReducers"; import { currentUser } from "../components/reducers/UsersReducer"; +import { alerts } from "../components/reducers/AlertsReducers"; export default combineReducers({ current_team: setCurrentTeam, all_teams: getListOfTeams, global_activities: globalActivities, current_user: currentUser, - showLeaveTeamModal + showLeaveTeamModal, + alerts }); diff --git a/package.json b/package.json index ef8f1c710..f87213bdd 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "redux-thunk": "^2.2.0", "resolve-url-loader": "^2.1.0", "sass-loader": "^6.0.6", + "shortid": "^2.2.8", "style-loader": "^0.18.2", "styled-components": "^2.1.1", "webpack": "^3.2.0", diff --git a/yarn.lock b/yarn.lock index 959663dcb..8a69711b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5313,6 +5313,10 @@ shelljs@^0.7.5: interpret "^1.0.0" rechoir "^0.6.2" +shortid@^2.2.8: + version "2.2.8" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.8.tgz#033b117d6a2e975804f6f0969dbe7d3d0b355131" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" From 296457963842ac65e8b31160a179834c70101aca Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Tue, 3 Oct 2017 11:14:47 +0200 Subject: [PATCH 8/8] Fix Zmago's comments --- .../AlertsContainer/components/Alert.jsx | 10 +++---- .../src/components/AlertsContainer/index.jsx | 28 ++++++++----------- app/javascript/src/styles/animations.scss | 4 +-- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/app/javascript/src/components/AlertsContainer/components/Alert.jsx b/app/javascript/src/components/AlertsContainer/components/Alert.jsx index e8da50db0..e664093a0 100644 --- a/app/javascript/src/components/AlertsContainer/components/Alert.jsx +++ b/app/javascript/src/components/AlertsContainer/components/Alert.jsx @@ -1,6 +1,6 @@ import React, { Component } from "react"; import styled from "styled-components"; -import PropTypes from "prop-types"; +import { string, number, func } from "prop-types"; import { Grid, Row, Col } from "react-bootstrap"; const Wrapper = styled.div` @@ -72,10 +72,10 @@ class Alert extends Component { } Alert.propTypes = { - message: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - timeout: PropTypes.number.isRequired, - onClose: PropTypes.func.isRequired + message: string.isRequired, + type: string.isRequired, + timeout: number.isRequired, + onClose: func.isRequired }; export default Alert; \ No newline at end of file diff --git a/app/javascript/src/components/AlertsContainer/index.jsx b/app/javascript/src/components/AlertsContainer/index.jsx index 274521423..0577b1ad0 100644 --- a/app/javascript/src/components/AlertsContainer/index.jsx +++ b/app/javascript/src/components/AlertsContainer/index.jsx @@ -3,7 +3,7 @@ import { connect } from "react-redux"; import styled from "styled-components"; import TransitionGroup from 'react-transition-group/TransitionGroup'; import CSSTransition from 'react-transition-group/CSSTransition'; -import PropTypes from "prop-types"; +import { shape, arrayOf, string, number, func } from "prop-types"; import { clearAlert } from "../actions/AlertsActions"; import Alert from "./components/Alert"; @@ -25,7 +25,7 @@ class AlertsContainer extends Component { this.props.onAlertClose(alert.id)} + onClose={() => this.props.clearAlert(alert.id)} /> ); } @@ -48,24 +48,18 @@ class AlertsContainer extends Component { } AlertsContainer.propTypes = { - alerts: PropTypes.arrayOf( - PropTypes.shape({ - message: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - id: PropTypes.string.isRequired, - timeout: PropTypes.number, - onClose: PropTypes.func + alerts: arrayOf( + shape({ + message: string.isRequired, + type: string.isRequired, + id: string.isRequired, + timeout: number, + onClose: func }).isRequired ).isRequired, - onAlertClose: PropTypes.func.isRequired + clearAlert: func.isRequired } const mapStateToProps = ({ alerts }) => ({ alerts }); -const mapDispatchToProps = dispatch => ({ - onAlertClose(id) { - dispatch(clearAlert(id)); - } -}); - -export default connect(mapStateToProps, mapDispatchToProps)(AlertsContainer); \ No newline at end of file +export default connect(mapStateToProps, { clearAlert })(AlertsContainer); \ No newline at end of file diff --git a/app/javascript/src/styles/animations.scss b/app/javascript/src/styles/animations.scss index 78d26dd64..32a90a5a4 100644 --- a/app/javascript/src/styles/animations.scss +++ b/app/javascript/src/styles/animations.scss @@ -1,5 +1,5 @@ .alert-animated-enter { - opacity: 0.01; + opacity: .01; &.alert-animated-enter-active { opacity: 1; @@ -16,7 +16,7 @@ &.alert-animated-exit-active { overflow: hidden; - opacity: 0.01; + opacity: .01; padding-top: 0; padding-bottom: 0; margin-bottom: 0;