diff --git a/circle.yml b/circle.yml index 51020211c..875af71cc 100644 --- a/circle.yml +++ b/circle.yml @@ -12,7 +12,7 @@ machine: dependencies: override: - - yarn + - yarn install --pure-lockfile cache_directories: - ~/.cache/yarn diff --git a/frontend/components/Layout/Layout.js b/frontend/components/Layout/Layout.js index af08b23a1..ae63413a3 100644 --- a/frontend/components/Layout/Layout.js +++ b/frontend/components/Layout/Layout.js @@ -13,6 +13,7 @@ import Avatar from 'components/Avatar'; import { LoadingIndicatorBar } from 'components/LoadingIndicator'; import Scrollable from 'components/Scrollable'; import Icon from 'components/Icon'; +import Toasts from 'components/Toasts'; import CollectionMenu from 'menus/CollectionMenu'; import AccountMenu from 'menus/AccountMenu'; @@ -153,6 +154,7 @@ type Props = { + ); } diff --git a/frontend/components/Toasts/Toasts.js b/frontend/components/Toasts/Toasts.js new file mode 100644 index 000000000..fd1457c6f --- /dev/null +++ b/frontend/components/Toasts/Toasts.js @@ -0,0 +1,39 @@ +// @flow +import React, { Component } from 'react'; +import { inject, observer } from 'mobx-react'; +import styled from 'styled-components'; +import { layout } from 'styles/constants'; +import Toast from './components/Toast'; + +@observer class Toasts extends Component { + handleClose = index => { + this.props.errors.remove(index); + }; + + render() { + const { errors } = this.props; + + return ( + + {errors.data.map((error, index) => ( + + ))} + + ); + } +} + +const List = styled.ol` + position: fixed; + left: ${layout.hpadding}; + bottom: ${layout.vpadding}; + list-style: none; + margin: 0; + padding: 0; +`; + +export default inject('errors')(Toasts); diff --git a/frontend/components/Toasts/components/Toast.js b/frontend/components/Toasts/components/Toast.js new file mode 100644 index 000000000..d4a96f5f4 --- /dev/null +++ b/frontend/components/Toasts/components/Toast.js @@ -0,0 +1,71 @@ +// @flow +import React, { Component } from 'react'; +import styled from 'styled-components'; +import { darken } from 'polished'; +import { color } from 'styles/constants'; +import { fadeAndScaleIn } from 'styles/animations'; +import Icon from 'components/Icon'; + +type Props = { + onRequestClose: () => void, + closeAfterMs: number, + message: string, + type: 'warning' | 'error' | 'info', +}; + +class Toast extends Component { + timeout: number; + props: Props; + + static defaultProps = { + closeAfterMs: 3000, + type: 'warning', + }; + + componentDidMount() { + this.timeout = setTimeout( + this.props.onRequestClose, + this.props.closeAfterMs + ); + } + + componentWillUnmount() { + clearTimeout(this.timeout); + } + + render() { + const { type, onRequestClose, message } = this.props; + + return ( + + {type === 'info' + ? + : } + {message} + + ); + } +} + +const Container = styled.li` + display: flex; + align-items: center; + animation: ${fadeAndScaleIn} 100ms ease; + margin: 8px 0; + padding: 8px; + color: ${color.white}; + background: ${props => color[props.type]}; + font-size: 15px; + border-radius: 5px; + cursor: default; + + &:hover { + background: ${props => darken(0.05, color[props.type])}; + } +`; + +const Message = styled.div` + padding-left: 5px; +`; + +export default Toast; diff --git a/frontend/components/Toasts/index.js b/frontend/components/Toasts/index.js new file mode 100644 index 000000000..f0b2732f0 --- /dev/null +++ b/frontend/components/Toasts/index.js @@ -0,0 +1,3 @@ +// @flow +import Toasts from './Toasts'; +export default Toasts; diff --git a/frontend/scenes/CollectionNew/CollectionNew.js b/frontend/scenes/CollectionNew/CollectionNew.js index e52d28eef..591135729 100644 --- a/frontend/scenes/CollectionNew/CollectionNew.js +++ b/frontend/scenes/CollectionNew/CollectionNew.js @@ -49,7 +49,6 @@ type Props = { render() { return (
- {this.collection.errors.errors.map(error => {error})} Collections are for grouping your Atlas. They work best when organized around a topic or internal team — Product or Engineering for example. diff --git a/frontend/scenes/Search/Search.js b/frontend/scenes/Search/Search.js index b6ca7c8ca..4d5c07e55 100644 --- a/frontend/scenes/Search/Search.js +++ b/frontend/scenes/Search/Search.js @@ -159,17 +159,17 @@ const StyledArrowKeyNavigation = styled(ArrowKeyNavigation)` > {this.resultIds.map((documentId, index) => { const document = documents.getById(documentId); - if (document) - return ( - - index === 0 && this.setFirstDocumentRef(ref)} - key={documentId} - document={document} - highlight={this.searchTerm} - showCollection - /> - ); + if (!document) return null; + return ( + + index === 0 && this.setFirstDocumentRef(ref)} + key={documentId} + document={document} + highlight={this.searchTerm} + showCollection + /> + ); })} diff --git a/frontend/stores/ErrorsStore.js b/frontend/stores/ErrorsStore.js index 02dc9739c..36f61c333 100644 --- a/frontend/stores/ErrorsStore.js +++ b/frontend/stores/ErrorsStore.js @@ -2,16 +2,16 @@ import { observable, action } from 'mobx'; class ErrorsStore { - @observable errors = observable.array([]); + @observable data = observable.array([]); /* Actions */ - @action add = (errorMessage: string): void => { - this.errors.push(errorMessage); + @action add = (message: string): void => { + this.data.push(message); }; @action remove = (index: number): void => { - this.errors.splice(index, 1); + this.data.splice(index, 1); }; } diff --git a/frontend/stores/ErrorsStore.test.js b/frontend/stores/ErrorsStore.test.js index b76de357a..daa72a016 100644 --- a/frontend/stores/ErrorsStore.test.js +++ b/frontend/stores/ErrorsStore.test.js @@ -10,18 +10,18 @@ describe('ErrorsStore', () => { }); test('#add should add errors', () => { - expect(store.errors.length).toBe(0); + expect(store.data.length).toBe(0); store.add('first error'); store.add('second error'); - expect(store.errors.length).toBe(2); + expect(store.data.length).toBe(2); }); test('#remove should remove errors', () => { store.add('first error'); store.add('second error'); - expect(store.errors.length).toBe(2); + expect(store.data.length).toBe(2); store.remove(0); - expect(store.errors.length).toBe(1); - expect(store.errors[0]).toBe('second error'); + expect(store.data.length).toBe(1); + expect(store.data[0]).toBe('second error'); }); });