diff --git a/client/components/button/style.scss b/client/components/button/style.scss
index 122fa9443d36..576125e8e3a6 100644
--- a/client/components/button/style.scss
+++ b/client/components/button/style.scss
@@ -102,7 +102,7 @@ button {
}
// Primary buttons
-.button.is-primary {
+@mixin button-is-primary {
background: $blue-medium;
border-color: $blue-wordpress;
color: $white;
@@ -123,6 +123,10 @@ button {
}
}
+.button.is-primary {
+ @include button-is-primary;
+}
+
// Scary buttons
.button.is-scary {
color: $alert-red;
diff --git a/client/post-editor/editor-ground-control/index.jsx b/client/post-editor/editor-ground-control/index.jsx
index 8e962028b08a..f64b457ea5e4 100644
--- a/client/post-editor/editor-ground-control/index.jsx
+++ b/client/post-editor/editor-ground-control/index.jsx
@@ -24,6 +24,7 @@ const Card = require( 'components/card' ),
PostListFetcher = require( 'components/post-list-fetcher' ),
stats = require( 'lib/posts/stats' );
import { setDate } from 'state/ui/editor/post/actions';
+import EditorPublishButton from 'post-editor/editor-publish-button';
const EditorGroundControl = React.createClass( {
displayName: 'EditorGroundControl',
@@ -33,9 +34,9 @@ const EditorGroundControl = React.createClass( {
isSaveBlocked: React.PropTypes.bool,
isPublishing: React.PropTypes.bool,
isSaving: React.PropTypes.bool,
- onClose: React.PropTypes.func,
onPreview: React.PropTypes.func,
onPublish: React.PropTypes.func,
+ onSave: React.PropTypes.func,
onSaveDraft: React.PropTypes.func,
post: React.PropTypes.object,
setDate: React.PropTypes.func,
@@ -53,12 +54,11 @@ const EditorGroundControl = React.createClass( {
isSaveBlocked: false,
isPublishing: false,
isSaving: false,
- onClose: noop,
onPublish: noop,
onSaveDraft: noop,
post: null,
savedPost: null,
- setDate: () => {},
+ setDate: noop,
site: {}
};
},
@@ -94,65 +94,6 @@ const EditorGroundControl = React.createClass( {
return this.translate( 'Preview' );
},
- trackPrimaryButton: function() {
- const postEvents = {
- update: 'Clicked Update Post Button',
- schedule: 'Clicked Schedule Post Button',
- requestReview: 'Clicked Request-Review Post Button',
- publish: 'Clicked Publish Post Button',
- };
- const pageEvents = {
- update: 'Clicked Update Page Button',
- schedule: 'Clicked Schedule Page Button',
- requestReview: 'Clicked Request-Review Page Button',
- publish: 'Clicked Publish Page Button',
- };
- const buttonState = this.getPrimaryButtonState();
- const eventString = postUtils.isPage( this.props.post ) ? pageEvents[ buttonState ] : postEvents[ buttonState ];
- stats.recordEvent( eventString );
- stats.recordEvent( 'Clicked Primary Button' );
- },
-
- getPrimaryButtonState: function() {
- if (
- postUtils.isPublished( this.props.savedPost ) &&
- ! postUtils.isBackDatedPublished( this.props.savedPost ) &&
- ! postUtils.isFutureDated( this.props.post ) ||
- (
- this.props.savedPost &&
- this.props.savedPost.status === 'future' &&
- postUtils.isFutureDated( this.props.post )
- )
- ) {
- return 'update';
- }
-
- if ( postUtils.isFutureDated( this.props.post ) ) {
- return 'schedule';
- }
-
- if ( siteUtils.userCan( 'publish_posts', this.props.site ) ) {
- return 'publish';
- }
-
- if ( this.props.savedPost && this.props.savedPost.status === 'pending' ) {
- return 'update';
- }
-
- return 'requestReview';
- },
-
- getPrimaryButtonLabel: function() {
- const primaryButtonState = this.getPrimaryButtonState();
- const buttonLabels = {
- update: this.translate( 'Update' ),
- schedule: this.translate( 'Schedule' ),
- publish: this.translate( 'Publish' ),
- requestReview: this.translate( 'Submit for Review' ),
- };
- return buttonLabels[ primaryButtonState ];
- },
-
toggleSchedulePopover: function() {
this.setState( { showSchedulePopover: ! this.state.showSchedulePopover } );
},
@@ -251,36 +192,14 @@ const EditorGroundControl = React.createClass( {
! this.props.isSaveBlocked;
},
- isPrimaryButtonEnabled: function() {
- return ! this.props.isPublishing &&
- ! this.props.isSaveBlocked &&
- this.props.hasContent;
+ canPublishPost: function() {
+ return siteUtils.userCan( 'publish_posts', this.props.site );
},
toggleAdvancedStatus: function() {
this.setState( { showAdvanceStatus: ! this.state.showAdvanceStatus } );
},
- onPrimaryButtonClick: function() {
- this.trackPrimaryButton();
-
- if ( postUtils.isFutureDated( this.props.post ) ) {
- return this.props.onSave( 'future' );
- }
-
- if ( postUtils.isPublished( this.props.savedPost ) &&
- ! postUtils.isBackDatedPublished( this.props.savedPost )
- ) {
- return this.props.onSave();
- }
-
- if ( siteUtils.userCan( 'publish_posts', this.props.site ) ) {
- return this.props.onPublish();
- }
-
- return this.props.onSave( 'pending' );
- },
-
onSaveButtonClick: function() {
this.props.onSave();
const eventLabel = postUtils.isPage( this.props.page ) ? 'Clicked Save Page Button' : 'Clicked Save Post Button';
@@ -373,18 +292,21 @@ const EditorGroundControl = React.createClass( {
{ this.getPreviewLabel() }
-
- { siteUtils.userCan( 'publish_posts', this.props.site ) &&
+ isPublishing={ this.props.isPublishing }
+ isSaveBlocked={ this.props.isSaveBlocked }
+ hasContent={ this.props.hasContent }
+ />
+ { this.canPublishPost() &&
:
}
+
+ { postUtils.isFutureDated( this.props.post )
+ ? this.moment( this.props.post.date ).calendar()
+ : this.translate( 'Choose Date' )
+ }
+
}
{ this.renderDateTooltip() }
- { siteUtils.userCan( 'publish_posts', this.props.site ) &&
+ { this.canPublishPost() &&
this.schedulePostPopover()
}
diff --git a/client/post-editor/editor-ground-control/style.scss b/client/post-editor/editor-ground-control/style.scss
index c8b8109b374c..d98b4500bf19 100644
--- a/client/post-editor/editor-ground-control/style.scss
+++ b/client/post-editor/editor-ground-control/style.scss
@@ -38,15 +38,36 @@
margin: 0 4px 8px;
}
-.editor-ground-control__publish-button {
+.editor-ground-control .editor-publish-button {
border-radius: 4px 0 0 4px;
flex-grow: 1;
+
+ @include breakpoint( "<660px" ) {
+ display: none;
+ }
}
+
.editor-ground-control__time-button {
- border-radius: 0 4px 4px 0;
+ @include breakpoint( ">660px" ) {
+ @include button-is-primary;
+
+ border-radius: 0 4px 4px 0;
+ padding-left: 8px;
+ padding-right: 8px;
+ margin-left: -1px;
+ }
+
+ @include breakpoint( "<660px" ) {
+ width: 100%;
+ }
+}
+
+.editor-ground-control__time-button__label {
padding-left: 8px;
- padding-right: 8px;
- margin-left: -1px;
+
+ @include breakpoint( ">660px" ) {
+ display: none;
+ }
}
.editor-ground-control__save.button.is-link,
diff --git a/client/post-editor/editor-ground-control/test/index.jsx b/client/post-editor/editor-ground-control/test/index.jsx
index fd204760f1d1..665211997764 100644
--- a/client/post-editor/editor-ground-control/test/index.jsx
+++ b/client/post-editor/editor-ground-control/test/index.jsx
@@ -2,9 +2,7 @@
* External dependencies
*/
import { expect } from 'chai';
-import moment from 'moment';
import React from 'react';
-import sinon from 'sinon';
import mockery from 'mockery';
/**
@@ -79,154 +77,6 @@ describe( 'EditorGroundControl', function() {
} );
} );
- describe( '#getPrimaryButtonLabel()', function() {
- it( 'should return Update if the post was originally published and is still slated to be published', function() {
- var tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Update' );
- } );
-
- it( 'should return Update if the post was originally published and is currently reverted to non-published status', function() {
- var tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Update' );
- } );
-
- it( 'should return Schedule if the post is dated in the future and not scheduled', function() {
- var now = moment( new Date() ),
- nextMonth = now.month( now.month() + 1 ).format(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Schedule' );
- } );
-
- it( 'should return Schedule if the post is dated in the future and published', function() {
- var now = moment( new Date() ),
- nextMonth = now.month( now.month() + 1 ).format(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Schedule' );
- } );
-
- it( 'should return Update if the post is scheduled and dated in the future', function() {
- var now = moment( new Date() ),
- nextMonth = now.month( now.month() + 1 ).format(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Update' );
- } );
-
- it( 'should return Update if the post is scheduled, dated in the future, and next status is draft', function() {
- var now = moment( new Date() ),
- nextMonth = now.month( now.month() + 1 ).format(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Update' );
- } );
-
- it( 'should return Publish if the post is scheduled and dated in the past', function() {
- var now = moment( new Date() ),
- lastMonth = now.month( now.month() - 1 ).format(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Publish' );
- } );
-
- it( 'should return Update if the post was originally published and is scheduled with date in the past', function() {
- var now = moment(),
- lastMonth = now.month( now.month() - 1 ).format(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Update' );
- } );
-
- it( 'should return Publish if the post is a draft', function() {
- var tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Publish' );
- } );
-
- it( 'should return "Submit for Review" if the post is a draft and user can\'t publish', function() {
- var tree = shallow(
-
- ).instance();
-
- expect( tree.getPrimaryButtonLabel() ).to.equal( 'Submit for Review' );
- } );
- } );
-
describe( '#isSaveEnabled()', function() {
it( 'should return false if form is saving', function() {
var tree = shallow( ).instance();
@@ -296,180 +146,4 @@ describe( 'EditorGroundControl', function() {
expect( tree.isPreviewEnabled() ).to.be.false;
} );
} );
-
- describe( '#isPrimaryButtonEnabled()', function() {
- it( 'should return true if form is not publishing and post is not empty', function() {
- var tree = shallow( ).instance();
-
- expect( tree.isPrimaryButtonEnabled() ).to.be.true;
- } );
-
- it( 'should return true if form is not publishind and post is new and has content, but is not dirty', function() {
- var tree = shallow( ).instance();
-
- expect( tree.isPrimaryButtonEnabled() ).to.be.true;
- } );
-
- it( 'should return false if form is publishing', function() {
- var tree = shallow( ).instance();
-
- expect( tree.isPrimaryButtonEnabled() ).to.be.false;
- } );
-
- it( 'should return false if saving is blocked', function() {
- var tree = shallow( ).instance();
-
- expect( tree.isPrimaryButtonEnabled() ).to.be.false;
- } );
-
- it( 'should return false if not dirty and has no content', function() {
- var tree = shallow( ).instance();
-
- expect( tree.isPrimaryButtonEnabled() ).to.be.false;
- } );
-
- it( 'should return false if post has no content', function() {
- var tree = shallow( ).instance();
-
- expect( tree.isPrimaryButtonEnabled() ).to.be.false;
- } );
- } );
-
- describe( '#onPrimaryButtonClick', function() {
- it( 'should publish a draft', function() {
- var onPublish = sinon.spy(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- tree.onPrimaryButtonClick();
-
- expect( onPublish ).to.have.been.called;
- } );
-
- it( 'should schedule a posted dated in future', function() {
- var now = moment( new Date() ),
- nextMonth = now.month( now.month() + 1 ).format(),
- onSave = sinon.spy(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- tree.onPrimaryButtonClick();
-
- expect( onSave ).to.have.been.calledWith( 'future' );
- } );
-
- it( 'should save a scheduled post dated in future', function() {
- var now = moment( new Date() ),
- nextMonth = now.month( now.month() + 1 ).format(),
- onSave = sinon.spy(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- tree.onPrimaryButtonClick();
-
- expect( onSave ).to.have.been.calledWith( 'future' );
- } );
-
- it( 'should publish a scheduled post dated in past', function() {
- var now = moment( new Date() ),
- lastMonth = now.month( now.month() - 1 ).format(),
- onPublish = sinon.spy(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- tree.onPrimaryButtonClick();
-
- expect( onPublish ).to.have.been.called;
- } );
-
- it( 'should update a published post that has changed status', function() {
- var onSave = sinon.spy(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- tree.onPrimaryButtonClick();
-
- expect( onSave ).to.have.been.called;
- } );
-
- it( 'should update a published post scheduled in the past', function() {
- var now = moment( new Date() ),
- lastMonth = now.month( now.month() - 1 ).format(),
- onSave = sinon.spy(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- tree.onPrimaryButtonClick();
-
- expect( onSave ).to.have.been.called;
- } );
-
- it( 'should set status to "pending" if the user can\'t publish', function() {
- var onSave = sinon.spy(),
- tree;
-
- tree = shallow(
-
- ).instance();
-
- tree.onPrimaryButtonClick();
-
- expect( onSave ).to.have.been.calledWith( 'pending' );
- } );
- } );
} );
diff --git a/client/post-editor/editor-mobile-navigation/index.jsx b/client/post-editor/editor-mobile-navigation/index.jsx
index d82f4450497f..151c1baee4a2 100644
--- a/client/post-editor/editor-mobile-navigation/index.jsx
+++ b/client/post-editor/editor-mobile-navigation/index.jsx
@@ -1,19 +1,42 @@
/**
* External dependencies
*/
-const React = require( 'react' );
+import React, { PropTypes } from 'react';
+import classnames from 'classnames';
/**
* Internal dependencies
*/
-const Site = require( 'my-sites/site' ),
- Gridicon = require( 'components/gridicon' ),
- layoutFocus = require( 'lib/layout-focus' );
+import Gridicon from 'components/gridicon';
+import EditorPublishButton from 'post-editor/editor-publish-button';
+import Button from 'components/button';
+import observe from 'lib/mixins/data-observe';
-const EditorMobileNavigation = React.createClass( {
+export default React.createClass( {
+ displayName: 'EditorMobileNavigation',
- toggleSidebar: function() {
- layoutFocus.set( 'sidebar' );
+ mixins: [ observe( 'layoutFocus' ) ],
+
+ propTypes: {
+ site: PropTypes.object,
+ post: PropTypes.object,
+ savedPost: PropTypes.object,
+ onSave: PropTypes.func,
+ onPublish: PropTypes.func,
+ tabIndex: PropTypes.number,
+ isPublishing: PropTypes.bool,
+ isSaveBlocked: PropTypes.bool,
+ hasContent: PropTypes.bool,
+ onClose: PropTypes.func,
+ layoutFocus: PropTypes.object
+ },
+
+ openSidebar: function() {
+ this.props.layoutFocus.set( 'sidebar' );
+ },
+
+ closeSidebar: function() {
+ this.props.layoutFocus.set( 'content' );
},
render: function() {
@@ -23,17 +46,40 @@ const EditorMobileNavigation = React.createClass( {
return (
-
-
-
+
+
);
}
} );
-
-module.exports = EditorMobileNavigation;
diff --git a/client/post-editor/editor-mobile-navigation/style.scss b/client/post-editor/editor-mobile-navigation/style.scss
index 05ac6f0e8546..b5a80dddad2c 100644
--- a/client/post-editor/editor-mobile-navigation/style.scss
+++ b/client/post-editor/editor-mobile-navigation/style.scss
@@ -1,42 +1,41 @@
.editor-mobile-navigation {
- color: $gray;
display: flex;
justify-content: space-between;
align-items: center;
- background-color: $gray-light;
+ background-color: $white;
+ border-bottom: 1px lighten( $gray, 20% ) solid;
@include breakpoint( ">660px" ) {
display: none;
}
}
-.editor-mobile-navigation__close {
- cursor: pointer;
- margin-right: 4px;
- padding: 8px;
+.editor-mobile-navigation__actions {
+ display: inline;
+ line-height: 0;
+}
- &:hover {
- cursor: button;
- fill: $gray-dark;
- }
+.editor-mobile-navigation__tabs {
+ display: inline-block;
+ border-left: 1px lighten( $gray, 20% ) solid;
+ border-right: 1px lighten( $gray, 20% ) solid;
}
-.editor-mobile-navigation .site {
- .site__title {
- padding: 6px 0;
- }
+.editor-mobile-navigation__icon {
+ cursor: pointer;
+ padding: 4px 12px;
+ color: lighten( $gray, 10% );
- .site__domain {
- display: none;
+ &:hover {
+ color: $gray-dark;
}
- .site__title::after,
- .site__domain::after {
- @include long-content-fade( $color: $gray-light );
+ &.is-selected {
+ cursor: default;
+ color: $gray-dark;
}
}
-.editor-mobile-navigation__toggle {
- margin-right: 8px;
+.editor-mobile-navigation .editor-publish-button {
+ margin-right: 12px;
}
-
diff --git a/client/post-editor/editor-publish-button/index.jsx b/client/post-editor/editor-publish-button/index.jsx
new file mode 100644
index 000000000000..515fefc3795a
--- /dev/null
+++ b/client/post-editor/editor-publish-button/index.jsx
@@ -0,0 +1,128 @@
+/**
+ * External dependencies
+ */
+import React, { PropTypes } from 'react';
+
+/**
+ * Internal dependencies
+ */
+import localize from 'lib/mixins/i18n/localize';
+import stats from 'lib/posts/stats';
+import postUtils from 'lib/posts/utils';
+import siteUtils from 'lib/site/utils';
+import Button from 'components/button';
+
+export const EditorPublishButton = React.createClass( {
+ propTypes: {
+ site: PropTypes.object,
+ post: PropTypes.object,
+ savedPost: PropTypes.object,
+ onSave: PropTypes.func,
+ onPublish: PropTypes.func,
+ tabIndex: PropTypes.number,
+ isPublishing: PropTypes.bool,
+ isSaveBlocked: PropTypes.bool,
+ hasContent: PropTypes.bool
+ },
+
+ trackClick: function() {
+ const postEvents = {
+ update: 'Clicked Update Post Button',
+ schedule: 'Clicked Schedule Post Button',
+ requestReview: 'Clicked Request-Review Post Button',
+ publish: 'Clicked Publish Post Button'
+ };
+ const pageEvents = {
+ update: 'Clicked Update Page Button',
+ schedule: 'Clicked Schedule Page Button',
+ requestReview: 'Clicked Request-Review Page Button',
+ publish: 'Clicked Publish Page Button'
+ };
+ const buttonState = this.getButtonState();
+ const eventString = postUtils.isPage( this.props.post ) ? pageEvents[ buttonState ] : postEvents[ buttonState ];
+ stats.recordEvent( eventString );
+ stats.recordEvent( 'Clicked Primary Button' );
+ },
+
+ getButtonState: function() {
+ if (
+ postUtils.isPublished( this.props.savedPost ) &&
+ ! postUtils.isBackDatedPublished( this.props.savedPost ) &&
+ ! postUtils.isFutureDated( this.props.post ) ||
+ (
+ this.props.savedPost &&
+ this.props.savedPost.status === 'future' &&
+ postUtils.isFutureDated( this.props.post )
+ )
+ ) {
+ return 'update';
+ }
+
+ if ( postUtils.isFutureDated( this.props.post ) ) {
+ return 'schedule';
+ }
+
+ if ( siteUtils.userCan( 'publish_posts', this.props.site ) ) {
+ return 'publish';
+ }
+
+ if ( this.props.savedPost && this.props.savedPost.status === 'pending' ) {
+ return 'update';
+ }
+
+ return 'requestReview';
+ },
+
+ getButtonLabel: function() {
+ switch ( this.getButtonState() ) {
+ case 'update':
+ return this.props.translate( 'Update' );
+ case 'schedule':
+ return this.props.translate( 'Schedule' );
+ case 'publish':
+ return this.props.translate( 'Publish' );
+ case 'requestReview':
+ return this.props.translate( 'Submit for Review' );
+ }
+ },
+
+ onClick: function() {
+ if ( postUtils.isFutureDated( this.props.post ) ) {
+ return this.props.onSave( 'future' );
+ }
+
+ if ( postUtils.isPublished( this.props.savedPost ) &&
+ ! postUtils.isBackDatedPublished( this.props.savedPost )
+ ) {
+ return this.props.onSave();
+ }
+
+ if ( siteUtils.userCan( 'publish_posts', this.props.site ) ) {
+ return this.props.onPublish();
+ }
+
+ return this.props.onSave( 'pending' );
+ },
+
+ isEnabled: function() {
+ return ! this.props.isPublishing &&
+ ! this.props.isSaveBlocked &&
+ this.props.hasContent;
+ },
+
+ render: function() {
+ return (
+
+ );
+ }
+} );
+
+export default localize( EditorPublishButton );
diff --git a/client/post-editor/editor-publish-button/test/index.jsx b/client/post-editor/editor-publish-button/test/index.jsx
new file mode 100644
index 000000000000..fd17154964b1
--- /dev/null
+++ b/client/post-editor/editor-publish-button/test/index.jsx
@@ -0,0 +1,376 @@
+/**
+ * External dependencies
+ */
+import { expect } from 'chai';
+import moment from 'moment';
+import React from 'react';
+import sinon from 'sinon';
+import TestUtils from 'react-addons-test-utils';
+import identity from 'lodash/identity';
+
+/**
+ * Internal dependencies
+ */
+import useFakeDom from 'test/helpers/use-fake-dom';
+
+/**
+ * Module variables
+ */
+
+describe( 'EditorPublishButton', function() {
+ var EditorPublishButton,
+ shallow,
+ MOCK_SITE = {
+ capabilities: {
+ publish_posts: true
+ },
+ options: {}
+ };
+
+ useFakeDom();
+
+ this.timeout( 10 * 1000 );
+
+ before( function() {
+ shallow = require( 'enzyme' ).shallow;
+ EditorPublishButton = require( '../' ).EditorPublishButton;
+ } );
+
+ describe( '#getButtonLabel()', function() {
+ it( 'should return Update if the post was originally published and is still slated to be published', function() {
+ const tree = shallow(
+
+ ).instance();
+ expect( tree.getButtonLabel() ).to.equal( 'Update' );
+ } );
+
+ it( 'should return Update if the post was originally published and is currently reverted to non-published status', function() {
+ const tree = shallow(
+
+ ).instance();
+
+ expect( tree.getButtonLabel() ).to.equal( 'Update' );
+ } );
+
+ it( 'should return Schedule if the post is dated in the future and not scheduled', function() {
+ var now = moment( new Date() ),
+ nextMonth = now.month( now.month() + 1 ).format(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ expect( tree.getButtonLabel() ).to.equal( 'Schedule' );
+ } );
+
+ it( 'should return Schedule if the post is dated in the future and published', function() {
+ var now = moment( new Date() ),
+ nextMonth = now.month( now.month() + 1 ).format(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ expect( tree.getButtonLabel() ).to.equal( 'Schedule' );
+ } );
+
+ it( 'should return Update if the post is scheduled and dated in the future', function() {
+ var now = moment( new Date() ),
+ nextMonth = now.month( now.month() + 1 ).format(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ expect( tree.getButtonLabel() ).to.equal( 'Update' );
+ } );
+
+ it( 'should return Update if the post is scheduled, dated in the future, and next status is draft', function() {
+ var now = moment( new Date() ),
+ nextMonth = now.month( now.month() + 1 ).format(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ expect( tree.getButtonLabel() ).to.equal( 'Update' );
+ } );
+
+ it( 'should return Publish if the post is scheduled and dated in the past', function() {
+ var now = moment( new Date() ),
+ lastMonth = now.month( now.month() - 1 ).format(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ expect( tree.getButtonLabel() ).to.equal( 'Publish' );
+ } );
+
+ it( 'should return Publish if the post is a draft', function() {
+ const tree = shallow(
+
+ ).instance();
+
+ expect( tree.getButtonLabel() ).to.equal( 'Publish' );
+ } );
+
+ it( 'should return "Submit for Review" if the post is a draft and user can\'t publish', function() {
+ const tree = shallow(
+
+ ).instance();
+
+ expect( tree.getButtonLabel() ).to.equal( 'Submit for Review' );
+ } );
+ } );
+
+ describe( '#isEnabled()', function() {
+ it( 'should return true if form is not publishing and post is not empty', function() {
+ const tree = shallow(
+ ).instance();
+
+ expect( tree.isEnabled() ).to.be.true;
+ } );
+
+ it( 'should return true if form is not publishind and post is new and has content, but is not dirty', function() {
+ const tree = shallow(
+ ).instance();
+
+ expect( tree.isEnabled() ).to.be.true;
+ } );
+
+ it( 'should return false if form is publishing', function() {
+ const tree = shallow(
+ ).instance();
+
+ expect( tree.isEnabled() ).to.be.false;
+ } );
+
+ it( 'should return false if saving is blocked', function() {
+ const tree = shallow(
+ ).instance();
+
+ expect( tree.isEnabled() ).to.be.false;
+ } );
+
+ it( 'should return false if not dirty and has no content', function() {
+ const tree = shallow(
+ ).instance();
+
+ expect( tree.isEnabled() ).to.be.false;
+ } );
+
+ it( 'should return false if post has no content', function() {
+ const tree = shallow(
+ ).instance();
+
+ expect( tree.isEnabled() ).to.be.false;
+ } );
+ } );
+
+ describe( '#onClick', function() {
+ it( 'should publish a draft', function() {
+ var onPublish = sinon.spy(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ tree.onClick();
+
+ expect( onPublish ).to.have.been.called;
+ } );
+
+ it( 'should schedule a posted dated in future', function() {
+ var now = moment( new Date() ),
+ nextMonth = now.month( now.month() + 1 ).format(),
+ onSave = sinon.spy(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ tree.onClick();
+
+ expect( onSave ).to.have.been.calledWith( 'future' );
+ } );
+
+ it( 'should save a scheduled post dated in future', function() {
+ var now = moment( new Date() ),
+ nextMonth = now.month( now.month() + 1 ).format(),
+ onSave = sinon.spy(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ tree.onClick();
+
+ expect( onSave ).to.have.been.calledWith( 'future' );
+ } );
+
+ it( 'should publish a scheduled post dated in past', function() {
+ var now = moment( new Date() ),
+ lastMonth = now.month( now.month() - 1 ).format(),
+ onPublish = sinon.spy(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ tree.onClick();
+
+ expect( onPublish ).to.have.been.called;
+ } );
+
+ it( 'should update a published post that has changed status', function() {
+ var onSave = sinon.spy(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ tree.onClick();
+
+ expect( onSave ).to.have.been.called;
+ } );
+
+ it( 'should set status to "pending" if the user can\'t publish', function() {
+ var onSave = sinon.spy(),
+ tree;
+
+ tree = shallow(
+
+ ).instance();
+
+ tree.onClick();
+
+ expect( onSave ).to.have.been.calledWith( 'pending' );
+ } );
+ } );
+} );
diff --git a/client/post-editor/editor-sidebar/header.jsx b/client/post-editor/editor-sidebar/header.jsx
index de4e7a4ffeb6..61d6445f3bb9 100644
--- a/client/post-editor/editor-sidebar/header.jsx
+++ b/client/post-editor/editor-sidebar/header.jsx
@@ -20,7 +20,7 @@ import Button from 'components/button';
import Gridicon from 'components/gridicon';
import DraftsButton from 'post-editor/drafts-button';
-function EditorSidebarHeader( { translate, type, showDrafts, toggleDrafts, allPostsUrl, toggleSidebar } ) {
+function EditorSidebarHeader( { translate, type, showDrafts, toggleDrafts, allPostsUrl } ) {
const className = classnames( 'editor-sidebar__header', {
'is-drafts-visible': showDrafts
} );
@@ -55,11 +55,6 @@ function EditorSidebarHeader( { translate, type, showDrafts, toggleDrafts, allPo
{ type === 'post' && (
) }
-
);
}
@@ -69,8 +64,7 @@ EditorSidebarHeader.propTypes = {
type: PropTypes.string,
showDrafts: PropTypes.bool,
toggleDrafts: PropTypes.func,
- allPostsUrl: PropTypes.string,
- toggleSidebar: PropTypes.func
+ allPostsUrl: PropTypes.string
};
export default connect(
diff --git a/client/post-editor/editor-sidebar/index.jsx b/client/post-editor/editor-sidebar/index.jsx
new file mode 100644
index 000000000000..a2566c48532a
--- /dev/null
+++ b/client/post-editor/editor-sidebar/index.jsx
@@ -0,0 +1,81 @@
+/**
+ * External dependencies
+ */
+import React, { PropTypes } from 'react';
+
+/**
+ * Internal dependencies
+ */
+import EditorDrawer from 'post-editor/editor-drawer';
+import EditorGroundControl from 'post-editor/editor-ground-control';
+import DraftList from 'my-sites/drafts/draft-list';
+import EditorSidebarHeader from './header';
+
+export default React.createClass( {
+ displayName: 'EditorSidebar',
+
+ propTypes: {
+ allPostsUrl: PropTypes.string,
+ sites: PropTypes.object,
+ onTitleClick: PropTypes.func,
+ savedPost: PropTypes.object,
+ post: PropTypes.object,
+ isNew: PropTypes.bool,
+ isDirty: PropTypes.bool,
+ isSaveBlocked: PropTypes.bool,
+ hasContent: PropTypes.bool,
+ isSaving: PropTypes.bool,
+ isPublishing: PropTypes.bool,
+ onSave: PropTypes.func,
+ onPreview: PropTypes.func,
+ onPublish: PropTypes.func,
+ onTrashingPost: PropTypes.func,
+ site: PropTypes.object,
+ type: PropTypes.string,
+ showDrafts: PropTypes.bool
+ },
+
+ render() {
+ return (
+
+
+ { this.props.showDrafts
+ ?
+ :
+
+
+
}
+
+ );
+ }
+
+} );
diff --git a/client/post-editor/editor-sidebar/style.scss b/client/post-editor/editor-sidebar/style.scss
index cb7dc27dfe8a..473eb471135b 100644
--- a/client/post-editor/editor-sidebar/style.scss
+++ b/client/post-editor/editor-sidebar/style.scss
@@ -1,12 +1,27 @@
+.post-editor__sidebar {
+ @extend .sidebar;
+
+ @include breakpoint( "<660px" ) {
+ position: relative;
+ top: 0;
+ left: 0;
+ display: none;
+
+ .focus-sidebar & {
+ display: block;
+ transform: none;
+ }
+ }
+}
+
.editor-sidebar__header {
- display: flex;
- flex-shrink: 0;
- justify-content: flex-end;
- align-items: center;
- padding: 8px;
+ display: none;
@include breakpoint( ">660px" ) {
+ display: flex;
justify-content: space-between;
+ align-items: center;
+ padding: 8px;
}
}
diff --git a/client/post-editor/editor-slug/index.jsx b/client/post-editor/editor-slug/index.jsx
index 457ed30992f2..1005070ce1ba 100644
--- a/client/post-editor/editor-slug/index.jsx
+++ b/client/post-editor/editor-slug/index.jsx
@@ -36,7 +36,7 @@ const PostEditorSlug = React.createClass( {
getDefaultProps() {
return {
- setSlug: () => {},
+ setSlug: noop,
onEscEnter: noop,
isEditable: true
};
diff --git a/client/post-editor/post-editor.jsx b/client/post-editor/post-editor.jsx
index 967026716dc0..325a377463b0 100644
--- a/client/post-editor/post-editor.jsx
+++ b/client/post-editor/post-editor.jsx
@@ -18,9 +18,7 @@ const actions = require( 'lib/posts/actions' ),
route = require( 'lib/route' ),
PostEditStore = require( 'lib/posts/post-edit-store' ),
EditorActionBar = require( 'post-editor/editor-action-bar' ),
- EditorDrawer = require( 'post-editor/editor-drawer' ),
FeaturedImage = require( 'post-editor/editor-featured-image' ),
- EditorGroundControl = require( 'post-editor/editor-ground-control' ),
EditorTitleContainer = require( 'post-editor/editor-title/container' ),
EditorPageSlug = require( 'post-editor/editor-page-slug' ),
NoticeAction = require( 'components/notice/notice-action' ),
@@ -34,7 +32,6 @@ const actions = require( 'lib/posts/actions' ),
layoutFocus = require( 'lib/layout-focus' ),
titleActions = require( 'lib/screen-title/actions' ),
observe = require( 'lib/mixins/data-observe' ),
- DraftList = require( 'my-sites/drafts/draft-list' ),
PreferencesActions = require( 'lib/preferences/actions' ),
InvalidURLDialog = require( 'post-editor/invalid-url-dialog' ),
RestorePostDialog = require( 'post-editor/restore-post-dialog' ),
@@ -59,7 +56,7 @@ import {
import { setEditorLastDraft, resetEditorLastDraft } from 'state/ui/editor/last-draft/actions';
import { isEditorDraftsVisible } from 'state/ui/editor/selectors';
import { toggleEditorDraftsVisible } from 'state/ui/editor/actions';
-import EditorSidebarHeader from 'post-editor/editor-sidebar/header';
+import EditorSidebar from 'post-editor/editor-sidebar';
const messages = {
post: {
@@ -327,8 +324,18 @@ const PostEditor = React.createClass( {
return (
+
-
- { this.iframePreviewEnabled()
- ?
- : null }
-
-
-
- { this.props.showDrafts
- ?
- :
-
-
-
-
}
+
+ { this.iframePreviewEnabled() ?
+
+ : null }
{ isTrashed
?
660px" ) {
+ display: block;
backface-visibility: visible;
perspective: none;
}
@@ -49,10 +61,6 @@
}
}
-.post-editor__sidebar {
- @extend .sidebar;
-}
-
.editor__header,
.editor .mce-container-body,
.editor .tinymce {
@@ -136,14 +144,6 @@
width: 160px;
z-index: z-index( 'root', '.editor__switch-mode' );
- .focus-sidebar & {
- z-index: auto;
-
- @include breakpoint( ">660px" ) {
- z-index: z-index( 'root', '.editor__switch-mode' );
- }
- }
-
@include breakpoint( ">960px" ) {
right: 32px;
}
diff --git a/client/post-editor/test/post-editor.jsx b/client/post-editor/test/post-editor.jsx
index c601f6853b4d..7dc702d08aff 100644
--- a/client/post-editor/test/post-editor.jsx
+++ b/client/post-editor/test/post-editor.jsx
@@ -47,13 +47,14 @@ describe( 'PostEditor', function() {
mockery.registerMock( 'post-editor/editor-title/container', MOCK_COMPONENT );
mockery.registerMock( 'post-editor/editor-page-slug', MOCK_COMPONENT );
mockery.registerMock( 'post-editor/editor-media-advanced', MOCK_COMPONENT );
+ mockery.registerMock( 'post-editor/editor-mobile-navigation', MOCK_COMPONENT );
mockery.registerMock( 'post-editor/editor-author', MOCK_COMPONENT );
mockery.registerMock( 'post-editor/editor-visibility', MOCK_COMPONENT );
mockery.registerMock( 'post-editor/editor-word-count', MOCK_COMPONENT );
mockery.registerMock( 'post-editor/editor-preview', MOCK_COMPONENT );
mockery.registerMock( 'post-editor/invalid-url-dialog', MOCK_COMPONENT );
mockery.registerMock( 'post-editor/restore-post-dialog', MOCK_COMPONENT );
- mockery.registerMock( 'post-editor/editor-sidebar/header', MOCK_COMPONENT );
+ mockery.registerMock( 'post-editor/editor-sidebar', MOCK_COMPONENT );
mockery.registerMock( './editor-preview', MOCK_COMPONENT );
mockery.registerMock( 'my-sites/drafts/draft-list', MOCK_COMPONENT );
mockery.registerMock( 'lib/layout-focus', { set() {} } );