From b1956a7ba17565aca813a41c22e5ab0d5e02fca8 Mon Sep 17 00:00:00 2001 From: Hemakshi Sachdev Date: Sun, 9 Jun 2019 05:25:55 +0530 Subject: [PATCH] fix: flash modals stack up and are not visible until scrolled to top (#35808) Co-authored-by: Mrugesh Mohapatra <1884376+raisedadead@users.noreply.github.com> --- client/package-lock.json | 161 +++++++++++++++++++-- client/package.json | 3 +- client/src/components/Flash/flash.css | 19 ++- client/src/components/Flash/index.js | 39 +++-- client/src/components/Flash/redux/index.js | 10 +- client/src/components/layouts/Default.js | 32 ++-- 6 files changed, 205 insertions(+), 59 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 69019b184b..cb89444ec7 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1064,6 +1064,48 @@ "loose-envify": "^1.3.1", "object-assign": "^4.1.1" } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + } + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" } } }, @@ -16213,6 +16255,65 @@ "prop-types-extra": "^1.0.1", "react-transition-group": "^2.2.0", "warning": "^3.0.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + } + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + } } }, "react-prop-types": { @@ -16374,24 +16475,58 @@ } }, "react-transition-group": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.4.0.tgz", - "integrity": "sha512-Xv5d55NkJUxUzLCImGSanK8Cl/30sgpOEMGc5m86t8+kZwrPxPCPcFqyx83kkr+5Lz5gs6djuvE5By+gce+VjA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.1.0.tgz", + "integrity": "sha512-/OITbogb3emGN49WaP7468QGSde7er5w6eIHldIDCSQBq/9QTSCzs8OgpgmOnaUXCXzBUcK1zoZ6DqRlM8CJtA==", "requires": { - "dom-helpers": "^3.3.1", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" }, "dependencies": { - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "@babel/runtime": { + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "regenerator-runtime": "^0.13.2" } + }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.8.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", + "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" } } }, diff --git a/client/package.json b/client/package.json index 519063e957..22b4811b2b 100644 --- a/client/package.json +++ b/client/package.json @@ -44,15 +44,16 @@ "react": "^16.8.6", "react-calendar-heatmap": "^1.8.0", "react-dom": "^16.8.6", - "react-instantsearch-dom": "^5.5.0", "react-ga": "^2.5.7", "react-helmet": "^5.2.0", + "react-instantsearch-dom": "^5.5.0", "react-monaco-editor": "^0.25.1", "react-redux": "^5.0.7", "react-reflex": "^3.0.13", "react-responsive": "^6.1.1", "react-spinkit": "^3.0.0", "react-stripe-elements": "^2.0.3", + "react-transition-group": "^4.1.0", "react-youtube": "^7.9.0", "redux": "^4.0.1", "redux-actions": "^2.6.5", diff --git a/client/src/components/Flash/flash.css b/client/src/components/Flash/flash.css index 51c5e401d1..7a5123e2b6 100644 --- a/client/src/components/Flash/flash.css +++ b/client/src/components/Flash/flash.css @@ -1,7 +1,24 @@ .flash-message { display: flex; justify-content: space-around; - align-items: center; flex-direction: row-reverse; margin-bottom: 0px; + position: fixed; + width: 100%; + z-index: 1000; +} + +.flash-message-enter { + opacity: 0; +} +.flash-message-enter-active { + opacity: 1; + transition: opacity 500ms; +} +.flash-message-exit { + opacity: 1; +} +.flash-message-exit-active { + opacity: 0; + transition: opacity 100ms; } diff --git a/client/src/components/Flash/index.js b/client/src/components/Flash/index.js index 340ef66859..c312b33d1b 100644 --- a/client/src/components/Flash/index.js +++ b/client/src/components/Flash/index.js @@ -1,35 +1,30 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Alert } from '@freecodecamp/react-bootstrap'; +import { TransitionGroup, CSSTransition } from 'react-transition-group'; import './flash.css'; -function createDismissHandler(fn, id) { - return () => fn(id); -} - -function Flash({ messages, onClose }) { - return messages.map(({ type, message, id }) => ( - -
- - )); +function Flash({ flashMessage, onClose }) { + const { type, message, id } = flashMessage; + return ( + + + +
+ + + + ); } Flash.displayName = 'FlashMessages'; Flash.propTypes = { - messages: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string, - type: PropTypes.string, - message: PropTypes.string - }) - ), + flashMessage: PropTypes.shape({ + id: PropTypes.string, + type: PropTypes.string, + message: PropTypes.string + }), onClose: PropTypes.func.isRequired }; diff --git a/client/src/components/Flash/redux/index.js b/client/src/components/Flash/redux/index.js index 905a35d6da..69da6981c5 100644 --- a/client/src/components/Flash/redux/index.js +++ b/client/src/components/Flash/redux/index.js @@ -6,7 +6,7 @@ import { createTypes } from '../../../utils/createTypes'; export const ns = 'flash'; const initialState = { - messages: [] + message: {} }; const types = createTypes(['createFlashMessage', 'removeFlashMessage'], ns); @@ -19,17 +19,17 @@ export const createFlashMessage = createAction( ); export const removeFlashMessage = createAction(types.removeFlashMessage); -export const flashMessagesSelector = state => state[ns].messages; +export const flashMessageSelector = state => state[ns].message; export const reducer = handleActions( { [types.createFlashMessage]: (state, { payload }) => ({ ...state, - messages: [...state.messages, payload] + message: payload }), - [types.removeFlashMessage]: (state, { payload }) => ({ + [types.removeFlashMessage]: state => ({ ...state, - messages: state.messages.filter(msg => msg.id !== payload) + message: {} }) }, initialState diff --git a/client/src/components/layouts/Default.js b/client/src/components/layouts/Default.js index 84ac3d5f52..6c161cca0a 100644 --- a/client/src/components/layouts/Default.js +++ b/client/src/components/layouts/Default.js @@ -13,7 +13,7 @@ import { onlineStatusChange, isOnlineSelector } from '../../redux'; -import { flashMessagesSelector, removeFlashMessage } from '../Flash/redux'; +import { flashMessageSelector, removeFlashMessage } from '../Flash/redux'; import { isBrowser } from '../../../utils'; @@ -58,14 +58,12 @@ const metaKeywords = [ const propTypes = { children: PropTypes.node.isRequired, fetchUser: PropTypes.func.isRequired, - flashMessages: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string, - type: PropTypes.string, - message: PropTypes.string - }) - ), - hasMessages: PropTypes.bool, + flashMessage: PropTypes.shape({ + id: PropTypes.string, + type: PropTypes.string, + message: PropTypes.string + }), + hasMessage: PropTypes.bool, isOnline: PropTypes.bool.isRequired, isSignedIn: PropTypes.bool, landingPage: PropTypes.bool, @@ -78,12 +76,12 @@ const propTypes = { const mapStateToProps = createSelector( isSignedInSelector, - flashMessagesSelector, + flashMessageSelector, isOnlineSelector, - (isSignedIn, flashMessages, isOnline) => ({ + (isSignedIn, flashMessage, isOnline) => ({ isSignedIn, - flashMessages, - hasMessages: !!flashMessages.length, + flashMessage, + hasMessage: !!flashMessage.message, isOnline }) ); @@ -128,8 +126,8 @@ class DefaultLayout extends Component { render() { const { children, - hasMessages, - flashMessages = [], + hasMessage, + flashMessage, isOnline, isSignedIn, landingPage, @@ -162,8 +160,8 @@ class DefaultLayout extends Component { className={`default-layout ${landingPage ? 'landing-page' : ''}`} > - {hasMessages ? ( - + {hasMessage && flashMessage ? ( + ) : null} {children}