Skip to content

Commit

Permalink
[added] Router.History
Browse files Browse the repository at this point in the history
[changed] goBack returns a boolean value

Users can use Router.History.length to determine if the router has
any history. router.goBack() is a no-op when the router does not
have any history and emits a warning.

Fixes remix-run#408
  • Loading branch information
mjackson committed Dec 4, 2014
1 parent b9eaba1 commit 457d944
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 4 deletions.
2 changes: 2 additions & 0 deletions modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ exports.State = require('./mixins/State');

exports.create = require('./utils/createRouter');
exports.run = require('./utils/runRouter');

exports.History = require('./utils/History');
6 changes: 5 additions & 1 deletion modules/locations/HashLocation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var LocationActions = require('../actions/LocationActions');
var History = require('../utils/History');
var Path = require('../utils/Path');

/**
Expand Down Expand Up @@ -29,6 +30,9 @@ function ensureSlash() {
var _changeListeners = [];

function notifyChange(type) {
if (type === LocationActions.PUSH)
History.length += 1;

var change = {
path: getHashPath(),
type: type
Expand Down Expand Up @@ -87,7 +91,7 @@ var HashLocation = {

pop: function () {
_actionType = LocationActions.POP;
window.history.back();
History.back();
},

getCurrentPath: getHashPath,
Expand Down
4 changes: 3 additions & 1 deletion modules/locations/HistoryLocation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var LocationActions = require('../actions/LocationActions');
var History = require('../utils/History');
var Path = require('../utils/Path');

/**
Expand Down Expand Up @@ -51,6 +52,7 @@ var HistoryLocation = {

push: function (path) {
window.history.pushState({ path: path }, '', Path.encode(path));
History.length += 1;
notifyChange(LocationActions.PUSH);
},

Expand All @@ -60,7 +62,7 @@ var HistoryLocation = {
},

pop: function () {
window.history.back();
History.back();
},

getCurrentPath: getWindowPath,
Expand Down
2 changes: 2 additions & 0 deletions modules/locations/RefreshLocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ var RefreshLocation = {
},

pop: function () {
// History will always have length 1 when using full
// page refreshes, so use window.history directly.
window.history.back();
},

Expand Down
14 changes: 14 additions & 0 deletions modules/locations/TestLocation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
var invariant = require('react/lib/invariant');
var LocationActions = require('../actions/LocationActions');
var History = require('../utils/History');

var _listener;

Expand All @@ -7,6 +9,10 @@ function notifyChange(type) {
_listener({ path: TestLocation.getCurrentPath(), type: type });
}

function updateHistoryLength() {
History.length = TestLocation.history.length;
}

/**
* A location that is convenient for testing and does not
* require a DOM. You should manually setup TestLocation.history
Expand All @@ -19,20 +25,28 @@ var TestLocation = {
addChangeListener: function (listener) {
// TestLocation only ever supports a single listener at a time.
_listener = listener;
updateHistoryLength();
},

push: function (path) {
TestLocation.history.push(path);
updateHistoryLength();
notifyChange(LocationActions.PUSH);
},

replace: function (path) {
invariant(
History.length,
'You cannot replace the current path with no history'
);

TestLocation.history[TestLocation.history.length - 1] = path;
notifyChange(LocationActions.REPLACE);
},

pop: function () {
TestLocation.history.pop();
updateHistoryLength();
notifyChange(LocationActions.POP);
},

Expand Down
29 changes: 29 additions & 0 deletions modules/utils/History.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
var invariant = require('react/lib/invariant');
var canUseDOM = require('react/lib/ExecutionEnvironment').canUseDOM;

var History = {

/**
* Sends the browser back one entry in the history, if one is available.
*/
back: function () {
invariant(
canUseDOM,
'Cannot use History.back without a DOM'
);

// Do this first so that History.length will
// be accurate in location change listeners.
History.length -= 1;

window.history.back();
},

/**
* The current number of entries in the history.
*/
length: 1

};

module.exports = History;
63 changes: 63 additions & 0 deletions modules/utils/__tests__/History-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
var expect = require('expect');
var React = require('react');
var { Foo, RedirectToFoo } = require('../../__tests__/TestHandlers');
var TestLocation = require('../../locations/TestLocation');
var Route = require('../../components/Route');
var Router = require('../../index');
var History = require('../History');

describe('History', function () {
describe('on the initial page load', function () {
it('has length 1', function () {
expect(History.length).toEqual(1);
});
});

describe('after navigating to a route', function () {
beforeEach(function () {
TestLocation.history = [ '/foo' ];
});

it('has length 2', function (done) {
var routes = [
<Route name="foo" handler={Foo}/>,
<Route name="about" handler={Foo}/>
];

var count = 0;

var router = Router.run(routes, TestLocation, function (Handler) {
count += 1;

if (count === 2) {
expect(History.length).toEqual(2);
done();
}
});

router.transitionTo('about');
});

describe('that redirects to another route', function () {
it('has length 2', function (done) {
var routes = [
<Route name="foo" handler={Foo}/>,
<Route name="about" handler={RedirectToFoo}/>
];

var count = 0;

var router = Router.run(routes, TestLocation, function (Handler) {
count += 1;

if (count === 2) {
expect(History.length).toEqual(2);
done();
}
});

router.transitionTo('about');
});
});
});
});
13 changes: 11 additions & 2 deletions modules/utils/createRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var supportsHistory = require('./supportsHistory');
var Transition = require('./Transition');
var PropTypes = require('./PropTypes');
var Redirect = require('./Redirect');
var History = require('./History');
var Cancellation = require('./Cancellation');
var Path = require('./Path');

Expand Down Expand Up @@ -242,15 +243,23 @@ function createRouter(options) {
},

/**
* Transitions to the previous URL.
* Transitions to the previous URL. Returns true if the router
* was able to go back, false otherwise.
*/
goBack: function () {
invariant(
typeof location !== 'string',
'You cannot use goBack with a static location'
);

location.pop();
if (History.length > 1) {
location.pop();
return true;
}

warning(false, 'goBack() was ignored because there is no router history');

return false;
},

/**
Expand Down

0 comments on commit 457d944

Please sign in to comment.