Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade react router v3 to react router dom #77

Open
wants to merge 1 commit into
base: feature/upgrade-testing-packages
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, {
import PropTypes from 'prop-types';
import {
Link
} from 'react-router';
} from 'react-router-dom';
import routes from '../../../src/constants/routes';
import services from '../services';

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"dependencies": {
"axios": "^0.17.1",
"es6-promise": "^4.2.4",
"history": "^4.7.2",
"immutable": "^3.8.2",
"keymirror": "^0.1.1",
"normalize.css": "^5.0.0",
Expand All @@ -37,13 +38,14 @@
"react-dom": "^15.4.1",
"react-immutable-proptypes": "^2.1.0",
"react-redux": "^5.0.1",
"react-router": "^3.0.0",
"react-router-dom": "^4.2.2",
"redux": "^3.6.0",
"redux-devtools-extension": "^2.13.0",
"redux-immutable": "^4.0.0",
"redux-persist-cookie-storage": "^0.3.0",
"redux-persist-immutable": "^4.3.1",
"redux-thunk": "^2.1.0",
"reselect": "^3.0.1",
"sanitize.css": "^4.1.0"
},
"devDependencies": {
Expand Down
4 changes: 3 additions & 1 deletion src/reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { combineReducers } from 'redux-immutable';
import authentication from '../../modules/authentication/reducer';
import helloWorld from './helloWorldReducer';
import persist from './persist';

export default combineReducers({
authentication,
helloWorld
helloWorld,
persist
});
17 changes: 17 additions & 0 deletions src/reducers/persist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Immutable from 'immutable';
import { REHYDRATE } from 'redux-persist-immutable/constants';

const INITIAL_STATE = Immutable.fromJS({
rehydrated: false
});

function reducer(state = INITIAL_STATE, action) {
switch (action.type) {
case REHYDRATE:
return state.set('rehydrated', true);
default:
return state;
}
}

export default reducer;
30 changes: 30 additions & 0 deletions src/reducers/persist.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Immutable from 'immutable';
import { REHYDRATE } from 'redux-persist-immutable/constants';

import persistReducer from './persist';

describe('reducers/persist', function() {
const INITIAL_STATE = Immutable.fromJS({
rehydrated: false
});

describe(REHYDRATE, function() {
let nextState;

beforeEach(function() {
nextState = persistReducer(INITIAL_STATE, {
type: REHYDRATE
});
});

it('sets rehydration to true', function() {
expect(nextState.get('rehydrated')).to.be.true;
});

it('returns a default initial state', function() {
nextState = persistReducer(undefined, {});// eslint-disable-line no-undefined

expect(nextState).to.deep.equal(INITIAL_STATE);
});
});
});
64 changes: 64 additions & 0 deletions src/routes/AuthorizedRoute.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Redirect,
Route
} from 'react-router-dom';
import { connect } from 'react-redux';

import isUserAuthorized from '../selectors/isUserAuthorized';
import routes from '../constants/routes';

const propTypes = {
component: PropTypes.func.isRequired,
exact: PropTypes.bool,
hydrated: PropTypes.bool.isRequired,
isApproved: PropTypes.bool,
isAuthorized: PropTypes.bool.isRequired,
location: PropTypes.object.isRequired,
path: PropTypes.string.isRequired
};

function AuthorizedRoute(props) {
const {
component: Component,
exact,
hydrated,
isAuthorized,
location,
path
} = props;

if (!hydrated) {
return <div>Loading...</div>;
}

return (
<Route
exact={exact}
path={path}
render={(routeProps) => {
return isAuthorized ?
<Component {...routeProps} /> :
<Redirect
to={{
pathname: routes.AUTHENTICATION.SIGN_IN,
search: location.search,
state: { from: location.pathname }
}}
/>;
}}
/>
);
}

AuthorizedRoute.propTypes = propTypes;

function mapStateToProps(state) {
return {
hydrated: state.getIn(['persist', 'rehydrated']),
isAuthorized: isUserAuthorized(state)
};
}

export default connect(mapStateToProps)(AuthorizedRoute);
38 changes: 17 additions & 21 deletions src/routes/index.jsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
browserHistory as history,
IndexRoute,
Route,
Router
} from 'react-router';
Router,
Switch
} from 'react-router-dom';
import { createBrowserHistory } from 'history';
import AppContainer from '../containers/AppContainer';
import ForgotPassword from '../../modules/authentication/containers/ForgotPassword';
import Register from '../../modules/authentication/containers/Register';
import RegistrationConfirmed from '../../modules/authentication/containers/RegistrationConfirmed';
// import RegistrationConfirmed from '../../modules/authentication/containers/RegistrationConfirmed';
import ResetPassword from '../../modules/authentication/containers/ResetPassword';
import SignIn from '../../modules/authentication/containers/SignIn';
import authenticationService from '../../modules/authentication/services';
// import authenticationService from '../../modules/authentication/services';
import routes from '../constants/routes';
import AuthorizedRoute from './AuthorizedRoute';

const history = createBrowserHistory();

const propTypes = {
store: PropTypes.object.isRequired
};

function Routes({ store }) {
function Routes() {
return (
<Router history={history}>
<Route path={routes.ROOT} onEnter={authenticationService.checkAuth(store)}>
<IndexRoute component={AppContainer} />
</Route>
<Route path={routes.AUTHENTICATION.FORGOT_PASSWORD} component={ForgotPassword} />
<Route path={routes.AUTHENTICATION.REGISTER} component={Register} />
<Route
path={routes.AUTHENTICATION.REGISTRATION_CONFIRMED}
component={RegistrationConfirmed}
onEnter={(nextState, replace, callback) => {
return authenticationService.prehydrateStore(store)(null, null, callback);
}}
/>
<Route path={routes.AUTHENTICATION.RESET_PASSWORD} component={ResetPassword} />
<Route path={routes.AUTHENTICATION.SIGN_IN} component={SignIn} />
<Switch>
<Route path={routes.AUTHENTICATION.REGISTER} component={Register} />
<Route path={routes.AUTHENTICATION.FORGOT_PASSWORD} component={ForgotPassword} />
<Route path={routes.AUTHENTICATION.RESET_PASSWORD} component={ResetPassword} />
<Route path={routes.AUTHENTICATION.SIGN_IN} component={SignIn} />
<AuthorizedRoute exact path="/" component={AppContainer} />
</Switch>
</Router>
);
}
Expand Down
15 changes: 15 additions & 0 deletions src/selectors/isUserAuthorized.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createSelector } from 'reselect';
import Immutable from 'immutable';
import { VALID_TOKEN_INFO_FIELDS } from '../../modules/authentication/constants';

function getTokenInfoKeys(state) {
return state.getIn(['authentication', 'tokenInfo'], Immutable.Map());
}

function isUserAuthorized(tokenInfo) {
return VALID_TOKEN_INFO_FIELDS.filter((field) => {
return tokenInfo.get(field, null);
}).length === VALID_TOKEN_INFO_FIELDS.length;
}

export default createSelector(getTokenInfoKeys, isUserAuthorized);
40 changes: 40 additions & 0 deletions src/selectors/isUserAuthorized.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Immutable from 'immutable';
import isUserAuthorized from './isUserAuthorized';
import { VALID_TOKEN_INFO_FIELDS } from '../../modules/authentication/constants';

describe('selectors/isUserAuthorized', function() {
it('returns true if the user has values in the valid token info fields', function() {
const validInfoFields = VALID_TOKEN_INFO_FIELDS.reduce((fields, field) => {
fields[field] = faker.lorem.word();
return fields;
}, {});

const state = Immutable.fromJS({
authentication: {
tokenInfo: validInfoFields
}
});

const isAuthorized = isUserAuthorized(state);

expect(isAuthorized).to.be.true;
});

it('returns false if any of the fields are missing a value', function() {
const randomIndex = Math.floor(Math.random() * VALID_TOKEN_INFO_FIELDS.length);
const validInfoFields = VALID_TOKEN_INFO_FIELDS.reduce((fields, field, index) => {
fields[field] = index !== randomIndex ? faker.lorem.word() : null;
return fields;
}, {});

const state = Immutable.fromJS({
authentication: {
tokenInfo: validInfoFields
}
});

const isAuthorized = isUserAuthorized(state);

expect(isAuthorized).to.be.false;
});
});
Empty file added src/utils/.keep
Empty file.
Loading