Add basic routing.

master
Tom Hacohen 7 years ago
parent b9798f1b13
commit 557e5ec0b4

@ -1,14 +1,59 @@
import * as React from 'react'; import * as React from 'react';
import { HashRouter } from 'react-router-dom'; import { HashRouter } from 'react-router-dom';
import { Switch, Route } from 'react-router';
import './App.css'; import './App.css';
import { JournalList } from './JournalList'; import { JournalList } from './JournalList';
import { RouteResolver, RouteKeysType } from './routes';
interface RouteItem {
path: string;
exact?: boolean;
keys?: RouteKeysType;
name: string;
component: React.ComponentType<any>;
}
const navigationRoutes: RouteItem[] = [
{
path: 'home',
exact: true,
name: 'Home',
component: JournalList,
},
{
path: 'journals._id',
exact: true,
name: 'Journal',
keys: {journalUid: 1},
component: JournalList,
},
];
const routeResolver = new RouteResolver({
home: '',
journals: {
_base: 'journals',
_id: {
_base: ':journalUid',
},
},
});
class App extends React.Component { class App extends React.Component {
render() { render() {
return ( return (
<HashRouter> <HashRouter>
<JournalList /> <Switch>
{navigationRoutes.map((route, index) => (
<Route
key={index}
path={routeResolver.getRoute(route.path)}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</HashRouter> </HashRouter>
); );
} }

@ -0,0 +1,61 @@
import { RouteResolver } from './routes';
const routes = {
home: '',
post: {
_base: 'post',
_id: {
_base: ':postId',
comment: 'comment/:commentId',
revision: 'history/:revisionId/:someOtherVar/test',
},
},
};
const routeResolver = new RouteResolver(routes);
it('translating routes', () => {
// Working basic resolves
expect(routeResolver.getRoute('home')).toBe('/');
expect(routeResolver.getRoute('post')).toBe('/post');
expect(routeResolver.getRoute('post._id')).toBe('/post/:postId');
expect(routeResolver.getRoute('post._id.comment')).toBe('/post/:postId/comment/:commentId');
// Working translation resolves
expect(routeResolver.getRoute('home')).toBe('/');
expect(routeResolver.getRoute('post')).toBe('/post');
expect(routeResolver.getRoute('post._id', { postId: 3 })).toBe('/post/3');
expect(routeResolver.getRoute('post._id.comment',
{ postId: 3, commentId: 5 })).toBe('/post/3/comment/5');
expect(routeResolver.getRoute('post._id.revision',
{ postId: 3, revisionId: 5, someOtherVar: 'a' })).toBe('/post/3/history/5/a/test');
// Failing basic resolves
expect(() => {
routeResolver.getRoute('bad');
}).toThrow();
expect(() => {
routeResolver.getRoute('home.bad');
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id.bad');
}).toThrow();
// Failing translations
expect(() => {
routeResolver.getRoute('home', { test: 4 });
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id', { test: 4 });
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id', { postId: 3, test: 4 });
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id.comment', { postId: 3, commentId: 5, test: 4 });
}).toThrow();
expect(() => {
routeResolver.getRoute('post._id.comment', { postId: 3 });
}).toThrow();
});

@ -0,0 +1,47 @@
export type RouteKeysType = { [Identifier: string]: any };
export class RouteResolver {
routes: {};
constructor(routes: {}) {
this.routes = routes;
}
getRoute(name: string, _keys?: RouteKeysType): string {
let dict = this.routes;
let path: string[] = [];
name.split('.').forEach((key) => {
const val = (typeof dict[key] === 'string') ? dict[key] : dict[key]._base;
path.push(val);
dict = dict[key];
});
if (_keys) {
let keys = Object.assign({}, _keys);
path = path.map((pathComponent) => {
return pathComponent.split('/').map((val) => {
if (val[0] === ':') {
const ret = keys[val.slice(1)];
if (ret === undefined) {
throw new Error('Missing key: ' + val.slice(1));
}
delete keys[val.slice(1)];
return ret;
}
return val;
}).join('/');
});
if (Object.keys(keys).length !== 0) {
throw new Error('Too many keys for route.');
}
}
return '/' + path.join('/');
}
}
Loading…
Cancel
Save