Skip to content

Commit

Permalink
refactor: extract common code to find & parse matching route from URL (
Browse files Browse the repository at this point in the history
…#80)

* refactor: extract common code to find & parse matching route from URL

extract the common code from both history-adapter.ts and static-adapter.ts and expose the new common
function for use in setting up the initialState of the routerStore

* refactor: rename files for consistency
  • Loading branch information
AndrewSadler authored May 13, 2020
1 parent eda74bf commit 98cf4af
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 42 deletions.
41 changes: 41 additions & 0 deletions src/adapters/find-matching-route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { JsRouterState, Route } from '../router-store';
import { matchUrl } from './match-url';
import { parse } from 'query-string';
import { Location } from 'history';

/*
* Find a route that matches the URL to a pattern and extracts the components.
*
* Can be used to generate the initial routerState when initialising the RouterStore.
* e.g.
* const history = createBrowserHistory();
routerStore = new RouterStore(
rootStore,
routes,
notFound,
findMatchingRoute(history.location, routes)
);
*/
export const findMatchingRoute = (
location: Location,
routes: Route[]
): JsRouterState | undefined => {
// Find the matching route
let matchingRoute = null;
let params = undefined;
for (let i = 0; i < routes.length; i++) {
const route = routes[i];
params = matchUrl(location.pathname, route.pattern);
if (params) {
matchingRoute = route;
break;
}
}
if (matchingRoute) {
return {
routeName: matchingRoute.name,
params,
queryParams: parse(location.search)
};
}
};
27 changes: 6 additions & 21 deletions src/adapters/history-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { History, Location } from 'history';
import { reaction } from 'mobx';
import { parse } from 'query-string';
import { RouterState, RouterStore } from '../router-store';
import { routerStateToUrl } from './generate-url';
import { matchUrl } from './match-url';
import { findMatchingRoute } from './find-matching-route';

/**
* Responsible for keeping the browser address bar and the `RouterState`
Expand Down Expand Up @@ -33,26 +32,12 @@ export class HistoryAdapter {
// }

// Find the matching route
const routes = this.routerStore.routes;
let matchingRoute = null;
let params = undefined;
for (let i = 0; i < routes.length; i++) {
const route = routes[i];
params = matchUrl(location.pathname, route.pattern);
if (params) {
matchingRoute = route;
break;
}
}

const matchingRoute = findMatchingRoute(
location,
this.routerStore.routes
);
if (matchingRoute) {
return this.routerStore.goTo(
new RouterState(
matchingRoute.name,
params,
parse(location.search)
)
);
return this.routerStore.goTo(RouterState.create(matchingRoute));
} else {
return this.routerStore.goToNotFound();
}
Expand Down
27 changes: 6 additions & 21 deletions src/adapters/static-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Location } from 'history';
import { parse } from 'query-string';
import { RouterState, RouterStore } from '../router-store';
import { matchUrl } from './match-url';
import { findMatchingRoute } from './find-matching-route';

/**
* Responsible for driving `RouterState` programmatically instead of the
Expand All @@ -25,26 +24,12 @@ export class StaticAdapter {
// }

// Find the matching route
const routes = this.routerStore.routes;
let matchingRoute = null;
let params = undefined;
for (let i = 0; i < routes.length; i++) {
const route = routes[i];
params = matchUrl(location.pathname, route.pattern);
if (params) {
matchingRoute = route;
break;
}
}

const matchingRoute = findMatchingRoute(
location,
this.routerStore.routes
);
if (matchingRoute) {
return this.routerStore.goTo(
new RouterState(
matchingRoute.name,
params,
parse(location.search)
)
);
return this.routerStore.goTo(RouterState.create(matchingRoute));
} else {
return this.routerStore.goToNotFound();
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export { RouterLink, RouterLinkProps } from './components/router-link';
export { HistoryAdapter } from './adapters/history-adapter';
export { StaticAdapter } from './adapters/static-adapter';
export { generateUrl, routerStateToUrl } from './adapters/generate-url';
export { findMatchingRoute } from './adapters/find-matching-route';
74 changes: 74 additions & 0 deletions test/find-matching-route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { findMatchingRoute } from '../src/adapters/find-matching-route';
import { createLocation } from 'history';
import { Route } from '../src/router-store';

const routes: Route[] = [
{ name: 'home', pattern: '/' },
{
name: 'department',
pattern: '/departments'
},
{
name: 'department-category',
pattern: '/departments/:id/:category'
},
{ name: 'notFound', pattern: '/not-found' }
];

describe('findMatchingRoute', () => {
test('returns params when url matches pattern, no params, no queryParams', () => {
const jsRouterState = findMatchingRoute(
createLocation('/departments'),
routes
);
expect(jsRouterState).toEqual({
routeName: 'department',
params: {},
queryParams: {}
});
});

test('returns params when url matches pattern, params, no queryParams', () => {
const jsRouterState = findMatchingRoute(
createLocation('/departments/electronics/computers'),
routes
);
expect(jsRouterState).toEqual({
routeName: 'department-category',
params: { id: 'electronics', category: 'computers' },
queryParams: {}
});
});

test('returns params when url matches pattern, params, single queryParam', () => {
const jsRouterState = findMatchingRoute(
createLocation('/departments/electronics/computers?q=apple'),
routes
);
expect(jsRouterState).toEqual({
routeName: 'department-category',
params: { id: 'electronics', category: 'computers' },
queryParams: { q: 'apple' }
});
});

test('returns params when url matches pattern, params, multiple queryParams', () => {
const jsRouterState = findMatchingRoute(
createLocation('/departments/electronics/computers?q=apple&r=pear'),
routes
);
expect(jsRouterState).toEqual({
routeName: 'department-category',
params: { id: 'electronics', category: 'computers' },
queryParams: { q: 'apple', r: 'pear' }
});
});

test('returns undefined when url matches no pattern', () => {
const jsRouterState = findMatchingRoute(
createLocation('/notValidUrl'),
routes
);
expect(jsRouterState).toEqual(undefined);
});
});

0 comments on commit 98cf4af

Please sign in to comment.