Skip to content

Commit

Permalink
Added spinner and error message for profile page (#661)
Browse files Browse the repository at this point in the history
  • Loading branch information
George Schneeloch authored Jun 30, 2016
1 parent a62a0b3 commit 03003e9
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 29 deletions.
8 changes: 4 additions & 4 deletions static/js/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export const receiveGetUserProfileSuccess = (username, profile) => ({
payload: { profile, username }
});

const receiveGetUserProfileFailure = username => ({
const receiveGetUserProfileFailure = (username, errorInfo) => ({
type: RECEIVE_GET_USER_PROFILE_FAILURE,
payload: { username }
payload: { username, errorInfo }
});

export const clearProfile = username => ({
Expand Down Expand Up @@ -89,8 +89,8 @@ export function fetchUserProfile(username) {
dispatch(requestGetUserProfile(username));
return api.getUserProfile(username).
then(json => dispatch(receiveGetUserProfileSuccess(username, json))).
catch(() => {
dispatch(receiveGetUserProfileFailure(username));
catch(error => {
dispatch(receiveGetUserProfileFailure(username, error));
// the exception is assumed handled and will not be propagated
});
};
Expand Down
2 changes: 1 addition & 1 deletion static/js/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ export const DASHBOARD_RESPONSE = [
},
];

export const DASHBOARD_RESPONSE_ERROR = {
export const ERROR_RESPONSE = {
"errorStatusCode": 500,
"error_code": "AB123",
"user_message": "custom error message for the user."
Expand Down
18 changes: 9 additions & 9 deletions static/js/containers/DashboardPage_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '../actions';
import {
DASHBOARD_RESPONSE,
DASHBOARD_RESPONSE_ERROR,
ERROR_RESPONSE,
} from '../constants';
import IntegrationTestHelper from '../util/integration_test_helper';

Expand Down Expand Up @@ -47,28 +47,28 @@ describe('DashboardPage', () => {
errorString = errorString.replace(/\s\s+/g, ' ');

it('error from the backend triggers error message in dashboard', () => {
helper.dashboardStub.returns(Promise.reject(DASHBOARD_RESPONSE_ERROR));
helper.dashboardStub.returns(Promise.reject(ERROR_RESPONSE));

return renderComponent("/dashboard", dashboardErrorActions, false).then(([, div]) => {
let message = div.getElementsByClassName('alert-message')[0];
assert(message.textContent.indexOf(errorString) > -1);
assert(message.textContent.indexOf(DASHBOARD_RESPONSE_ERROR.error_code) > -1);
assert(message.textContent.indexOf(ERROR_RESPONSE.error_code) > -1);
assert(message.textContent.indexOf("Additional info:") > -1);
assert(message.textContent.indexOf(DASHBOARD_RESPONSE_ERROR.user_message) > -1);
assert(message.textContent.indexOf(ERROR_RESPONSE.user_message) > -1);
});
});

it('the error from the backend does not need to be complete', () => {
let response = _.cloneDeep(DASHBOARD_RESPONSE_ERROR);
let response = _.cloneDeep(ERROR_RESPONSE);
delete response.user_message;
helper.dashboardStub.returns(Promise.reject(response));

return renderComponent("/dashboard", dashboardErrorActions, false).then(([, div]) => {
let message = div.getElementsByClassName('alert-message')[0];
assert(message.textContent.indexOf(errorString) > -1);
assert(message.textContent.indexOf(DASHBOARD_RESPONSE_ERROR.error_code) > -1);
assert(message.textContent.indexOf(ERROR_RESPONSE.error_code) > -1);
assert.equal(message.textContent.indexOf("Additional info:"), -1);
assert.equal(message.textContent.indexOf(DASHBOARD_RESPONSE_ERROR.user_message), -1);
assert.equal(message.textContent.indexOf(ERROR_RESPONSE.user_message), -1);
});
});

Expand All @@ -78,9 +78,9 @@ describe('DashboardPage', () => {
return renderComponent("/dashboard", dashboardErrorActions, false).then(([, div]) => {
let message = div.getElementsByClassName('alert-message')[0];
assert(message.textContent.indexOf(errorString) > -1);
assert.equal(message.textContent.indexOf(DASHBOARD_RESPONSE_ERROR.error_code), -1);
assert.equal(message.textContent.indexOf(ERROR_RESPONSE.error_code), -1);
assert.equal(message.textContent.indexOf("Additional info:"), -1);
assert.equal(message.textContent.indexOf(DASHBOARD_RESPONSE_ERROR.user_message), -1);
assert.equal(message.textContent.indexOf(ERROR_RESPONSE.user_message), -1);
});
});

Expand Down
49 changes: 35 additions & 14 deletions static/js/containers/ProfilePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@
/* global SETTINGS */
import React from 'react';
import { connect } from 'react-redux';
import Loader from 'react-loader';

import { getPreferredName } from '../util/util';
import {
getPreferredName,
makeProfileProgressDisplay,
} from '../util/util';
import { FETCH_PROCESSING } from '../actions';
import Jumbotron from '../components/Jumbotron';
import { makeProfileProgressDisplay } from '../util/util';
import ErrorMessage from '../components/ErrorMessage';
import ProfileFormContainer from './ProfileFormContainer';
import PersonalTab from '../components/PersonalTab';
import EmploymentTab from '../components/EmploymentTab';
Expand All @@ -15,7 +20,7 @@ import {
PERSONAL_STEP,
EDUCATION_STEP,
EMPLOYMENT_STEP,
PRIVACY_STEP
PRIVACY_STEP,
} from '../constants';

class ProfilePage extends ProfileFormContainer {
Expand Down Expand Up @@ -56,27 +61,43 @@ class ProfilePage extends ProfileFormContainer {

render() {
const { profiles } = this.props;
const profileInfo = profiles[SETTINGS.username];
let props, text, profile;
let [prev, next] = this.stepTransitions();
props = Object.assign({}, this.profileProps(profiles[SETTINGS.username]), {
props = Object.assign({}, this.profileProps(profileInfo), {
prevStep: prev,
nextStep: next
});
profile = props.profile;
text = `Welcome ${getPreferredName(profile)}, let's
complete your enrollment to MIT MicroMasters.`;

return <div className="card">
<Jumbotron profile={profile} text={text}>
<div className="card-copy">
<div style={{textAlign: "center"}}>
{makeProfileProgressDisplay(this.currentStep())}
let loaded, content, errorMessage;
if (profileInfo !== undefined) {
loaded = profileInfo.getStatus !== FETCH_PROCESSING;
if (profileInfo.errorInfo !== undefined) {
errorMessage = <ErrorMessage errorInfo={profileInfo.errorInfo} />;
} else {
content = <Jumbotron profile={profile} text={text}>
<div className="card-copy">
<div style={{textAlign: "center"}}>
{makeProfileProgressDisplay(this.currentStep())}
</div>
<section>
{this.currentComponent(props)}
</section>
</div>
<section>
{this.currentComponent(props)}
</section>
</div>
</Jumbotron>
</Jumbotron>;
}
} else {
loaded = false;
}

return <div className="card">
<Loader loaded={loaded}>
{errorMessage}
{content}
</Loader>
</div>;
}
}
Expand Down
41 changes: 41 additions & 0 deletions static/js/containers/ProfilePage_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import { assert } from 'chai';
import _ from 'lodash';

import {
REQUEST_GET_USER_PROFILE,
RECEIVE_GET_USER_PROFILE_FAILURE,
REQUEST_PATCH_USER_PROFILE,
RECEIVE_PATCH_USER_PROFILE_SUCCESS,
RECEIVE_DASHBOARD_SUCCESS,
START_PROFILE_EDIT,
UPDATE_PROFILE_VALIDATION,
CLEAR_PROFILE_EDIT,
Expand All @@ -25,6 +28,7 @@ import {
} from '../actions/ui';
import {
USER_PROFILE_RESPONSE,
ERROR_RESPONSE,
EDUCATION_LEVELS,
ASSOCIATE,
DOCTORATE,
Expand Down Expand Up @@ -447,4 +451,41 @@ describe("ProfilePage", function() {
});
}
}

it('shows a spinner when profile get is processing', () => {
return renderComponent('/profile').then(([, div]) => {
assert.notOk(div.querySelector(".spinner"), "Found spinner but no fetch in progress");
helper.store.dispatch({
type: REQUEST_GET_USER_PROFILE,
payload: {
username: SETTINGS.username
}
});

assert(div.querySelector(".spinner"), "Unable to find spinner");
});
});

it('shows an error when profile get has errored', () => {
return renderComponent('/profile').then(([, div]) => {
assert.notOk(div.querySelector(".spinner"), "Found spinner but no fetch in progress");
helper.store.dispatch({
type: REQUEST_GET_USER_PROFILE,
payload: {
username: SETTINGS.username
}
});

assert(div.querySelector(".spinner"), "Unable to find spinner");
});
});

it('renders errors when there is an error receiving the profile', () => {
helper.profileGetStub.returns(Promise.reject(ERROR_RESPONSE));

const types = [RECEIVE_DASHBOARD_SUCCESS, RECEIVE_GET_USER_PROFILE_FAILURE];
return renderComponent("/profile", types, false).then(([, div]) => {
assert(div.getElementsByClassName('alert-message').length > 0, 'no alert message found');
});
});
});
3 changes: 2 additions & 1 deletion static/js/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ export const profiles = (state: ProfileState = INITIAL_PROFILES_STATE, action: A
});
case RECEIVE_GET_USER_PROFILE_FAILURE:
return patchProfile({
getStatus: FETCH_FAILURE
getStatus: FETCH_FAILURE,
errorInfo: action.payload.errorInfo
});
case CLEAR_PROFILE: {
let clone = Object.assign({}, state);
Expand Down

0 comments on commit 03003e9

Please sign in to comment.