-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved pre/post publish flow. #5767
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,28 +21,29 @@ import PostPublishPanelPostpublish from './postpublish'; | |
import { | ||
getCurrentPostType, | ||
isCurrentPostPublished, | ||
isCurrentPostScheduled, | ||
isSavingPost, | ||
isEditedPostDirty, | ||
} from '../../store/selectors'; | ||
|
||
class PostPublishPanel extends Component { | ||
constructor() { | ||
super( ...arguments ); | ||
this.onPublish = this.onPublish.bind( this ); | ||
this.onSubmit = this.onSubmit.bind( this ); | ||
this.state = { | ||
loading: false, | ||
published: false, | ||
submitted: false, | ||
}; | ||
} | ||
|
||
componentWillReceiveProps( newProps ) { | ||
if ( | ||
newProps.isPublished && | ||
! this.state.published && | ||
! newProps.isSaving | ||
! this.state.submitted && | ||
! newProps.isSaving && | ||
( newProps.isPublished || newProps.isScheduled ) | ||
) { | ||
this.setState( { | ||
published: true, | ||
submitted: true, | ||
loading: false, | ||
} ); | ||
} | ||
|
@@ -56,25 +57,32 @@ class PostPublishPanel extends Component { | |
} | ||
} | ||
|
||
onPublish() { | ||
onSubmit() { | ||
const { user, onClose } = this.props; | ||
const userCanPublishPosts = get( user.data, [ 'post_type_capabilities', 'publish_posts' ], false ); | ||
const isContributor = user.data && ! userCanPublishPosts; | ||
if ( isContributor ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nicely moved to one place. I hope we move that logic to selector one day. Not possible at the moment without a bigger refactor :) |
||
onClose(); | ||
return; | ||
} | ||
this.setState( { loading: true } ); | ||
} | ||
|
||
render() { | ||
const { onClose, user, forceIsDirty, forceIsSaving } = this.props; | ||
const { loading, published } = this.state; | ||
const canPublish = get( user.data, [ 'post_type_capabilities', 'publish_posts' ], false ); | ||
|
||
const { isScheduled, onClose, forceIsDirty, forceIsSaving } = this.props; | ||
const { loading, submitted } = this.state; | ||
return ( | ||
<div className="editor-post-publish-panel"> | ||
<div className="editor-post-publish-panel__header"> | ||
{ ! published && ( | ||
{ ! submitted && ( | ||
<div className="editor-post-publish-panel__header-publish-button"> | ||
<PostPublishButton onSubmit={ this.onPublish } forceIsDirty={ forceIsDirty } forceIsSaving={ forceIsSaving } /> | ||
<PostPublishButton onSubmit={ this.onSubmit } forceIsDirty={ forceIsDirty } forceIsSaving={ forceIsSaving } /> | ||
</div> | ||
) } | ||
{ published && ( | ||
<div className="editor-post-publish-panel__header-published">{ __( 'Published' ) }</div> | ||
{ submitted && ( | ||
<div className="editor-post-publish-panel__header-published"> | ||
{ isScheduled ? __( 'Scheduled' ) : __( 'Published' ) } | ||
</div> | ||
) } | ||
<IconButton | ||
onClick={ onClose } | ||
|
@@ -83,9 +91,9 @@ class PostPublishPanel extends Component { | |
/> | ||
</div> | ||
<div className="editor-post-publish-panel__content"> | ||
{ canPublish && ! loading && ! published && <PostPublishPanelPrepublish /> } | ||
{ loading && ! published && <Spinner /> } | ||
{ published && <PostPublishPanelPostpublish /> } | ||
{ ! loading && ! submitted && <PostPublishPanelPrepublish /> } | ||
{ loading && ! submitted && <Spinner /> } | ||
{ submitted && <PostPublishPanelPostpublish /> } | ||
</div> | ||
</div> | ||
); | ||
|
@@ -97,6 +105,7 @@ const applyConnect = connect( | |
return { | ||
postType: getCurrentPostType( state ), | ||
isPublished: isCurrentPostPublished( state ), | ||
isScheduled: isCurrentPostScheduled( state ), | ||
isSaving: isSavingPost( state ), | ||
isDirty: isEditedPostDirty( state ), | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,12 +9,13 @@ import { connect } from 'react-redux'; | |
*/ | ||
import { PanelBody, Button, ClipboardButton, withAPIData } from '@wordpress/components'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { Component, compose } from '@wordpress/element'; | ||
import { Component, compose, Fragment } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal Dependencies | ||
*/ | ||
import { getCurrentPost, getCurrentPostType } from '../../store/selectors'; | ||
import PostScheduleLabel from '../post-schedule/label'; | ||
import { getCurrentPost, getCurrentPostType, isCurrentPostScheduled } from '../../store/selectors'; | ||
|
||
class PostPublishPanelPostpublish extends Component { | ||
constructor() { | ||
|
@@ -48,26 +49,35 @@ class PostPublishPanelPostpublish extends Component { | |
} | ||
|
||
render() { | ||
const { post, postType } = this.props; | ||
const { isScheduled, post, postType } = this.props; | ||
const viewPostLabel = get( postType, [ 'data', 'labels', 'view_item' ] ); | ||
|
||
const postPublishNonLinkHeader = isScheduled ? | ||
<Fragment>{ __( 'is now scheduled. It will go live on' ) } <PostScheduleLabel />.</Fragment> : | ||
__( 'is now live.' ); | ||
const postPublishBodyText = isScheduled ? | ||
__( 'The post address will be:' ) : | ||
__( 'What\'s next?' ); | ||
|
||
return ( | ||
<div className="post-publish-panel__postpublish"> | ||
<PanelBody className="post-publish-panel__postpublish-header"> | ||
<a href={ post.link }>{ post.title || __( '(no title)' ) }</a>{ __( ' is now live.' ) } | ||
<a href={ post.link }>{ post.title || __( '(no title)' ) }</a> { postPublishNonLinkHeader } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should not concatenate translated strings, because the order of the concatenated parts can change in localization:
https://codex.wordpress.org/I18n_for_WordPress_Developers#Best_Practices There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To preempt: It is unsolved this need to include React elements in translated strings. We can look to some other efforts as prior art: https://github.com/Automattic/interpolate-components But if we don't want to solve the larger problem, we're probably better off not localizing at all here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should definitely fix this — cc @jorgefilipecosta There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might take it upon myself to look at what would be needed for component interpolation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Description of the issue / task at #9846 |
||
</PanelBody> | ||
<PanelBody> | ||
<div><strong>{ __( 'What\'s next?' ) }</strong></div> | ||
<div><strong>{ postPublishBodyText }</strong></div> | ||
<input | ||
className="post-publish-panel__postpublish-link-input" | ||
readOnly | ||
value={ post.link } | ||
onFocus={ this.onSelectInput } | ||
/> | ||
<div className="post-publish-panel__postpublish-buttons"> | ||
<Button className="button" href={ post.link }> | ||
{ viewPostLabel } | ||
</Button> | ||
{ ! isScheduled && ( | ||
<Button className="button" href={ post.link }> | ||
{ viewPostLabel } | ||
</Button> | ||
) } | ||
|
||
<ClipboardButton className="button" text={ post.link } onCopy={ this.onCopy }> | ||
{ this.state.showCopyConfirmation ? __( 'Copied!' ) : __( 'Copy Link' ) } | ||
|
@@ -84,6 +94,7 @@ const applyConnect = connect( | |
return { | ||
post: getCurrentPost( state ), | ||
postTypeSlug: getCurrentPostType( state ), | ||
isScheduled: isCurrentPostScheduled( state ), | ||
}; | ||
} | ||
); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,9 @@ const { | |
getDocumentTitle, | ||
getEditedPostExcerpt, | ||
getEditedPostVisibility, | ||
isCurrentPostPending, | ||
isCurrentPostPublished, | ||
isCurrentPostScheduled, | ||
isEditedPostPublishable, | ||
isEditedPostSaveable, | ||
isEditedPostEmpty, | ||
|
@@ -587,6 +589,36 @@ describe( 'selectors', () => { | |
} ); | ||
} ); | ||
|
||
describe( 'isCurrentPostPending', () => { | ||
it( 'should return true for posts in pending state', () => { | ||
const state = { | ||
currentPost: { | ||
status: 'pending', | ||
}, | ||
}; | ||
|
||
expect( isCurrentPostPending( state ) ).toBe( true ); | ||
} ); | ||
|
||
it( 'should return false for draft posts', () => { | ||
const state = { | ||
currentPost: { | ||
status: 'draft', | ||
}, | ||
}; | ||
|
||
expect( isCurrentPostPending( state ) ).toBe( false ); | ||
} ); | ||
|
||
it( 'should return false if status is unknown', () => { | ||
const state = { | ||
currentPost: {}, | ||
}; | ||
|
||
expect( isCurrentPostPending( state ) ).toBe( false ); | ||
} ); | ||
} ); | ||
|
||
describe( 'isCurrentPostPublished', () => { | ||
it( 'should return true for public posts', () => { | ||
const state = { | ||
|
@@ -630,6 +662,58 @@ describe( 'selectors', () => { | |
} ); | ||
} ); | ||
|
||
describe( 'isCurrentPostScheduled', () => { | ||
it( 'should return true for posts with status future', () => { | ||
const state = { | ||
currentPost: { | ||
status: 'future', | ||
}, | ||
}; | ||
|
||
expect( isCurrentPostScheduled( state ) ).toBe( true ); | ||
} ); | ||
|
||
it( 'should return true for future scheduled posts', () => { | ||
const state = { | ||
currentPost: { | ||
status: 'future', | ||
date: '2100-05-30T17:21:39', | ||
}, | ||
}; | ||
|
||
expect( isCurrentPostScheduled( state ) ).toBe( true ); | ||
} ); | ||
|
||
it( 'should return false for old scheduled posts that were already published', () => { | ||
const state = { | ||
currentPost: { | ||
status: 'future', | ||
date: '2016-05-30T17:21:39', | ||
}, | ||
}; | ||
|
||
expect( isCurrentPostScheduled( state ) ).toBe( false ); | ||
} ); | ||
|
||
it( 'should return false for auto draft posts', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar as above. One of the tests might be obsolete. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I followed your feedback and now we test for auto-draft in scheduled posts and for the normal draft's in pending posts so no tests testing the same thing exist in each test case but we still test for but status in the app. |
||
const state = { | ||
currentPost: { | ||
status: 'auto-draft', | ||
}, | ||
}; | ||
|
||
expect( isCurrentPostScheduled( state ) ).toBe( false ); | ||
} ); | ||
|
||
it( 'should return false if status is unknown', () => { | ||
const state = { | ||
currentPost: {}, | ||
}; | ||
|
||
expect( isCurrentPostScheduled( state ) ).toBe( false ); | ||
} ); | ||
} ); | ||
|
||
describe( 'isEditedPostPublishable', () => { | ||
it( 'should return true for pending posts', () => { | ||
const state = { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are very simple corresponding tests which help to avoid regressions. Can we cover this case, too?