-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Post Editor: Refactor URL redirect as BrowserURL component (#7122)
* State: Set post status to draft when autosave reset * Post Editor: Refactor URL redirect as BrowserURL component Behavior is specific to post editor, and can be expressed in terms of lifecycle as: Sync to current post ID so long as post is not auto-draft.
- Loading branch information
Showing
9 changed files
with
209 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
BrowserURL | ||
========== | ||
|
||
`<BrowserURL />` is a component used to keep the editor's saved post ID in sync with the browser's URL. Using the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API), it makes an in-place replacement (using `window.replaceState`) of the URL if the post ID changes and is not an auto-draft. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Component } from '@wordpress/element'; | ||
import { withSelect } from '@wordpress/data'; | ||
import { addQueryArgs } from '@wordpress/url'; | ||
|
||
/** | ||
* Returns the Post's Edit URL. | ||
* | ||
* @param {number} postId Post ID. | ||
* | ||
* @return {string} Post edit URL. | ||
*/ | ||
export function getPostEditURL( postId ) { | ||
return addQueryArgs( 'post.php', { post: postId, action: 'edit' } ); | ||
} | ||
|
||
export class BrowserURL extends Component { | ||
constructor() { | ||
super( ...arguments ); | ||
|
||
this.state = { | ||
historyId: null, | ||
}; | ||
} | ||
|
||
componentDidUpdate( prevProps ) { | ||
const { postId, postStatus } = this.props; | ||
const { historyId } = this.state; | ||
if ( postId === prevProps.postId && postId === historyId ) { | ||
return; | ||
} | ||
|
||
if ( postStatus !== 'auto-draft' ) { | ||
this.setBrowserURL( postId ); | ||
} | ||
} | ||
|
||
/** | ||
* Replaces the browser URL with a post editor link for the given post ID. | ||
* | ||
* Note it is important that, since this function may be called when the | ||
* editor first loads, the result generated `getPostEditURL` matches that | ||
* produced by the server. Otherwise, the URL will change unexpectedly. | ||
* | ||
* @param {number} postId Post ID for which to generate post editor URL. | ||
*/ | ||
setBrowserURL( postId ) { | ||
window.history.replaceState( | ||
{ id: postId }, | ||
'Post ' + postId, | ||
getPostEditURL( postId ) | ||
); | ||
|
||
this.setState( () => ( { | ||
historyId: postId, | ||
} ) ); | ||
} | ||
|
||
render() { | ||
return null; | ||
} | ||
} | ||
|
||
export default withSelect( ( select ) => { | ||
const { getCurrentPost } = select( 'core/editor' ); | ||
const { id, status } = getCurrentPost(); | ||
|
||
return { | ||
postId: id, | ||
postStatus: status, | ||
}; | ||
} )( BrowserURL ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { shallow } from 'enzyme'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { getPostEditURL, BrowserURL } from '../'; | ||
|
||
describe( 'getPostEditURL', () => { | ||
it( 'should generate relative path with post and action arguments', () => { | ||
const url = getPostEditURL( 1 ); | ||
|
||
expect( url ).toBe( 'post.php?post=1&action=edit' ); | ||
} ); | ||
} ); | ||
|
||
describe( 'BrowserURL', () => { | ||
let replaceStateSpy; | ||
|
||
beforeAll( () => { | ||
replaceStateSpy = jest.spyOn( window.history, 'replaceState' ); | ||
} ); | ||
|
||
beforeEach( () => { | ||
replaceStateSpy.mockReset(); | ||
} ); | ||
|
||
afterAll( () => { | ||
replaceStateSpy.mockRestore(); | ||
} ); | ||
|
||
it( 'not update URL if post is auto-draft', () => { | ||
const wrapper = shallow( <BrowserURL /> ); | ||
wrapper.setProps( { | ||
postId: 1, | ||
postStatus: 'auto-draft', | ||
} ); | ||
|
||
expect( replaceStateSpy ).not.toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'update URL if post is no longer auto-draft', () => { | ||
const wrapper = shallow( <BrowserURL /> ); | ||
wrapper.setProps( { | ||
postId: 1, | ||
postStatus: 'auto-draft', | ||
} ); | ||
wrapper.setProps( { | ||
postStatus: 'draft', | ||
} ); | ||
|
||
expect( replaceStateSpy ).toHaveBeenCalledWith( | ||
{ id: 1 }, | ||
'Post 1', | ||
'post.php?post=1&action=edit' | ||
); | ||
} ); | ||
|
||
it( 'not update URL if history is already set', () => { | ||
const wrapper = shallow( <BrowserURL /> ); | ||
wrapper.setProps( { | ||
postId: 1, | ||
postStatus: 'draft', | ||
} ); | ||
replaceStateSpy.mockReset(); | ||
wrapper.setProps( { | ||
postId: 1, | ||
} ); | ||
|
||
expect( replaceStateSpy ).not.toHaveBeenCalled(); | ||
} ); | ||
|
||
it( 'update URL if post ID changes', () => { | ||
const wrapper = shallow( <BrowserURL /> ); | ||
wrapper.setProps( { | ||
postId: 1, | ||
postStatus: 'draft', | ||
} ); | ||
replaceStateSpy.mockReset(); | ||
wrapper.setProps( { | ||
postId: 2, | ||
} ); | ||
|
||
expect( replaceStateSpy ).toHaveBeenCalledWith( | ||
{ id: 2 }, | ||
'Post 2', | ||
'post.php?post=2&action=edit' | ||
); | ||
} ); | ||
|
||
it( 'renders nothing', () => { | ||
const wrapper = shallow( <BrowserURL /> ); | ||
|
||
expect( wrapper.type() ).toBeNull(); | ||
} ); | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters