diff --git a/edit-post/components/header/index.js b/edit-post/components/header/index.js index 9fcafc7b32582..8c74fda38e9ce 100644 --- a/edit-post/components/header/index.js +++ b/edit-post/components/header/index.js @@ -7,6 +7,7 @@ import { PostPreviewButton, PostSavedState, PostPublishPanelToggle, + PostPublishButton, } from '@wordpress/editor'; import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; @@ -26,6 +27,7 @@ function Header( { openGeneralSidebar, closeGeneralSidebar, isPublishSidebarOpened, + isPublishSidebarEnabled, togglePublishSidebar, hasActiveMetaboxes, isSaving, @@ -48,12 +50,19 @@ function Header( { forceIsSaving={ isSaving } /> - + { isPublishSidebarEnabled ? ( + + ) : ( + + ) }
( { isEditorSidebarOpened: select( 'core/edit-post' ).isEditorSidebarOpened(), isPublishSidebarOpened: select( 'core/edit-post' ).isPublishSidebarOpened(), + isPublishSidebarEnabled: select( 'core/editor' ).isPublishSidebarEnabled(), hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(), isSaving: select( 'core/edit-post' ).isSavingMetaBoxes(), hasBlockSelection: !! select( 'core/editor' ).getBlockSelectionStart(), diff --git a/edit-post/components/header/more-menu/index.js b/edit-post/components/header/more-menu/index.js index 17ea090a1b710..60f4a9225b52d 100644 --- a/edit-post/components/header/more-menu/index.js +++ b/edit-post/components/header/more-menu/index.js @@ -14,6 +14,7 @@ import PluginMoreMenuGroup from '../plugins-more-menu-group'; import TipsToggle from '../tips-toggle'; import KeyboardShortcutsHelpMenuItem from '../keyboard-shortcuts-help-menu-item'; import WritingMenu from '../writing-menu'; +import PublishSidebarToggle from '../publish-sidebar-toggle'; const MoreMenu = () => ( ( { __( 'Manage All Reusable Blocks' ) } + diff --git a/edit-post/components/header/publish-sidebar-toggle/index.js b/edit-post/components/header/publish-sidebar-toggle/index.js new file mode 100644 index 0000000000000..b3949ec866b95 --- /dev/null +++ b/edit-post/components/header/publish-sidebar-toggle/index.js @@ -0,0 +1,38 @@ +/** + * WordPress Dependencies + */ +import { __ } from '@wordpress/i18n'; +import { MenuItem } from '@wordpress/components'; +import { compose } from '@wordpress/compose'; +import { withSelect, withDispatch } from '@wordpress/data'; + +const PublishSidebarToggle = function( { onToggle, isEnabled } ) { + return ( + + { __( 'Enable Pre-publish Checks' ) } + + ); +}; + +export default compose( [ + withSelect( ( select ) => ( { + isEnabled: select( 'core/editor' ).isPublishSidebarEnabled(), + } ) ), + withDispatch( ( dispatch, ownProps ) => ( { + onToggle() { + const { disablePublishSidebar, enablePublishSidebar } = dispatch( 'core/editor' ); + if ( ownProps.isEnabled ) { + disablePublishSidebar(); + } else { + enablePublishSidebar(); + } + ownProps.onToggle(); + }, + } ) ), +] )( PublishSidebarToggle ); diff --git a/packages/editor/src/components/post-publish-panel/index.js b/packages/editor/src/components/post-publish-panel/index.js index 93a21c18b84c4..f4b7248d8a715 100644 --- a/packages/editor/src/components/post-publish-panel/index.js +++ b/packages/editor/src/components/post-publish-panel/index.js @@ -8,8 +8,8 @@ import { get } from 'lodash'; */ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; -import { IconButton, Spinner } from '@wordpress/components'; -import { withSelect } from '@wordpress/data'; +import { IconButton, Spinner, CheckboxControl } from '@wordpress/components'; +import { withSelect, withDispatch } from '@wordpress/data'; import { compose } from '@wordpress/compose'; /** @@ -62,7 +62,7 @@ class PostPublishPanel extends Component { } render() { - const { isScheduled, onClose, forceIsDirty, forceIsSaving, PrePublishExtension, PostPublishExtension, ...additionalProps } = this.props; + const { isScheduled, isPublishSidebarEnabled, onClose, onTogglePublishSidebar, forceIsDirty, forceIsSaving, PrePublishExtension, PostPublishExtension, ...additionalProps } = this.props; const { loading, submitted } = this.state; return (
@@ -97,6 +97,13 @@ class PostPublishPanel extends Component { ) }
+
+ +
); } @@ -112,6 +119,7 @@ export default compose( [ isSavingPost, isEditedPostDirty, } = select( 'core/editor' ); + const { isPublishSidebarEnabled } = select( 'core/editor' ); return { postType: getCurrentPostType(), hasPublishAction: get( getCurrentPost(), [ '_links', 'wp:action-publish' ], false ), @@ -119,6 +127,19 @@ export default compose( [ isScheduled: isCurrentPostScheduled(), isSaving: isSavingPost(), isDirty: isEditedPostDirty(), + isPublishSidebarEnabled: isPublishSidebarEnabled(), + }; + } ), + withDispatch( ( dispatch, { isPublishSidebarEnabled } ) => { + const { disablePublishSidebar, enablePublishSidebar } = dispatch( 'core/editor' ); + return { + onTogglePublishSidebar: ( ) => { + if ( isPublishSidebarEnabled ) { + disablePublishSidebar(); + } else { + enablePublishSidebar(); + } + }, }; } ), ] )( PostPublishPanel ); diff --git a/packages/editor/src/components/post-publish-panel/style.scss b/packages/editor/src/components/post-publish-panel/style.scss index 0a2b539bca965..137fc6876793f 100644 --- a/packages/editor/src/components/post-publish-panel/style.scss +++ b/packages/editor/src/components/post-publish-panel/style.scss @@ -30,6 +30,12 @@ flex-grow: 1; } +.editor-post-publish-panel__footer { + padding: 16px; + position: absolute; + bottom: 0; +} + .components-button.editor-post-publish-panel__toggle.is-primary { display: inline-flex; align-items: center; diff --git a/packages/editor/src/store/actions.js b/packages/editor/src/store/actions.js index d9d4e3186b73c..4d81471a6fa4f 100644 --- a/packages/editor/src/store/actions.js +++ b/packages/editor/src/store/actions.js @@ -763,3 +763,25 @@ export function unregisterToken( name ) { name, }; } + +/** + * Returns an action object used in signalling that the user has enabled the publish sidebar. + * + * @return {Object} Action object + */ +export function enablePublishSidebar() { + return { + type: 'ENABLE_PUBLISH_SIDEBAR', + }; +} + +/** + * Returns an action object used in signalling that the user has disabled the publish sidebar. + * + * @return {Object} Action object + */ +export function disablePublishSidebar() { + return { + type: 'DISABLE_PUBLISH_SIDEBAR', + }; +} diff --git a/packages/editor/src/store/defaults.js b/packages/editor/src/store/defaults.js index bc4be6692059b..1118028ccd2d9 100644 --- a/packages/editor/src/store/defaults.js +++ b/packages/editor/src/store/defaults.js @@ -5,6 +5,7 @@ import { __ } from '@wordpress/i18n'; export const PREFERENCES_DEFAULTS = { insertUsage: {}, + isPublishSidebarEnabled: true, }; /** diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index 09d58a57be956..fbca6f70ad976 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -773,9 +773,6 @@ export function settings( state = EDITOR_SETTINGS_DEFAULTS, action ) { * Reducer returning the user preferences. * * @param {Object} state Current state. - * @param {string} state.mode Current editor mode, either "visual" or "text". - * @param {boolean} state.isSidebarOpened Whether the sidebar is opened or closed. - * @param {Object} state.panels The state of the different sidebar panels. * @param {Object} action Dispatched action. * * @return {string} Updated state. @@ -810,6 +807,18 @@ export function preferences( state = PREFERENCES_DEFAULTS, action ) { ...state, insertUsage: omitBy( state.insertUsage, ( { insert } ) => insert.ref === action.id ), }; + + case 'ENABLE_PUBLISH_SIDEBAR': + return { + ...state, + isPublishSidebarEnabled: true, + }; + + case 'DISABLE_PUBLISH_SIDEBAR': + return { + ...state, + isPublishSidebarEnabled: false, + }; } return state; diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index 81bc282d774e6..10068aadf3609 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -27,6 +27,11 @@ import { serialize, getBlockType, getBlockTypes, hasBlockSupport, hasChildBlocks import { moment } from '@wordpress/date'; import { removep } from '@wordpress/autop'; +/** + * Dependencies + */ +import { PREFERENCES_DEFAULTS } from './defaults'; + /*** * Module constants */ @@ -1879,3 +1884,18 @@ export function getTokenSettings( state, name ) { export function canUserUseUnfilteredHTML( state ) { return has( getCurrentPost( state ), [ '_links', 'wp:action-unfiltered_html' ] ); } + +/** + * Returns whether the pre-publish panel should be shown + * or skipped when the user clicks the "publish" button. + * + * @param {Object} state Global application state. + * + * @return {boolean} Whether the pre-publish panel should be shown or not. + */ +export function isPublishSidebarEnabled( state ) { + if ( state.preferences.hasOwnProperty( 'isPublishSidebarEnabled' ) ) { + return state.preferences.isPublishSidebarEnabled; + } + return PREFERENCES_DEFAULTS.isPublishSidebarEnabled; +} diff --git a/packages/editor/src/store/test/reducer.js b/packages/editor/src/store/test/reducer.js index 9445b32a11e7c..9913daf213ec2 100644 --- a/packages/editor/src/store/test/reducer.js +++ b/packages/editor/src/store/test/reducer.js @@ -1580,9 +1580,28 @@ describe( 'state', () => { expect( state ).toEqual( { insertUsage: {}, + isPublishSidebarEnabled: true, } ); } ); + it( 'should disable the publish sidebar', () => { + const original = deepFreeze( preferences( undefined, { } ) ); + const state = preferences( original, { + type: 'DISABLE_PUBLISH_SIDEBAR', + } ); + + expect( state.isPublishSidebarEnabled ).toBe( false ); + } ); + + it( 'should enable the publish sidebar', () => { + const original = deepFreeze( preferences( { isPublishSidebarEnabled: false }, { } ) ); + const state = preferences( original, { + type: 'ENABLE_PUBLISH_SIDEBAR', + } ); + + expect( state.isPublishSidebarEnabled ).toBe( true ); + } ); + it( 'should record recently used blocks', () => { const state = preferences( deepFreeze( { insertUsage: {} } ), { type: 'INSERT_BLOCKS', diff --git a/packages/editor/src/store/test/selectors.js b/packages/editor/src/store/test/selectors.js index 60bde150d492a..9b88a450c763c 100644 --- a/packages/editor/src/store/test/selectors.js +++ b/packages/editor/src/store/test/selectors.js @@ -13,6 +13,7 @@ import { moment } from '@wordpress/date'; * Internal dependencies */ import * as selectors from '../selectors'; +import { PREFERENCES_DEFAULTS } from '../defaults'; const { canUserUseUnfilteredHTML, @@ -82,6 +83,7 @@ const { getReusableBlocks, getStateBeforeOptimisticTransaction, isPublishingPost, + isPublishSidebarEnabled, canInsertBlockType, getInserterItems, isValidTemplate, @@ -3387,6 +3389,33 @@ describe( 'selectors', () => { } ); } ); + describe( 'isPublishSidebarEnabled', () => { + it( 'should return the value on state if it is thruthy', () => { + const state = { + preferences: { + isPublishSidebarEnabled: true, + }, + }; + expect( isPublishSidebarEnabled( state ) ).toBe( state.preferences.isPublishSidebarEnabled ); + } ); + + it( 'should return the value on state if it is falsy', () => { + const state = { + preferences: { + isPublishSidebarEnabled: false, + }, + }; + expect( isPublishSidebarEnabled( state ) ).toBe( state.preferences.isPublishSidebarEnabled ); + } ); + + it( 'should return the default value if there is no isPublishSidebarEnabled key on state', () => { + const state = { + preferences: { }, + }; + expect( isPublishSidebarEnabled( state ) ).toBe( PREFERENCES_DEFAULTS.isPublishSidebarEnabled ); + } ); + } ); + describe( 'isFetchingReusableBlock', () => { it( 'should return false when the block is not being fetched', () => { const state = { diff --git a/test/e2e/specs/publishing.test.js b/test/e2e/specs/publishing.test.js index 0eed46e1e4c1b..31265b35ece7a 100644 --- a/test/e2e/specs/publishing.test.js +++ b/test/e2e/specs/publishing.test.js @@ -4,48 +4,76 @@ import { newPost, publishPost, + publishPostWithoutPrePublishChecks, + enablePrePublishChecks, + disablePrePublishChecks, + arePrePublishChecksEnabled, } from '../support/utils'; describe( 'Publishing', () => { - describe( 'a post', () => { - beforeEach( async () => { - await newPost(); - } ); + [ 'post', 'page' ].forEach( ( postType ) => { + let werePrePublishChecksEnabled; + describe( `a ${ postType }`, () => { + beforeEach( async () => { + await newPost( postType ); + werePrePublishChecksEnabled = await arePrePublishChecksEnabled(); + if ( ! werePrePublishChecksEnabled ) { + await enablePrePublishChecks(); + } + } ); + + afterEach( async () => { + if ( ! werePrePublishChecksEnabled ) { + await disablePrePublishChecks(); + } + } ); - it( 'should publish a post and close the panel once we start editing again', async () => { - await page.type( '.editor-post-title__input', 'E2E Test Post' ); + it( `should publish the ${ postType } and close the panel once we start editing again.`, async () => { + await page.type( '.editor-post-title__input', 'E2E Test Post' ); - await publishPost(); + await publishPost(); - // The post-publishing panel is visible. - expect( await page.$( '.editor-post-publish-panel' ) ).not.toBeNull(); + // The post-publishing panel is visible. + expect( await page.$( '.editor-post-publish-panel' ) ).not.toBeNull(); - // Start editing again. - await page.type( '.editor-post-title__input', ' (Updated)' ); + // Start editing again. + await page.type( '.editor-post-title__input', ' (Updated)' ); - // The post-publishing panel is not visible anymore. - expect( await page.$( '.editor-post-publish-panel' ) ).toBeNull(); + // The post-publishing panel is not visible anymore. + expect( await page.$( '.editor-post-publish-panel' ) ).toBeNull(); + } ); } ); } ); - describe( 'a page', () => { - beforeEach( async () => { - await newPost( 'page' ); - } ); + [ 'post', 'page' ].forEach( ( postType ) => { + let werePrePublishChecksEnabled; + describe( `a ${ postType } with pre-publish checks disabled`, () => { + beforeEach( async () => { + await newPost( postType ); + werePrePublishChecksEnabled = await arePrePublishChecksEnabled(); + if ( werePrePublishChecksEnabled ) { + await disablePrePublishChecks(); + } + } ); - it( 'should publish a page and close the panel once we start editing again', async () => { - await page.type( '.editor-post-title__input', 'E2E Test Page' ); + afterEach( async () => { + if ( werePrePublishChecksEnabled ) { + await enablePrePublishChecks(); + } + } ); - await publishPost(); + it( `should publish the ${ postType } without opening the post-publish sidebar.`, async () => { + await page.type( '.editor-post-title__input', 'E2E Test Post' ); - // The post-publishing panel is visible. - expect( await page.$( '.editor-post-publish-panel' ) ).not.toBeNull(); + // The "Publish" button should be shown instead of the "Publish..." toggle + expect( await page.$( '.editor-post-publish-panel__toggle' ) ).toBeNull(); + expect( await page.$( '.editor-post-publish-button' ) ).not.toBeNull(); - // Start editing the page again. - await page.type( '.editor-post-title__input', ' (Updated)' ); + await publishPostWithoutPrePublishChecks(); - // The post-publishing panel is not visible anymore. - expect( await page.$( '.editor-post-publish-panel' ) ).toBeNull(); + // The post-publishing panel should have been not shown. + expect( await page.$( '.editor-post-publish-panel' ) ).toBeNull(); + } ); } ); } ); } ); diff --git a/test/e2e/support/utils.js b/test/e2e/support/utils.js index ea162ed86df7f..15a4d7bcf4768 100644 --- a/test/e2e/support/utils.js +++ b/test/e2e/support/utils.js @@ -136,6 +136,28 @@ export async function newPost( { postType, enableTips = false } = {} ) { } } +export async function togglePrePublishChecks( ) { + await page.click( '.edit-post-more-menu' ); + await page.waitForSelector( '.components-popover__content' ); + await page.click( '.edit-post__pre-publish-checks' ); +} + +export async function arePrePublishChecksEnabled( ) { + return page.evaluate( () => window.wp.data.select( 'core/editor' ).isPublishSidebarEnabled() ); +} + +export async function enablePrePublishChecks( ) { + if ( ! await arePrePublishChecksEnabled( ) ) { + await togglePrePublishChecks(); + } +} + +export async function disablePrePublishChecks( ) { + if ( await arePrePublishChecksEnabled( ) ) { + await togglePrePublishChecks(); + } +} + export async function setViewport( type ) { const allowedDimensions = { large: { width: 960, height: 700 }, @@ -294,6 +316,17 @@ export async function publishPost() { return page.waitForSelector( '.components-notice.is-success' ); } +/** + * Publishes the post without the pre-publish checks, + * resolving once the request is complete (once a notice is displayed). + * + * @return {Promise} Promise resolving when publish is complete. + */ +export async function publishPostWithoutPrePublishChecks() { + await page.click( '.editor-post-publish-button' ); + return page.waitForSelector( '.components-notice.is-success' ); +} + /** * Saves the post as a draft, resolving once the request is complete (once the * "Saved" indicator is displayed).