diff --git a/examples/pinterest/app.css b/examples/pinterest/app.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/pinterest/app.js b/examples/pinterest/app.js index 7cbaf25051..476f443573 100644 --- a/examples/pinterest/app.js +++ b/examples/pinterest/app.js @@ -45,7 +45,7 @@ var Feed = React.createClass({
{pictures.map(picture => ( @@ -116,7 +116,7 @@ var RootRoute = { } }; -React.render(, document.getElementById('example')); +React.render(, document.getElementById('example')); // Wait a sec ... what's happening? // diff --git a/modules/BrowserHistory.js b/modules/BrowserHistory.js index 7d13eaff55..94984609fd 100644 --- a/modules/BrowserHistory.js +++ b/modules/BrowserHistory.js @@ -78,13 +78,15 @@ export class BrowserHistory extends DOMHistory { } // http://www.w3.org/TR/2011/WD-html5-20110113/history.html#dom-history-pushstate - push(path) { + push(path, transitionState) { if (this.isSupported) { this._recordScrollPosition(); var key = createRandomKey(); window.history.pushState({ key }, '', path); - this.location = this._createLocation(path, key, NavigationTypes.PUSH); + this.location = this._createLocation( + path, key, NavigationTypes.PUSH, transitionState + ); this._notifyChange(); } else { window.location = path; @@ -92,11 +94,13 @@ export class BrowserHistory extends DOMHistory { } // http://www.w3.org/TR/2011/WD-html5-20110113/history.html#dom-history-replacestate - replace(path) { + replace(path, transitionState) { if (this.isSupported) { var key = createRandomKey(); window.history.replaceState({ key }, '', path); - this.location = this._createLocation(path, key, NavigationTypes.REPLACE); + this.location = this._createLocation( + path, key, NavigationTypes.REPLACE, transitionState + ); this._notifyChange(); } else { window.location.replace(path); diff --git a/modules/DOMHistory.js b/modules/DOMHistory.js index 35ad09e48e..7df20724c8 100644 --- a/modules/DOMHistory.js +++ b/modules/DOMHistory.js @@ -11,6 +11,7 @@ export class DOMHistory extends History { super(); this.getScrollPosition = getScrollPosition; this.scrollHistory = {}; + this.stateHistory = {}; } go(n) { @@ -20,11 +21,13 @@ export class DOMHistory extends History { window.history.go(n); } - _createLocation(path, key, navigationType) { + _createLocation(path, key, navigationType, transitionState) { var scrollKey = key || path; var scrollPosition = this.scrollHistory[scrollKey]; + transitionState = transitionState || this.stateHistory[key]; + this.stateHistory[key] = transitionState; - return new Location(path, key, navigationType, scrollPosition); + return new Location(path, key, navigationType, scrollPosition, transitionState); } _recordScrollPosition() { diff --git a/modules/Link.js b/modules/Link.js index 3db61d4cad..540d997df3 100644 --- a/modules/Link.js +++ b/modules/Link.js @@ -1,4 +1,5 @@ import React from 'react'; +console.log('got the source'); var { object, string, func } = React.PropTypes; @@ -69,7 +70,9 @@ export class Link extends React.Component { event.preventDefault(); if (allowTransition) - this.context.router.transitionTo(this.props.to, this.props.query); + this.context.router.transitionTo( + this.props.to, this.props.query, this.props.transitionState + ); } render() { diff --git a/modules/Location.js b/modules/Location.js index da7703b36f..6b98d93cea 100644 --- a/modules/Location.js +++ b/modules/Location.js @@ -20,16 +20,17 @@ export class Location { return new Location(object); if (object && object.path) - return new Location(object.path, object.key, object.navigationType, object.scrollPosition); + return new Location(object.path, object.key, object.navigationType, object.scrollPosition, object.transitionState); throw new Error('Unable to create a Location from ' + object); } - constructor(path, key=null, navigationType=NavigationTypes.POP, scrollPosition=null) { + constructor(path, key=null, navigationType=NavigationTypes.POP, scrollPosition=null, transitionState=null) { this.path = path; this.key = key; this.navigationType = navigationType; this.scrollPosition = scrollPosition; + this.transitionState = transitionState; } } diff --git a/modules/MemoryHistory.js b/modules/MemoryHistory.js index 0e8b51e4ca..c209769190 100644 --- a/modules/MemoryHistory.js +++ b/modules/MemoryHistory.js @@ -35,17 +35,17 @@ export class MemoryHistory extends History { } // http://www.w3.org/TR/2011/WD-html5-20110113/history.html#dom-history-pushstate - push(path) { + push(path, transitionState) { this.current += 1; this.entries = this.entries.slice(0, this.current).concat([ path ]); - this.location = new Location(path, null, NavigationTypes.PUSH); + this.location = new Location(path, null, NavigationTypes.PUSH, null, transitionState); this._notifyChange(); } // http://www.w3.org/TR/2011/WD-html5-20110113/history.html#dom-history-replacestate - replace(path) { + replace(path, transitionState) { this.entries[this.current] = path; - this.location = new Location(path, null, NavigationTypes.REPLACE); + this.location = new Location(path, null, NavigationTypes.REPLACE, null, transitionState); this._notifyChange(); } diff --git a/modules/Router.js b/modules/Router.js index a4abebc799..b4b9b08254 100644 --- a/modules/Router.js +++ b/modules/Router.js @@ -146,7 +146,7 @@ export class Router extends React.Component { try { for (var i = 0, len = hooks.length; i < len; ++i) { - hooks[i].call(this); + hooks[i].call(this, location.transitionState); if (this.nextLocation !== nextLocation) break; // No need to proceed further. @@ -218,29 +218,29 @@ export class Router extends React.Component { return path; } - transitionTo(pathname, query) { + transitionTo(pathname, query, transitionState) { var path = this.makePath(pathname, query); var { history } = this.props; if (history) { if (this.nextLocation) { - history.replace(path); + history.replace(path, transitionState); } else { - history.push(path); + history.push(path, transitionState); } } else { - this._updateLocation(path); + this._updateLocation({ path, transitionState }); } } - replaceWith(pathname, query) { + replaceWith(pathname, query, transitionState) { var path = this.makePath(pathname, query); var { history } = this.props; if (history) { - history.replace(path); + history.replace(path, transitionState); } else { - this._updateLocation(path); + this._updateLocation({path, transitionState}); } } diff --git a/modules/__tests__/getProps-test.js b/modules/__tests__/getProps-test.js index f044e4996a..54bfb5ba4f 100644 --- a/modules/__tests__/getProps-test.js +++ b/modules/__tests__/getProps-test.js @@ -1,4 +1,5 @@ import expect, { spyOn } from 'expect'; +import Location from '../Location'; import { getProps } from '../getProps'; import qs from 'qs'; @@ -17,7 +18,7 @@ describe('Matching pathnames', function () { CoursesRoute = { path: 'courses', - getChildRoutes(callback) { + getChildRoutes(transitionState, callback) { callback(null, [ GradesRoute ]); } }; @@ -35,7 +36,7 @@ describe('Matching pathnames', function () { }; CourseRoute = { - getChildRoutes(callback) { + getChildRoutes(transitionState, callback) { setTimeout(function () { callback(null, [ CourseGradesRoute, AssignmentRoute, AssignmentsRoute ]); }, 0); @@ -53,7 +54,7 @@ describe('Matching pathnames', function () { ProfileRoute = { path: 'profile', - getIndexRoute (cb) { + getIndexRoute (transitionState, cb) { cb(null, ProfileIndexRoute); } }; @@ -210,6 +211,46 @@ describe('Matching pathnames', function () { expect(outerProps).toNotExist(); }); }); + +}); + +describe('transitionState', function () { + var RouteA = {path: 'test'}; + var RouteB = {path: 'test'}; + var RootRoute = { + path: '/', + getIndexRoute (transitionState, cb) { + if (transitionState.test) + cb(null, RouteA); + else + cb(null, RouteB); + }, + getChildRoutes (transitionState, cb) { + if (transitionState.test) + cb(null, [RouteA]); + else + cb(null, [RouteB]); + } + }; + + it('is passed to getChildRoutes', function (done) { + var transitionState = { test: true }; + var location = new Location('/test', null, null, null, transitionState); + getProps(RootRoute, location, parseQueryString, function (err, props) { + expect(props.branch).toEqual([RootRoute, RouteA]); + done(); + }); + }); + + it('is passed to getIndexRoute', function (done) { + var transitionState = { test: true }; + var location = new Location('/', null, null, null, transitionState); + getProps(RootRoute, location, parseQueryString, function (err, props) { + expect(props.branch).toEqual([RootRoute, RouteA]); + done(); + }); + }); + }); describe('Matching params', function () { @@ -364,4 +405,5 @@ describe('Matching params', function () { }); }); }); + }); diff --git a/modules/getProps.js b/modules/getProps.js index 6339d0e3fb..97d39d6916 100644 --- a/modules/getProps.js +++ b/modules/getProps.js @@ -3,11 +3,11 @@ import { createRoutes } from './RouteUtils'; import { getPathname, getQueryString, matchPattern, stripLeadingSlashes } from './PathUtils'; import Location from './Location'; -function getChildRoutes(route, callback) { +function getChildRoutes(route, transitionState, callback) { if (route.childRoutes) { callback(null, route.childRoutes); } else if (route.getChildRoutes) { - route.getChildRoutes(callback); + route.getChildRoutes(transitionState, callback); } else { callback(); } @@ -33,7 +33,7 @@ function createParams(paramNames, paramValues) { return assignParams({}, paramNames, paramValues); } -function matchRouteDeep(route, pathname, callback) { +function matchRouteDeep(route, pathname, transitionState, callback) { var { remainingPathname, paramNames, paramValues } = matchPattern(route.path, pathname); var isExactMatch = remainingPathname === ''; @@ -45,7 +45,7 @@ function matchRouteDeep(route, pathname, callback) { branch.push(route.indexRoute); callback(null, { params, branch }); } else if (route.getIndexRoute) { - route.getIndexRoute(function (error, indexRoute) { + route.getIndexRoute(transitionState, function (error, indexRoute) { if (error) { callback(error); } else { @@ -58,12 +58,12 @@ function matchRouteDeep(route, pathname, callback) { } } else if (remainingPathname != null) { // This route matched at least some of the path. - getChildRoutes(route, function (error, childRoutes) { + getChildRoutes(route, transitionState, function (error, childRoutes) { if (error) { callback(error); } else if (childRoutes) { // Check the child routes to see if any of them match. - matchRoutes(childRoutes, remainingPathname, function (error, match) { + matchRoutes(childRoutes, remainingPathname, transitionState, function (error, match) { if (error) { callback(error); } else if (match) { @@ -84,11 +84,11 @@ function matchRouteDeep(route, pathname, callback) { } } -function matchRoutes(routes, pathname, callback) { +function matchRoutes(routes, pathname, transitionState, callback) { routes = createRoutes(routes); loopAsync(routes.length, function (index, next, done) { - matchRouteDeep(routes[index], pathname, function (error, match) { + matchRouteDeep(routes[index], pathname, transitionState, function (error, match) { if (error || match) { done(error, match); } else { @@ -116,7 +116,7 @@ export function getProps(routes, location, parseQueryString, callback) { var pathname = stripLeadingSlashes(getPathname(location.path)); - matchRoutes(routes, pathname, function (error, props) { + matchRoutes(routes, pathname, location.transitionState, function (error, props) { if (error || props == null) { callback(error); } else {