From 563b9eb5ea5e9a09dd45fb19b0cb914ba4a64702 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Wed, 18 Nov 2015 20:59:14 -0500 Subject: [PATCH] Accept objects in push and replace --- modules/__tests__/describeBasename.js | 56 ++++++++++++- modules/__tests__/describePush.js | 97 ++++++++++++++++----- modules/__tests__/describeQueries.js | 116 ++++++++++++++++++++++++++ modules/__tests__/describeReplace.js | 97 ++++++++++++++++----- modules/createHistory.js | 25 ++++-- modules/createLocation.js | 15 ++-- modules/useBasename.js | 18 ++-- modules/useQueries.js | 23 ++++- 8 files changed, 383 insertions(+), 64 deletions(-) diff --git a/modules/__tests__/describeBasename.js b/modules/__tests__/describeBasename.js index 09eaa3458..57fc1efe4 100644 --- a/modules/__tests__/describeBasename.js +++ b/modules/__tests__/describeBasename.js @@ -48,7 +48,7 @@ function describeBasename(createHistory) { }) describe('in push', function () { - it('works', function (done) { + it('works with string', function (done) { let steps = [ function (location) { expect(location.pathname).toEqual('/') @@ -70,6 +70,32 @@ function describeBasename(createHistory) { unlisten = history.listen(execSteps(steps, done)) }) + + it('works with object', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + expect(location.basename).toEqual('') + + history.push({ + pathname: '/home', + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('') + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(PUSH) + expect(location.basename).toEqual('/base/url') + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) }) describe('in replaceState', function () { @@ -98,7 +124,7 @@ function describeBasename(createHistory) { }) describe('in replace', function () { - it('works', function (done) { + it('works with string', function (done) { let steps = [ function (location) { expect(location.pathname).toEqual('/') @@ -120,6 +146,32 @@ function describeBasename(createHistory) { unlisten = history.listen(execSteps(steps, done)) }) + + it('works with object', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + expect(location.basename).toEqual('') + + history.replace({ + pathname: '/home', + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('') + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(REPLACE) + expect(location.basename).toEqual('/base/url') + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) }) describe('in createPath', function () { diff --git a/modules/__tests__/describePush.js b/modules/__tests__/describePush.js index 63389460a..14d448f5f 100644 --- a/modules/__tests__/describePush.js +++ b/modules/__tests__/describePush.js @@ -15,25 +15,84 @@ function describePush(createHistory) { unlisten() }) - it('calls change listeners with the new location', function (done) { - let steps = [ - function (location) { - expect(location.pathname).toEqual('/') - expect(location.search).toEqual('') - expect(location.state).toEqual(null) - expect(location.action).toEqual(POP) - - history.push('/home?the=query') - }, - function (location) { - expect(location.pathname).toEqual('/home') - expect(location.search).toEqual('?the=query') - expect(location.state).toEqual(null) - expect(location.action).toEqual(PUSH) - } - ] - - unlisten = history.listen(execSteps(steps, done)) + describe('with a path string', function () { + it('calls change listeners with the new location', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + history.push('/home?the=query') + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?the=query') + expect(location.state).toEqual(null) + expect(location.action).toEqual(PUSH) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) + }) + + describe('with a path object', function () { + it('calls change listeners with the new location', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + history.push({ + pathname: '/home', + search: '?the=query', + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?the=query') + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(PUSH) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) + + it('correctly merges with old location', function (done) { + let oldLocation + + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + oldLocation = location + + history.push({ + ...location, + search: '?the=query', + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual(oldLocation.pathname) + expect(location.search).toEqual('?the=query') + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(PUSH) + expect(location.key).toNotEqual(oldLocation.key) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) }) }) } diff --git a/modules/__tests__/describeQueries.js b/modules/__tests__/describeQueries.js index 3060ce50f..5a2169c07 100644 --- a/modules/__tests__/describeQueries.js +++ b/modules/__tests__/describeQueries.js @@ -45,6 +45,35 @@ function describeQueries(createHistory) { }) }) + describe('in push', function () { + it('works', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.query).toEqual({}) + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + history.push({ + pathname: '/home', + query: { the: 'query value' }, + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?the=query+value') + expect(location.query).toEqual({ the: 'query value' }) + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(PUSH) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) + }) + describe('in replaceState', function () { it('works', function (done) { let steps = [ @@ -70,6 +99,35 @@ function describeQueries(createHistory) { }) }) + describe('in replace', function () { + it('works', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.query).toEqual({}) + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + history.replace({ + pathname: '/home', + query: { the: 'query value' }, + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?the=query+value') + expect(location.query).toEqual({ the: 'query value' }) + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(REPLACE) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) + }) + describe('in createPath', function () { it('works', function () { expect( @@ -152,6 +210,35 @@ function describeQueries(createHistory) { }) }) + describe('in push', function () { + it('works', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.query).toEqual('PARSE_QUERY_STRING') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + history.push({ + pathname: '/home', + query: { the: 'query' }, + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?STRINGIFY_QUERY') + expect(location.query).toEqual('PARSE_QUERY_STRING') + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(PUSH) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) + }) + describe('in replaceState', function () { it('works', function (done) { let steps = [ @@ -177,6 +264,35 @@ function describeQueries(createHistory) { }) }) + describe('in replace', function () { + it('works', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.query).toEqual('PARSE_QUERY_STRING') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + history.replace({ + pathname: '/home', + query: { the: 'query' }, + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?STRINGIFY_QUERY') + expect(location.query).toEqual('PARSE_QUERY_STRING') + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(REPLACE) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) + }) + describe('in createPath', function () { it('works', function () { expect( diff --git a/modules/__tests__/describeReplace.js b/modules/__tests__/describeReplace.js index 851be1f9b..fcb2b9e1d 100644 --- a/modules/__tests__/describeReplace.js +++ b/modules/__tests__/describeReplace.js @@ -15,25 +15,84 @@ function describeReplace(createHistory) { unlisten() }) - it('calls change listeners with the new location', function (done) { - let steps = [ - function (location) { - expect(location.pathname).toEqual('/') - expect(location.search).toEqual('') - expect(location.state).toEqual(null) - expect(location.action).toEqual(POP) - - history.replace('/home?the=query') - }, - function (location) { - expect(location.pathname).toEqual('/home') - expect(location.search).toEqual('?the=query') - expect(location.state).toEqual() - expect(location.action).toEqual(REPLACE) - } - ] - - unlisten = history.listen(execSteps(steps, done)) + describe('with a path string', function () { + it('calls change listeners with the new location', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + history.replace('/home?the=query') + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?the=query') + expect(location.state).toEqual() + expect(location.action).toEqual(REPLACE) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) + }) + + describe('with a path object', function () { + it('calls change listeners with the new location', function (done) { + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + history.replace({ + pathname: '/home', + search: '?the=query', + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?the=query') + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(REPLACE) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) + + it('correctly merges with old location', function (done) { + let oldLocation + + let steps = [ + function (location) { + expect(location.pathname).toEqual('/') + expect(location.search).toEqual('') + expect(location.state).toEqual(null) + expect(location.action).toEqual(POP) + + oldLocation = location + + history.replace({ + ...location, + search: '?the=query', + state: { the: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual(oldLocation.pathname) + expect(location.search).toEqual('?the=query') + expect(location.state).toEqual({ the: 'state' }) + expect(location.action).toEqual(REPLACE) + expect(location.key).toNotEqual(oldLocation.key) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) }) }) } diff --git a/modules/createHistory.js b/modules/createHistory.js index 9276d35ce..081440cc3 100644 --- a/modules/createHistory.js +++ b/modules/createHistory.js @@ -2,6 +2,7 @@ import deepEqual from 'deep-equal' import { loopAsync } from './AsyncUtils' import { PUSH, REPLACE, POP } from './Actions' import _createLocation from './createLocation' +import parsePath from './parsePath' import runTransitionHook from './runTransitionHook' import deprecate from './deprecate' @@ -137,25 +138,31 @@ function createHistory(options={}) { } function pushState(state, path) { + if (typeof path === 'string') + path = parsePath(path) + + push({ state, ...path }) + } + + function push(location) { transitionTo( - createLocation(path, state, PUSH, createKey()) + createLocation(location, null, PUSH, createKey()) ) } - function push(path) { - pushState(null, path) + function replaceState(state, path) { + if (typeof path === 'string') + path = parsePath(path) + + replace({ state, ...path }) } - function replaceState(state, path) { + function replace(location) { transitionTo( - createLocation(path, state, REPLACE, createKey()) + createLocation(location, null, REPLACE, createKey()) ) } - function replace(path) { - replaceState(null, path) - } - function goBack() { go(-1) } diff --git a/modules/createLocation.js b/modules/createLocation.js index 4e044dd80..81199685a 100644 --- a/modules/createLocation.js +++ b/modules/createLocation.js @@ -1,13 +1,16 @@ import { POP } from './Actions' import parsePath from './parsePath' -function createLocation(path='/', state=null, action=POP, key=null) { - if (typeof path === 'string') - path = parsePath(path) +function createLocation(location='/', state=null, action=POP, key=null) { + if (typeof location === 'string') + location = parsePath(location) - const pathname = path.pathname || '/' - const search = path.search || '' - const hash = path.hash || '' + const pathname = location.pathname || '/' + const search = location.search || '' + const hash = location.hash || '' + + // TODO: Deprecate passing state directly into createLocation. + state = location.state || state return { pathname, diff --git a/modules/useBasename.js b/modules/useBasename.js index 1796342c6..fec157ed9 100644 --- a/modules/useBasename.js +++ b/modules/useBasename.js @@ -66,19 +66,25 @@ function useBasename(createHistory) { // Override all write methods with basename-aware versions. function pushState(state, path) { - history.pushState(state, prependBasename(path)) + if (typeof path === 'string') + path = parsePath(path) + + push({ state, ...path }) } - function push(path) { - pushState(null, path) + function push(location) { + history.push(prependBasename(location)) } function replaceState(state, path) { - history.replaceState(state, prependBasename(path)) + if (typeof path === 'string') + path = parsePath(path) + + replace({ state, ...path }) } - function replace(path) { - replaceState(null, path) + function replace(location) { + history.replace(prependBasename(location)) } function createPath(path) { diff --git a/modules/useQueries.js b/modules/useQueries.js index c3bd7116d..ac1c5ef4f 100644 --- a/modules/useQueries.js +++ b/modules/useQueries.js @@ -40,7 +40,8 @@ function useQueries(createHistory) { if (typeof path === 'string') path = parsePath(path) - const search = path.search + (path.search ? '&' : '?') + queryString + const searchBase = path.search ? path.search + '&' : '?' + const search = searchBase + queryString return { ...path, @@ -63,11 +64,25 @@ function useQueries(createHistory) { // Override all write methods with query-aware versions. function pushState(state, path, query) { - return history.pushState(state, appendQuery(path, query)) + if (typeof path === 'string') + path = parsePath(path) + + push({ state, ...path, query }) + } + + function push(location) { + history.push(appendQuery(location, location.query)) } function replaceState(state, path, query) { - return history.replaceState(state, appendQuery(path, query)) + if (typeof path === 'string') + path = parsePath(path) + + replace({ state, ...path, query }) + } + + function replace(location) { + history.replace(appendQuery(location, location.query)) } function createPath(path, query) { @@ -87,7 +102,9 @@ function useQueries(createHistory) { listenBefore, listen, pushState, + push, replaceState, + replace, createPath, createHref, createLocation