diff --git a/static_src/components/not_found.jsx b/static_src/components/not_found.jsx index a769b29f..eb4797e1 100644 --- a/static_src/components/not_found.jsx +++ b/static_src/components/not_found.jsx @@ -1,6 +1,6 @@ import React from 'react'; const NotFound = () => -

Not Found

+

Not Found

; export default NotFound; diff --git a/static_src/components/router/route_provider.jsx b/static_src/components/router/route_provider.jsx new file mode 100644 index 00000000..bde91c04 --- /dev/null +++ b/static_src/components/router/route_provider.jsx @@ -0,0 +1,36 @@ +import React from 'react'; +import RouterStore from '../../stores/router_store.js'; +import Loading from '../loading.jsx'; + +class RouteProvider extends React.Component { + constructor() { + super(); + + this.state = RouterStore.component; + + this.onChange = this.onChange.bind(this); + } + + componentDidMount() { + RouterStore.addChangeListener(this.onChange); + } + + shouldComponentUpdate(nextProps, nextState) { + return this.state === nextState || true; + } + + componentWillUnmount() { + RouterStore.removeChangeListener(this.onChange); + } + + onChange() { + this.setState({ ...RouterStore.component }); + } + + render() { + const { component: Component, props } = this.state; + return Component ? : ; + } +} + +export default RouteProvider; diff --git a/static_src/main.js b/static_src/main.js index d07c7e22..10605793 100644 --- a/static_src/main.js +++ b/static_src/main.js @@ -4,61 +4,27 @@ import './css/main.css'; import './img/dashboard-uaa-icon.jpg'; import 'cloudgov-style/img/favicon.ico'; +import React from 'react'; +import ReactDOM from 'react-dom'; + import axios from 'axios'; import { Router } from 'director'; import { trackPageView } from './util/analytics.js'; import routes, { checkAuth, clearErrors, notFound } from './routes'; -import RouterStore from './stores/router_store.js'; import MainContainer from './components/main_container.jsx'; -import Loading from './components/loading.jsx'; - -const meta = document.querySelector('meta[name="gorilla.csrf.Token"]'); - -if (meta) { - axios.defaults.headers.common['X-CSRF-Token'] = meta.content; -} - -import React from 'react'; -import ReactDOM from 'react-dom'; - -class RouteHandler extends React.Component { - constructor() { - super(); - - this.state = { - component: null - }; - - this.onChange = this.onChange.bind(this); - } - - componentDidMount() { - RouterStore.addChangeListener(this.onChange); - } +import RouteProvider from './components/router/route_provider.jsx'; - shouldComponentUpdate(nextProps, nextState) { - return this.state === nextState ? false : true; +const initCSRFHeader = metaTag => { + if (metaTag) { + axios.defaults.headers.common['X-CSRF-Token'] = metaTag.content; } - - componentWillUnmount() { - RouterStore.removeChangeListener(this.onChange); - } - - onChange() { - this.setState({ ...RouterStore.component }); - } - - render() { - const { component: Component, props } = this.state; - return Component ? : ; - } -} +}; const cRouter = { - run(routes, renderEl) { - const router = new Router(routes); + run(routeConfig, renderEl) { + const router = new Router(routeConfig); router.configure({ async: true, before: [clearErrors, checkAuth], @@ -67,13 +33,15 @@ const cRouter = { trackPageView(window.location.hash); } }); + ReactDOM.render( - + , renderEl); router.init('/'); } }; +initCSRFHeader(document.querySelector('meta[name="gorilla.csrf.Token"]')); cRouter.run(routes, document.querySelector('.js-app')); diff --git a/static_src/routes.js b/static_src/routes.js index 2c623b1e..83eab517 100644 --- a/static_src/routes.js +++ b/static_src/routes.js @@ -1,6 +1,3 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - import activityActions from './actions/activity_actions.js'; import AppContainer from './components/app_container.jsx'; import appActions from './actions/app_actions.js'; @@ -10,7 +7,6 @@ import Loading from './components/loading.jsx'; import Login from './components/login.jsx'; import loginActions from './actions/login_actions'; import LoginStore from './stores/login_store'; -import MainContainer from './components/main_container.jsx'; import NotFound from './components/not_found.jsx'; import orgActions from './actions/org_actions.js'; import Overview from './components/overview_container.jsx'; @@ -27,14 +23,11 @@ import windowUtil from './util/window'; import userActions from './actions/user_actions.js'; import routerActions from './actions/router_actions.js'; -// TODO this is hard to stub since we query it at module load time. It should -// be passed in or something. -const mainEl = document.querySelector('.js-app'); - const MAX_OVERVIEW_SPACES = 10; export function login(next) { routerActions.navigate(Login); + next(); } export function overview(next) { @@ -44,7 +37,7 @@ export function overview(next) { orgActions.changeCurrentOrg(); spaceActions.changeCurrentSpace(); appActions.changeCurrentApp(); - + routerActions.navigate(Overview); spaceActions.fetchAll() .then(spaces => { let i = 0; @@ -56,8 +49,9 @@ export function overview(next) { return Promise.all(fetches); }) - .then(pageActions.loadSuccess, pageActions.loadError) - .then(() => routerActions.navigate(Overview)); + .then(pageActions.loadSuccess, pageActions.loadError); + + next(); } export function org(orgGuid, next) { @@ -72,6 +66,7 @@ export function org(orgGuid, next) { userActions.fetchOrgUsers(orgGuid); userActions.fetchOrgUserRoles(orgGuid); routerActions.navigate(OrgContainer); + next(); } export function space(orgGuid, spaceGuid, next) { @@ -89,6 +84,7 @@ export function space(orgGuid, spaceGuid, next) { orgActions.fetch(orgGuid); serviceActions.fetchAllServices(orgGuid); routerActions.navigate(SpaceContainer, { currentPage: 'apps' }); + next(); } export function app(orgGuid, spaceGuid, appGuid, next) { @@ -111,6 +107,7 @@ export function app(orgGuid, spaceGuid, appGuid, next) { serviceActions.fetchAllInstances(spaceGuid); serviceActions.fetchServiceBindings(); routerActions.navigate(AppContainer); + next(); } export function checkAuth(...args) { @@ -149,9 +146,6 @@ export function checkAuth(...args) { // Just in case something goes wrong, don't leave the user hanging. Show // a delayed loading indicator to give them a hint. Hopefully the // redirect is quick and they never see the loader. - // ReactDOM.render( - // - // , mainEl); routerActions.navigate(Loading, { text: 'Redirecting to login', loadingDelayMS: 3000, @@ -185,6 +179,7 @@ export function notFound(next) { spaceActions.changeCurrentSpace(); appActions.changeCurrentApp(); routerActions.navigate(NotFound); + next(); } const routes = { diff --git a/static_src/stores/router_store.js b/static_src/stores/router_store.js index 00338896..7dd58fae 100644 --- a/static_src/stores/router_store.js +++ b/static_src/stores/router_store.js @@ -12,17 +12,19 @@ class RouterStore extends BaseStore { registerToActions(action) { const { type, data } = action; - switch(type) { + switch (type) { case routerActionTypes.NAVIGATE: this.routeComponent = Object.assign({}, { ...data }); this.emitChange(); break; + default: + break; } } get component() { return this.routeComponent; } -}; +} export default new RouterStore(); diff --git a/static_src/test/unit/routes.spec.js b/static_src/test/unit/routes.spec.js index 4cdb6486..0dc1148e 100644 --- a/static_src/test/unit/routes.spec.js +++ b/static_src/test/unit/routes.spec.js @@ -1,6 +1,6 @@ import '../global_setup.js'; -import ReactDOM from 'react-dom'; +import routerActions from '../../actions/router_actions'; import errorActions from '../../actions/error_actions'; import loginActions from '../../actions/login_actions'; import LoginStore from '../../stores/login_store'; @@ -126,7 +126,7 @@ describe('routes', function () { beforeEach(function (done) { next = sandbox.spy(done); - sandbox.stub(ReactDOM, 'render'); + sandbox.stub(routerActions, 'navigate'); sandbox.stub(windowUtil, 'redirect'); loginActions.fetchStatus.returns(Promise.resolve({ status: 'unauthorized' })); @@ -142,7 +142,7 @@ describe('routes', function () { }); it('renders a loader', function () { - expect(ReactDOM.render).toHaveBeenCalledOnce(); + expect(routerActions.navigate).toHaveBeenCalledOnce(); }); it('does not fetch page data', function () {