diff --git a/examples/animations/app.js b/examples/animations/app.js index 964cd23de9..95be4af9e7 100644 --- a/examples/animations/app.js +++ b/examples/animations/app.js @@ -36,7 +36,7 @@ var Image = React.createClass({ var routes = ( - + ); diff --git a/examples/dynamic-segments/app.js b/examples/dynamic-segments/app.js index 79b38b1501..cf8287beb9 100644 --- a/examples/dynamic-segments/app.js +++ b/examples/dynamic-segments/app.js @@ -49,8 +49,8 @@ var Task = React.createClass({ var routes = ( - - + + ); diff --git a/modules/helpers/Path.js b/modules/helpers/Path.js index 052ee07d89..5e2044dc2a 100644 --- a/modules/helpers/Path.js +++ b/modules/helpers/Path.js @@ -140,11 +140,25 @@ var Path = { return path; }, + /** + * Returns true if the given path is absolute. + */ + isAbsolute: function (path) { + return path.charAt(0) === '/'; + }, + /** * Returns a normalized version of the given path. */ - normalize: function (path) { + normalize: function (path, parentRoute) { return path.replace(/^\/*/, '/'); + }, + + /** + * Joins two URL paths together. + */ + join: function (a, b) { + return a.replace(/\/*$/, '/') + b; } }; diff --git a/modules/helpers/makePath.js b/modules/helpers/makePath.js index e29b1ccd69..f650b2db4a 100644 --- a/modules/helpers/makePath.js +++ b/modules/helpers/makePath.js @@ -8,8 +8,8 @@ var Path = require('./Path'); */ function makePath(to, params, query) { var path; - if (to.charAt(0) === '/') { - path = Path.normalize(to); // Absolute path. + if (Path.isAbsolute(path)) { + path = Path.normalize(to); } else { var route = RouteStore.getRouteByName(to); diff --git a/modules/stores/RouteStore.js b/modules/stores/RouteStore.js index ae8935ec67..3c8baebcce 100644 --- a/modules/stores/RouteStore.js +++ b/modules/stores/RouteStore.js @@ -48,10 +48,18 @@ var RouteStore = { props.name || props.path ); + var parentPath = (parentRoute && parentRoute.props.path) || '/'; + if ((props.path || props.name) && !props.isDefault && !props.catchAll) { - props.path = Path.normalize(props.path || props.name); + var path = props.path || props.name; + + // Relative paths extend their parent. + if (!Path.isAbsolute(path)) + path = Path.join(parentPath, path); + + props.path = Path.normalize(path); } else { - props.path = (parentRoute && parentRoute.props.path) || '/'; + props.path = parentPath; if (props.catchAll) props.path += '*'; diff --git a/specs/RouteStore.spec.js b/specs/RouteStore.spec.js index ded71e5b14..9cf5e1b3f5 100644 --- a/specs/RouteStore.spec.js +++ b/specs/RouteStore.spec.js @@ -28,6 +28,31 @@ describe('when a route is looked up by name', function () { }); describe('when registering a route', function () { + + describe('that starts with /', function() { + it('does not inherit the parent path', function() { + var child; + var route = Route({ name: 'home', handler: App }, + child = Route({ path: '/foo', handler: App }) + ); + RouteStore.registerRoute(route); + expect(child.props.path).toEqual('/foo'); + RouteStore.unregisterRoute(route); + }); + }); + + describe('that does not start with /', function() { + it('inherits the parent path', function() { + var child; + var route = Route({ name: 'home', handler: App }, + child = Route({ path: 'foo', handler: App }) + ); + RouteStore.registerRoute(route); + expect(child.props.path).toEqual('/home/foo'); + RouteStore.unregisterRoute(route); + }); + }); + describe('with no handler', function () { it('throws an Error', function () { expect(function () {