From d2aa71ea01cec53222d6f0d3a7671ae8860f5175 Mon Sep 17 00:00:00 2001 From: Kyle Doherty <kdoh@users.noreply.github.com> Date: Thu, 31 May 2018 11:43:07 -0700 Subject: [PATCH] Improve Toast workflow (#7755) * initial version of withToast * add dark card, use for undo item * use triggerToast on AutomaticDashboardApp * betterify * fix flow --- frontend/src/metabase/components/Card.jsx | 9 ++- .../src/metabase/containers/UndoListing.css | 43 ----------- .../src/metabase/containers/UndoListing.jsx | 73 +++++++++---------- .../containers/AutomaticDashboardApp.jsx | 37 ++++------ frontend/src/metabase/hoc/Toast.jsx | 37 ++++++++++ .../internal/components/ColorsApp.jsx | 28 ++----- frontend/src/metabase/redux/undo.js | 2 +- 7 files changed, 101 insertions(+), 128 deletions(-) delete mode 100644 frontend/src/metabase/containers/UndoListing.css create mode 100644 frontend/src/metabase/hoc/Toast.jsx diff --git a/frontend/src/metabase/components/Card.jsx b/frontend/src/metabase/components/Card.jsx index c3969b2ab0b..d4e66039514 100644 --- a/frontend/src/metabase/components/Card.jsx +++ b/frontend/src/metabase/components/Card.jsx @@ -3,14 +3,15 @@ import { space } from "styled-system"; import { normal } from "metabase/lib/colors"; const Card = styled.div` - ${space} background-color: white; - border: 1px solid #f5f6f7; + ${space} background-color: ${props => (props.dark ? "#2e353b" : "white")}; + border: 1px solid ${props => (props.dark ? "transparent" : "#f5f6f7")}; + ${props => props.dark && `color: white`}; border-radius: 6px; - box-shadow: 0 1px 3px ${normal.grey1}; + box-shadow: 0 1px 3px ${props => (props.dark ? "#65686b" : normal.grey1)}; ${props => props.hoverable && `&:hover { - box-shadow: 0 2px 3px #DCE1E4; + box-shadow: 0 2px 3px ${props.dark ? "#2e35b" : "#DCE1E4"}; }`}; `; diff --git a/frontend/src/metabase/containers/UndoListing.css b/frontend/src/metabase/containers/UndoListing.css deleted file mode 100644 index e9c00660e9b..00000000000 --- a/frontend/src/metabase/containers/UndoListing.css +++ /dev/null @@ -1,43 +0,0 @@ -:local(.listing) { - composes: m2 from "style"; - composes: fixed left bottom from "style"; - z-index: 99; -} - -:local(.undo) { - composes: mt2 p2 from "style"; - composes: bordered rounded shadowed from "style"; - composes: relative from "style"; - composes: flex align-center from "style"; - background-color: #2e353b; - color: white; -} - -:local(.actions) { - composes: flex align-center flex-align-right from "style"; -} - -:local(.undoButton) { - composes: mx2 from "style"; - composes: text-uppercase text-bold from "style"; - color: var(--brand-color); -} -:local(.dismissButton) { - composes: cursor-pointer from "style"; - color: var(--grey-1); -} -:local(.dismissButton):hover { - color: var(--grey-3); -} - -/* enter and exit initial and final state */ -.UndoListing-enter, -.UndoListing-leave.UndoListing-leave-active { - opacity: 0.01; - transition: opacity 300ms ease-in; -} - -.UndoListing-leave, -.UndoListing-enter.UndoListing-enter-active { - opacity: 1; -} diff --git a/frontend/src/metabase/containers/UndoListing.jsx b/frontend/src/metabase/containers/UndoListing.jsx index c92cf2451b7..931c1fc7f14 100644 --- a/frontend/src/metabase/containers/UndoListing.jsx +++ b/frontend/src/metabase/containers/UndoListing.jsx @@ -2,29 +2,33 @@ import React, { Component } from "react"; import PropTypes from "prop-types"; import { connect } from "react-redux"; +import styled from "styled-components"; +import { space } from "styled-system"; +import { Flex } from "rebass"; +import { t } from "c-3po"; -import S from "./UndoListing.css"; - +import { normal } from "metabase/lib/colors"; import { dismissUndo, performUndo } from "metabase/redux/undo"; import { getUndos } from "metabase/selectors/undo"; -import { t } from "c-3po"; -import Icon from "metabase/components/Icon"; import BodyComponent from "metabase/components/BodyComponent"; +import Card from "metabase/components/Card"; +import Icon from "metabase/components/Icon"; +import Link from "metabase/components/Link"; -import { CSSTransitionGroup } from "react-transition-group"; - -const mapStateToProps = (state, props) => { - return { - undos: getUndos(state, props), - }; -}; +const mapStateToProps = (state, props) => ({ + undos: getUndos(state, props), +}); const mapDispatchToProps = { dismissUndo, performUndo, }; +const UndoList = styled.ul` + ${space}, z-index: 99; +`; + @connect(mapStateToProps, mapDispatchToProps) @BodyComponent export default class UndoListing extends Component { @@ -37,37 +41,28 @@ export default class UndoListing extends Component { render() { const { undos, performUndo, dismissUndo } = this.props; return ( - <ul className={S.listing}> - <CSSTransitionGroup - transitionName="UndoListing" - transitionEnterTimeout={300} - transitionLeaveTimeout={300} - > - {undos.map(undo => ( - <li key={undo._domId} className={S.undo}> - <div className={S.message}> - {typeof undo.message === "function" - ? undo.message(undo) - : undo.message} - </div> + <UndoList m={2} className="fixed left bottom"> + {undos.map(undo => ( + <Card key={undo._domId} dark p={2}> + <Flex align="center"> + {typeof undo.message === "function" + ? undo.message(undo) + : undo.message} {undo.actions && ( - <div className={S.actions}> - <a - className={S.undoButton} - onClick={() => performUndo(undo.id)} - >{t`Undo`}</a> - <Icon - className={S.dismissButton} - name="close" - onClick={() => dismissUndo(undo.id)} - /> - </div> + <Link onClick={() => performUndo(undo.id)}>{t`Undo`}</Link> )} - </li> - ))} - </CSSTransitionGroup> - </ul> + <Icon + ml={1} + color={normal.grey1} + hover={{ color: normal.grey2 }} + name="close" + onClick={() => dismissUndo(undo.id)} + /> + </Flex> + </Card> + ))} + </UndoList> ); } } diff --git a/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx b/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx index 05f7e18cb2d..ff26f9efd1a 100644 --- a/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx +++ b/frontend/src/metabase/dashboard/containers/AutomaticDashboardApp.jsx @@ -6,6 +6,7 @@ import { connect } from "react-redux"; import { Link } from "react-router"; import title from "metabase/hoc/Title"; +import withToast from "metabase/hoc/Toast"; import ActionButton from "metabase/components/ActionButton"; import Button from "metabase/components/Button"; import Icon from "metabase/components/Icon"; @@ -18,8 +19,6 @@ import { Dashboard } from "metabase/dashboard/containers/Dashboard"; import DashboardData from "metabase/dashboard/hoc/DashboardData"; import Parameters from "metabase/parameters/components/Parameters"; -import { addUndo, createUndo } from "metabase/redux/undo"; - import { getMetadata } from "metabase/selectors/metadata"; import { getUserIsAdmin } from "metabase/selectors/user"; @@ -40,8 +39,9 @@ const mapStateToProps = (state, props) => ({ dashboardId: getDashboardId(state, props), }); -@connect(mapStateToProps, { addUndo, createUndo }) +@connect(mapStateToProps) @DashboardData +@withToast @title(({ dashboard }) => dashboard && dashboard.name) class AutomaticDashboardApp extends React.Component { state = { @@ -56,27 +56,22 @@ class AutomaticDashboardApp extends React.Component { } save = async () => { - const { dashboard, addUndo, createUndo } = this.props; + const { dashboard, triggerToast } = this.props; // remove the transient id before trying to save const newDashboard = await DashboardApi.save(dissoc(dashboard, "id")); - addUndo( - createUndo({ - type: "metabase/automatic-dashboards/link-to-created-object", - message: () => ( - <div className="flex align-center"> - <Icon name="dashboard" size={22} className="mr2" color="#93A1AB" /> - {t`Your dashboard was saved`} - <Link - className="link text-bold ml1" - to={Urls.dashboard(newDashboard.id)} - > - {t`See it`} - </Link> - </div> - ), - action: null, - }), + triggerToast( + <div className="flex align-center"> + <Icon name="dashboard" size={22} className="mr2" color="#93A1AB" /> + {t`Your dashboard was saved`} + <Link + className="link text-bold ml1" + to={Urls.dashboard(newDashboard.id)} + > + {t`See it`} + </Link> + </div>, ); + this.setState({ savedDashboardId: newDashboard.id }); MetabaseAnalytics.trackEvent("AutoDashboard", "Save"); }; diff --git a/frontend/src/metabase/hoc/Toast.jsx b/frontend/src/metabase/hoc/Toast.jsx new file mode 100644 index 00000000000..6539da55cd8 --- /dev/null +++ b/frontend/src/metabase/hoc/Toast.jsx @@ -0,0 +1,37 @@ +import React from "react"; +import { connect } from "react-redux"; + +import { createUndo, addUndo } from "metabase/redux/undo"; + +const mapDispatchToProps = { + createUndo, + addUndo, +}; + +const withToaster = ComposedComponent => { + @connect(null, mapDispatchToProps) + class ToastedComponent extends React.Component { + _triggerToast = toastContent => { + const { addUndo, createUndo } = this.props; + + addUndo( + createUndo({ + type: "toast", + message: toastContent, + }), + ); + }; + render() { + return ( + <ComposedComponent + triggerToast={this._triggerToast} + // TODO - omit createUndo, addUndo + {...this.props} + /> + ); + } + } + return ToastedComponent; +}; + +export default withToaster; diff --git a/frontend/src/metabase/internal/components/ColorsApp.jsx b/frontend/src/metabase/internal/components/ColorsApp.jsx index 97d506c04f1..0cdca998075 100644 --- a/frontend/src/metabase/internal/components/ColorsApp.jsx +++ b/frontend/src/metabase/internal/components/ColorsApp.jsx @@ -1,35 +1,23 @@ -/* @flow */ import React from "react"; import cx from "classnames"; import { Box, Flex, Subhead } from "rebass"; -import { connect } from "react-redux"; import CopyToClipboard from "react-copy-to-clipboard"; -import { addUndo, createUndo } from "metabase/redux/undo"; import { normal, saturated, harmony } from "metabase/lib/colors"; -const SWATCH_SIZE = 150; +import withToast from "metabase/hoc/Toast"; -const mapDispatchToProps = { - addUndo, - createUndo, -}; +const SWATCH_SIZE = 150; -@connect(() => ({}), mapDispatchToProps) +@withToast class ColorSwatch extends React.Component { - _onCopy(colorValue) { - const { addUndo, createUndo } = this.props; - addUndo( - createUndo({ - type: "copy-color", - message: <div>Copied {colorValue} to clipboard</div>, - }), - ); - } render() { - const { color, name } = this.props; + const { color, name, triggerToast } = this.props; return ( - <CopyToClipboard text={color} onCopy={() => this._onCopy(color)}> + <CopyToClipboard + text={color} + onCopy={() => triggerToast(`${color} copied to clipboard`)} + > <Box w={SWATCH_SIZE} style={{ diff --git a/frontend/src/metabase/redux/undo.js b/frontend/src/metabase/redux/undo.js index 0448f6f3efe..b3041ca7a8c 100644 --- a/frontend/src/metabase/redux/undo.js +++ b/frontend/src/metabase/redux/undo.js @@ -14,7 +14,7 @@ let nextUndoId = 0; // A convenience shorthand for creating single undos export function createUndo({ type, message, action }) { return { - type: type, + type, count: 1, message, actions: action ? [action] : null, -- GitLab