diff --git a/CHANGES.md b/CHANGES.md index 98961d393..0aadc4cae 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,20 +1,17 @@ ## [HEAD] -#### New features -- Accept objects in `history.push` and `history.replace` ([#141]) - -#### Bug fixes -- Disable browser history on Chrome iOS ([#146]) -- Do not convert same-path PUSH to REPLACE if the hash has changed ([#167]) - -#### Other -- Add ES2015 module build ([#152]) +- **Feature:** Accept objects in `history.push` and `history.replace` ([#141]) +- **Deprecation:** Deprecate `history.pushState` and `history.replaceState` in favor of passing objects to `history.push` and `history.replace` ([#168]) +- **Bugfix:** Disable browser history on Chrome iOS ([#146]) +- **Bugfix:** Do not convert same-path PUSH to REPLACE if the hash has changed ([#167]) +- **Other:** Add ES2015 module build ([#152]) [HEAD]: https://github.com/rackt/history/compare/latest...HEAD [#141]: https://github.com/rackt/history/pull/141 [#146]: https://github.com/rackt/history/pull/146 [#152]: https://github.com/rackt/history/pull/152 [#167]: https://github.com/rackt/history/pull/167 +[#167]: https://github.com/rackt/history/pull/168 ## [v1.13.1] > Nov 13, 2015 diff --git a/docs/BasenameSupport.md b/docs/BasenameSupport.md index e788d1a37..16883cc48 100644 --- a/docs/BasenameSupport.md +++ b/docs/BasenameSupport.md @@ -17,11 +17,10 @@ history.listen(function (location) { }) ``` -Basename-enhanced histories also automatically prepend the basename to paths used in `pushState`, `push`, `replaceState`, `replace`, `createPath`, and `createHref`. +Basename-enhanced histories also automatically prepend the basename to paths used in `push`, `replace`, `createPath`, and `createHref`. ```js history.createPath('/the/path') // /base/the/path -history.pushState(null, '/the/path') // push /base/the/path history.push('/the/path') // push /base/the/path ``` diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 45356752e..17dabaa7f 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -27,33 +27,32 @@ unlisten() You can also use a `history` object to programmatically change the current `location` using the following methods: -- `pushState(state, path)` - `push(location)` -- `replaceState(state, path)` - `replace(location)` - `go(n)` - `goBack()` - `goForward()` -The [`path`](Glossary.md#path) argument to `pushState` and `replaceState` represents a complete URL path, including the [search string](Glossary.md#search) and [hash](Glossary.md#hash). The [`state`](Glossary.md#locationstate) argument should be a JSON-serializable object. +The `push` and `replace` methods take a [path string](Glossary.md#path) that represents a complete URL path, including the [search string](Glossary.md#search) and [hash](Glossary.md#hash). -The location argument to `push` and `replace` can be either a path string as above or a [location descriptor](Glossary.md#locationdescriptor) object representing the next history entry, including the state. +They can also accept a [location descriptor](Glossary.md#locationdescriptor) object that defines the path as a combination of [`pathname`](Glossary.md#pathname), [`search`](Glossary.md#search), and [`hash`](Glossary.md#hash). This object can also include [`state`](Glossary.md#locationstate) as a JSON-serializable object. ```js // Push a new entry onto the history stack. -history.pushState({ some: 'state' }, '/home') +history.push('/home') // Replace the current entry on the history stack. -history.replaceState({ some: 'other state' }, '/profile') +history.replace('/profile') -// Push a path with null state. -history.push('/about') - -// Push a new history location object with state. -history.push({ pathname: '/contact', state: { some: 'state' } }) +// Push a new entry with state onto the history stack. +history.push({ + pathname: '/about', + search: '?the=search', + state: { some: 'state' } +}) // Change just the search on an existing location. -history.push({ ...location, search: '?the=search' }) +history.push({ ...location, search: '?the=other+search' }) // Go back to the previous history entry. The following // two lines are synonymous. diff --git a/docs/Glossary.md b/docs/Glossary.md index a3bfc7993..947c08881 100644 --- a/docs/Glossary.md +++ b/docs/Glossary.md @@ -64,9 +64,7 @@ A *hash* is a string that represents the hash portion of the URL. It is synonymo listenBefore: (hook: TransitionHook) => Function; listen: (listener: LocationListener) => Function; transitionTo(location: Location) => void; - pushState(state: LocationState, path: Path) => void; push(location: LocationDescriptor) => void; - replaceState(state: LocationState, path: Path) => void; replace(location: LocationDescriptor) => void; go(n: number) => void; goBack() => void; diff --git a/docs/QuerySupport.md b/docs/QuerySupport.md index 50a6ece26..2f5951604 100644 --- a/docs/QuerySupport.md +++ b/docs/QuerySupport.md @@ -23,10 +23,9 @@ history.listen(function (location) { }) ``` -Query-enhanced histories accept URL queries as trailing arguments to `pushState`, `replaceState`, `createPath`, and `createHref`, and accept `query` as a key for `push` and `replace`. +Query-enhanced histories accept URL queries as trailing arguments to `createPath`, and `createHref`, and accept `query` as a key for `push` and `replace`. ```js history.createPath('/the/path', { the: 'query' }) -history.pushState(null, '/the/path', { the: 'query' }) history.push({ pathname: '/the/path', query: { the: 'query' } }) ``` diff --git a/modules/__tests__/MemoryHistory-test.js b/modules/__tests__/MemoryHistory-test.js index 699198b95..14ce24e9e 100644 --- a/modules/__tests__/MemoryHistory-test.js +++ b/modules/__tests__/MemoryHistory-test.js @@ -21,7 +21,7 @@ describe('memory history', function () { describeQueries(createMemoryHistory) describeGo(createMemoryHistory) - describe('when using pushState in the middle of the stack', function () { + describe('when using push in the middle of the stack', function () { it('clears rest of stack so the user cannot go forward', function () { let history = createMemoryHistory(), location @@ -29,10 +29,22 @@ describe('memory history', function () { location = loc }) - history.pushState({ id: 1 }, '/1') - history.pushState({ id: 2 }, '/2') - history.pushState({ id: 3 }, '/3') - history.pushState({ id: 4 }, '/4') + history.push({ + pathname: '/1', + state: { id: 1 } + }) + history.push({ + pathname: '/2', + state: { id: 2 } + }) + history.push({ + pathname: '/3', + state: { id: 3 } + }) + history.push({ + pathname: '/4', + state: { id: 4 } + }) expect(location.state).toEqual({ id: 4 }) @@ -40,7 +52,10 @@ describe('memory history', function () { expect(location.state).toEqual({ id: 2 }) - history.pushState({ id: 5 }, '/5') + history.push({ + pathname: '/5', + state: { id: 5 } + }) expect(location.state).toEqual({ id: 5 }) expect(location.pathname).toEqual('/5') @@ -59,7 +74,10 @@ describe('memory history', function () { }).toThrow(/Cannot go\(\d+\) there is not enough history/) history.goBack() - history.pushState({ id: 6 }, '/6') + history.push({ + pathname: '/6', + state: { id: 6 } + }) expect(function () { history.goForward() diff --git a/modules/__tests__/describeBasename.js b/modules/__tests__/describeBasename.js index 031037c46..7fc1c1ec2 100644 --- a/modules/__tests__/describeBasename.js +++ b/modules/__tests__/describeBasename.js @@ -243,7 +243,7 @@ function describeBasename(createHistory) { }) }) - describe('in pushState', () => { + describe('in push', () => { it('works', function (done) { let steps = [ function (location) { @@ -253,7 +253,10 @@ function describeBasename(createHistory) { expect(location.action).toEqual(POP) expect(location.basename).toEqual('') - history.pushState({ the: 'state' }, '/home') + history.push({ + pathname: '/home', + state: { the: 'state' } + }) }, function (location) { expect(location.pathname).toEqual('/home') diff --git a/modules/__tests__/describeGo.js b/modules/__tests__/describeGo.js index 38669315a..a67b8e665 100644 --- a/modules/__tests__/describeGo.js +++ b/modules/__tests__/describeGo.js @@ -23,7 +23,11 @@ function describeGo(createHistory) { expect(location.state).toEqual(null) expect(location.action).toEqual(POP) - history.pushState({ the: 'state' }, '/home?the=query') + history.push({ + pathname: '/home', + search: '?the=query', + state: { the: 'state' } + }) }, function (location) { expect(location.pathname).toEqual('/home') @@ -54,7 +58,11 @@ function describeGo(createHistory) { expect(location.state).toEqual(null) expect(location.action).toEqual(POP) - history.pushState({ the: 'state' }, '/home?the=query') + history.push({ + pathname: '/home', + search: '?the=query', + state: { the: 'state' } + }) }, function (location) { expect(location.pathname).toEqual('/home') diff --git a/modules/__tests__/describeHashSupport.js b/modules/__tests__/describeHashSupport.js index 542faf38f..d002e6502 100644 --- a/modules/__tests__/describeHashSupport.js +++ b/modules/__tests__/describeHashSupport.js @@ -23,7 +23,12 @@ function describeHashSupport(createHistory) { expect(location.state).toEqual(null) expect(location.action).toEqual(POP) - history.pushState({ the: 'state' }, '/home?the=query#the-hash') + history.push({ + pathname: '/home', + search: '?the=query', + hash: '#the-hash', + state: { the: 'state' } + }) }, function (location) { expect(location.pathname).toEqual('/home') @@ -46,7 +51,7 @@ function describeHashSupport(createHistory) { expect(location.state).toEqual(null) expect(location.action).toEqual(POP) - history.pushState(null, '/#the-hash') + history.push('/#the-hash') }, function (location) { expect(location.pathname).toEqual('/') diff --git a/modules/__tests__/describeInitialLocation.js b/modules/__tests__/describeInitialLocation.js index aee865dec..0b2e0fe86 100644 --- a/modules/__tests__/describeInitialLocation.js +++ b/modules/__tests__/describeInitialLocation.js @@ -29,7 +29,10 @@ function describeInitialLocation(createHistory) { it('emits POP with current location key', function (done) { // set initial state, this is needed because all implementations gets state from different places - history.pushState({ initial: 'state' }, '/') + history.push({ + pathname: '/', + state: { initial: 'state' } + }) // now create history for testing if initial POP event has location.key history = createHistory() diff --git a/modules/__tests__/describePopState.js b/modules/__tests__/describePopState.js index 0a45ba77f..ec8e118b1 100644 --- a/modules/__tests__/describePopState.js +++ b/modules/__tests__/describePopState.js @@ -4,7 +4,7 @@ function describePopState(createHistory) { beforeEach(function () { history = createHistory() - history.pushState(null, '/home') + history.push('/home') }) afterEach(function () { @@ -26,7 +26,7 @@ function describePopState(createHistory) { beforeEach(function () { history = createHistory() - history.pushState(null, '/home') + history.push('/home') }) afterEach(function () { diff --git a/modules/__tests__/describePush.js b/modules/__tests__/describePush.js index 636f4f39b..7dfb6bfa8 100644 --- a/modules/__tests__/describePush.js +++ b/modules/__tests__/describePush.js @@ -1,5 +1,5 @@ import expect from 'expect' -import { PUSH, POP } from '../Actions' +import { PUSH, POP, REPLACE } from '../Actions' import execSteps from './execSteps' function describePush(createHistory) { @@ -92,6 +92,43 @@ function describePush(createHistory) { unlisten = history.listen(execSteps(steps, done)) }) + + it('becomes a REPLACE if path is unchanged', 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) + + history.push({ + pathname: '/home', + search: '?the=query', + state: { different: 'state' } + }) + }, + function (location) { + expect(location.pathname).toEqual('/home') + expect(location.search).toEqual('?the=query') + expect(location.state).toEqual({ different: 'state' }) + expect(location.action).toEqual(REPLACE) + } + ] + + unlisten = history.listen(execSteps(steps, done)) + }) }) }) } diff --git a/modules/__tests__/describeQueryKey.js b/modules/__tests__/describeQueryKey.js index 9a3657280..e94292b3d 100644 --- a/modules/__tests__/describeQueryKey.js +++ b/modules/__tests__/describeQueryKey.js @@ -22,7 +22,7 @@ function describeQueryKey(createHistory) { expect(location.state).toEqual(null) expect(location.action).toEqual(POP) - history.pushState(null, '/home?the=query') + history.push('/home?the=query') }, function (location) { expect(location.pathname).toEqual('/home') @@ -71,7 +71,11 @@ function describeQueryKey(createHistory) { expect(location.state).toEqual(null) expect(location.action).toEqual(POP) - history.pushState({ the: 'state' }, '/home?the=query') + history.push({ + pathname: '/home', + search: '?the=query', + state: { the: 'state' } + }) }, function (location) { expect(location.pathname).toEqual('/home') diff --git a/modules/__tests__/describeTransitions.js b/modules/__tests__/describeTransitions.js index f2a27fe14..c759942e4 100644 --- a/modules/__tests__/describeTransitions.js +++ b/modules/__tests__/describeTransitions.js @@ -21,7 +21,11 @@ function describeTransitions(createHistory) { it('receives the next location', function (done) { let steps = [ function () { - history.pushState({ the: 'state' }, '/home?the=query') + history.push({ + pathname: '/home', + search: '?the=query', + state: { the: 'state' } + }) }, function (location) { expect(nextLocation).toBe(location) @@ -54,7 +58,11 @@ function describeTransitions(createHistory) { it('receives the next location', function (done) { let steps = [ function () { - history.pushState({ the: 'state' }, '/home?the=query') + history.push({ + pathname: '/home', + search: '?the=query', + state: { the: 'state' } + }) }, function (location) { expect(nextLocation).toBe(location) @@ -103,7 +111,11 @@ function describeTransitions(createHistory) { it('updates the location', function () { let prevLocation = location - history.pushState({ the: 'state' }, '/home?the=query') + history.push({ + pathname: '/home', + search: '?the=query', + state: { the: 'state' } + }) expect(prevLocation).toNotBe(location) assert(location) @@ -147,7 +159,7 @@ function describeTransitions(createHistory) { it('does not update the location', function () { let prevLocation = location - history.pushState(null, '/home') + history.push('/home') expect(prevLocation).toBe(location) }) }) @@ -178,7 +190,7 @@ function describeTransitions(createHistory) { it('does not update the location', function () { let prevLocation = location - history.pushState(null, '/home') + history.push('/home') expect(prevLocation).toBe(location) }) }) diff --git a/modules/createHashHistory.js b/modules/createHashHistory.js index 10a5532e6..3d0e851b8 100644 --- a/modules/createHashHistory.js +++ b/modules/createHashHistory.js @@ -156,24 +156,6 @@ function createHashHistory(options={}) { } } - function pushState(state, path) { - warning( - queryKey || state == null, - 'You cannot use state without a queryKey it will be dropped' - ) - - history.pushState(state, path) - } - - function replaceState(state, path) { - warning( - queryKey || state == null, - 'You cannot use state without a queryKey it will be dropped' - ) - - history.replaceState(state, path) - } - let goIsSupportedWithoutReload = supportsGoWithoutReloadUsingHash() function go(n) { @@ -205,6 +187,26 @@ function createHashHistory(options={}) { stopHashChangeListener() } + // deprecated - warning is in createHistory + function pushState(state, path) { + warning( + queryKey || state == null, + 'You cannot use state without a queryKey it will be dropped' + ) + + history.pushState(state, path) + } + + // deprecated - warning is in createHistory + function replaceState(state, path) { + warning( + queryKey || state == null, + 'You cannot use state without a queryKey it will be dropped' + ) + + history.replaceState(state, path) + } + return { ...history, listenBefore, diff --git a/modules/createHistory.js b/modules/createHistory.js index c510da2cc..a06acc190 100644 --- a/modules/createHistory.js +++ b/modules/createHistory.js @@ -136,26 +136,12 @@ function createHistory(options={}) { }) } - function pushState(state, path) { - if (typeof path === 'string') - path = parsePath(path) - - push({ state, ...path }) - } - function push(location) { transitionTo( createLocation(location, null, PUSH, createKey()) ) } - function replaceState(state, path) { - if (typeof path === 'string') - path = parsePath(path) - - replace({ state, ...path }) - } - function replace(location) { transitionTo( createLocation(location, null, REPLACE, createKey()) @@ -225,12 +211,26 @@ function createHistory(options={}) { transitionHooks = transitionHooks.filter(item => item !== hook) } + // deprecated + function pushState(state, path) { + if (typeof path === 'string') + path = parsePath(path) + + push({ state, ...path }) + } + + // deprecated + function replaceState(state, path) { + if (typeof path === 'string') + path = parsePath(path) + + replace({ state, ...path }) + } + return { listenBefore, listen, transitionTo, - pushState, - replaceState, push, replace, go, @@ -252,6 +252,14 @@ function createHistory(options={}) { unregisterTransitionHook: deprecate( unregisterTransitionHook, 'unregisterTransitionHook is deprecated; use the callback returned from listenBefore instead' + ), + pushState: deprecate( + pushState, + 'pushState is deprecated; use push instead' + ), + replaceState: deprecate( + replaceState, + 'replaceState is deprecated; use replace instead' ) } } diff --git a/modules/useBasename.js b/modules/useBasename.js index 75f42d840..51a4bb02f 100644 --- a/modules/useBasename.js +++ b/modules/useBasename.js @@ -2,6 +2,7 @@ import { canUseDOM } from './ExecutionEnvironment' import runTransitionHook from './runTransitionHook' import extractPath from './extractPath' import parsePath from './parsePath' +import deprecate from './deprecate' function useBasename(createHistory) { return function (options={}) { @@ -65,24 +66,10 @@ function useBasename(createHistory) { } // Override all write methods with basename-aware versions. - function pushState(state, path) { - if (typeof path === 'string') - path = parsePath(path) - - push({ state, ...path }) - } - function push(location) { history.push(prependBasename(location)) } - function replaceState(state, path) { - if (typeof path === 'string') - path = parsePath(path) - - replace({ state, ...path }) - } - function replace(location) { history.replace(prependBasename(location)) } @@ -99,17 +86,40 @@ function useBasename(createHistory) { return addBasename(history.createLocation.apply(history, arguments)) } + // deprecated + function pushState(state, path) { + if (typeof path === 'string') + path = parsePath(path) + + push({ state, ...path }) + } + + // deprecated + function replaceState(state, path) { + if (typeof path === 'string') + path = parsePath(path) + + replace({ state, ...path }) + } + return { ...history, listenBefore, listen, - pushState, push, - replaceState, replace, createPath, createHref, - createLocation + createLocation, + + pushState: deprecate( + pushState, + 'pushState is deprecated; use push instead' + ), + replaceState: deprecate( + replaceState, + 'replaceState is deprecated; use replace instead' + ) } } } diff --git a/modules/useQueries.js b/modules/useQueries.js index 2cc5a5b02..1faa56fd0 100644 --- a/modules/useQueries.js +++ b/modules/useQueries.js @@ -1,6 +1,7 @@ import qs from 'qs' import runTransitionHook from './runTransitionHook' import parsePath from './parsePath' +import deprecate from './deprecate' const SEARCH_BASE_KEY = '$searchBase' @@ -79,24 +80,10 @@ function useQueries(createHistory) { } // Override all write methods with query-aware versions. - function pushState(state, 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) { - if (typeof path === 'string') - path = parsePath(path) - - replace({ state, ...path, query }) - } - function replace(location) { history.replace(appendQuery(location, location.query)) } @@ -113,17 +100,40 @@ function useQueries(createHistory) { return addQuery(history.createLocation.apply(history, arguments)) } + // deprecated + function pushState(state, path, query) { + if (typeof path === 'string') + path = parsePath(path) + + push({ state, ...path, query }) + } + + // deprecated + function replaceState(state, path, query) { + if (typeof path === 'string') + path = parsePath(path) + + replace({ state, ...path, query }) + } + return { ...history, listenBefore, listen, - pushState, push, - replaceState, replace, createPath, createHref, - createLocation + createLocation, + + pushState: deprecate( + pushState, + 'pushState is deprecated; use push instead' + ), + replaceState: deprecate( + replaceState, + 'replaceState is deprecated; use replace instead' + ) } } }