From ab6afe95a39330b2ff621889175578c81c3a8a42 Mon Sep 17 00:00:00 2001 From: Todd Dembrey <todd.dembrey@torchbox.com> Date: Sun, 10 Feb 2019 15:58:14 +0000 Subject: [PATCH] GH-877: handle error message when fetching with existing data --- .../src/app/src/components/Listing/index.js | 44 +++++++++++++------ .../src/app/src/components/Listing/style.scss | 9 ++++ .../src/app/src/containers/NoteListing.js | 22 ++++++---- .../src/app/src/redux/reducers/notes.js | 15 +++++-- .../src/app/src/redux/selectors/notes.js | 4 +- 5 files changed, 67 insertions(+), 27 deletions(-) diff --git a/opentech/static_src/src/app/src/components/Listing/index.js b/opentech/static_src/src/app/src/components/Listing/index.js index b92e13362..76a7ce103 100644 --- a/opentech/static_src/src/app/src/components/Listing/index.js +++ b/opentech/static_src/src/app/src/components/Listing/index.js @@ -35,22 +35,25 @@ export default class Listing extends React.Component { listRef, } = this.props; - if (isLoading && !items) { - return ( - <div className="listing__list"> - <LoadingPanel /> - </div> - ); - } else if (isError) { - return this.renderError(); - } else if (items.length === 0) { - return <EmptyPanel column={this.props.column} />; + if ( items.length === 0 ) { + if (isLoading) { + return ( + <div className="listing__list"> + <LoadingPanel /> + </div> + ); + } else if (isError) { + return this.renderError(); + } else { + return <EmptyPanel column={this.props.column} />; + } } return ( <> { isLoading && <InlineLoading /> } <ul className={`listing__list listing__list--${column}`} ref={listRef}> + { isError && this.renderErrorItem() } <TransitionGroup component={null} > {items.map(v => renderItem(v))} </TransitionGroup> @@ -59,9 +62,24 @@ export default class Listing extends React.Component { ); } + retryButton = (handleRetry) => { + return <a className="listing__help-link" onClick={handleRetry}>Refresh</a>; + } + + renderErrorItem = () => { + const { handleRetry, error } = this.props; + return ( + <li className={`listing__item listing__item--error`}> + <h5>Something went wrong!</h5> + <p>{ error }</p> + { !navigator.onLine && <p>You appear to be offline.</p>} + { handleRetry && this.retryButton(handleRetry) } + </li> + ) + } + renderError = () => { const { handleRetry, error, column } = this.props; - const retryButton = <a className="listing__help-link" onClick={handleRetry}>Refresh</a>; return ( <div className={`listing__list listing__list--${column} is-blank`}> @@ -71,14 +89,14 @@ export default class Listing extends React.Component { <p>Something went wrong!</p> } - {handleRetry && retryButton && + {handleRetry && <> <div className="listing__blank-icon"> <SadNoteIcon /> </div> <p className="listing__help-text listing__help-text--standout">Something went wrong!</p> <p className="listing__help-text">Sorry we couldn't load the notes</p> - {retryButton} + { this.handleRetry(handleRetry) } </> } </div> diff --git a/opentech/static_src/src/app/src/components/Listing/style.scss b/opentech/static_src/src/app/src/components/Listing/style.scss index 618ac1019..0f3b6ca6b 100644 --- a/opentech/static_src/src/app/src/components/Listing/style.scss +++ b/opentech/static_src/src/app/src/components/Listing/style.scss @@ -53,6 +53,14 @@ &__item { @include submission-list-item; + &--error { + padding: 5px 20px 5px; + + p { + margin: 5px 0px; + } + } + &.is-active { @include target-edge { margin-left: 8px; @@ -71,6 +79,7 @@ } } + // <a> tags &__link { display: block; diff --git a/opentech/static_src/src/app/src/containers/NoteListing.js b/opentech/static_src/src/app/src/containers/NoteListing.js index b278537d9..587bc3d0e 100644 --- a/opentech/static_src/src/app/src/containers/NoteListing.js +++ b/opentech/static_src/src/app/src/containers/NoteListing.js @@ -8,6 +8,7 @@ import Listing from '@components/Listing'; import Note from '@containers/Note'; import { getNotesErrorState, + getNotesErrorMessage, getNoteIDsForSubmissionOfID, getNotesFetchState, } from '@selectors/notes'; @@ -19,6 +20,7 @@ class NoteListing extends React.Component { submissionID: PropTypes.number, noteIDs: PropTypes.array, isErrored: PropTypes.bool, + errorMessage: PropTypes.string, isLoading: PropTypes.bool, }; @@ -63,16 +65,17 @@ class NoteListing extends React.Component { } render() { - const { noteIDs } = this.props; - const passProps = { - isLoading: this.props.isLoading, - isError: this.props.isErrored, - handleRetry: this.handleRetry, - renderItem: this.renderItem, - items: noteIDs, - }; + const { noteIDs, isLoading, isErrored, errorMessage } = this.props; return ( - <Listing {...passProps} column="notes" /> + <Listing + isLoading={ isLoading } + isError={ isErrored } + error={ errorMessage } + handleRetry={ this.handleRetry } + renderItem={ this.renderItem } + items={ noteIDs } + column="notes" + /> ); } } @@ -85,6 +88,7 @@ const mapStateToProps = (state, ownProps) => ({ noteIDs: getNoteIDsForSubmissionOfID(ownProps.submissionID)(state), isLoading: getNotesFetchState(state), isErrored: getNotesErrorState(state), + errorMessage: getNotesErrorMessage(state), }); export default connect(mapStateToProps, mapDispatchToProps)(NoteListing); diff --git a/opentech/static_src/src/app/src/redux/reducers/notes.js b/opentech/static_src/src/app/src/redux/reducers/notes.js index 4c4a196fd..f27e5d351 100644 --- a/opentech/static_src/src/app/src/redux/reducers/notes.js +++ b/opentech/static_src/src/app/src/redux/reducers/notes.js @@ -21,13 +21,20 @@ function notesFetching(state = false, action) { } } -function notesErrored(state = false, action) { +function notesErrored(state = {errored: false, message: null}, action) { switch (action.type) { case UPDATE_NOTES: case START_FETCHING_NOTES: - return false; + return { + ...state, + errored: false, + }; case FAIL_FETCHING_NOTES: - return true; + return { + ...state, + message: action.error, + errored: true, + }; default: return state; } @@ -110,7 +117,7 @@ function notesByID(state = {}, action) { export default combineReducers({ byID: notesByID, isFetching: notesFetching, - isErrored: notesErrored, + error: notesErrored, createError: notesFailedCreating, isCreating: notesCreating, }); diff --git a/opentech/static_src/src/app/src/redux/selectors/notes.js b/opentech/static_src/src/app/src/redux/selectors/notes.js index 98af76b0a..dd91f2cc0 100644 --- a/opentech/static_src/src/app/src/redux/selectors/notes.js +++ b/opentech/static_src/src/app/src/redux/selectors/notes.js @@ -10,7 +10,9 @@ export const getNoteOfID = (noteID) => createSelector( export const getNotesFetchState = state => state.notes.isFetching === true; -export const getNotesErrorState = state => state.notes.isErrored === true; +export const getNotesErrorState = state => state.notes.error.errored === true; + +export const getNotesErrorMessage = state => state.notes.error.message; export const getNoteIDsForSubmissionOfID = submissionID => createSelector( [getSubmissionOfID(submissionID)], -- GitLab