Skip to content

Commit

Permalink
Move Entities Saved States from Modal to Sidebar (#21522)
Browse files Browse the repository at this point in the history
* moved to sidebar, functional in post editor / hidden in site

* copied over some styles, working in site editor now

* added close button

* added icons and styles

* added selection feature

* refactored store goo

* refactored z-index

* refactored entities saved states

* refactored scss

* added test for actions

* added tests for reducer

* added selector test

* fixing lint

* fixing more lint

* cleanup bad auto-pretty

* last of cleanup?

* renamed select entity button

* added actions/reducers/tests to edit-post

* added store things to edit-site

* restructured to run on isOpen/closePanel props

* removed from editor package store

* created Actions component for post editor layout

* added save panel hidden button to post editor

* added hidden button to site editor

* added styles and class names

* fixed z-index name issue

* restored z-indexes to previous values, no need with non-overlapping panels

* refactor ActionsPanel, always mount entitites"

* removed unnecessary vars and imports

* make save component always mounted in edit site"

* added some comments

* fixed closePanel for selecting entity in small width

* removed entities actions/reducers from edit-site editor

* passed localstate down through props in edit-site

* removed entities goo from edit-site store

* added parentBlockId as dep to callback in entitiy selection

* converted entities to local state in edit-post layout

* set up edit-post save panel on local state props

* removed entities goo from edit-post store

* fixed z-index naming convention

* refactored actions panel

* fixed comment misspellings

* refactored entitiesSavedStates icon enum

* removed unnecessary comments

* added comment to redundant looking useCallback

* updated description titles in test to no longer say 'modal'

* added tests for panels/a11y buttons rendering

* minor name and descriptor changes

* added waitForSelector to failing post-visibility test

* changed waitFor to wait for button to be clickable

* changed 'Select entity' button to 'Select'

* Update packages/base-styles/_z-index.scss

Co-Authored-By: Enrique Piqueras <[email protected]>

* applying some suggestions

* Update packages/edit-post/src/components/layout/actions-panel.js

Co-Authored-By: Enrique Piqueras <[email protected]>

* added comment to setting callback

* updated callback goo

* fixed useCallback for dismissing panel

* updates prop name from closePanel to close

* updated how we clear the callback

* simplified logic on callback setting and open/close

* minor comment nit

Co-authored-by: Enrique Piqueras <[email protected]>
  • Loading branch information
Addison-Stavlo and epiqueras authored May 1, 2020
1 parent e110119 commit 4830c2c
Show file tree
Hide file tree
Showing 16 changed files with 541 additions and 135 deletions.
8 changes: 7 additions & 1 deletion packages/base-styles/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ $z-layers: (
".edit-post-layout .edit-post-post-publish-panel": 100001,
// For larger views, the wp-admin navbar dropdown should be at top of
// the Publish Post sidebar.
".edit-post-layout .edit-post-post-publish-panel-break-medium": 99998,
".edit-post-layout .edit-post-post-publish-panel {greater than small}": 99998,

".entities-saved-states__panel": 100001,
// For larger views, the wp-admin navbar dropdown should be on top of
// the multi-entity saving sidebar.
".entities-saved-states__panel {greater than small}": 99998,
".edit-site-editor__toggle-save-panel": 100000,

// Show sidebar in greater than small viewports above editor related elements
// but bellow #adminmenuback { z-index: 100 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ describe( 'Post visibility', () => {
// Enter a title for this post.
await page.type( '.editor-post-title__input', ' Changed' );

// Wait for the button to be clickable before attempting to click.
// This could cause errors when we try to click before changes are registered.
await page.waitForSelector(
'.editor-post-publish-button[aria-disabled="false"]'
);
await page.click( '.editor-post-publish-button' );

const currentStatus = await page.evaluate( () => {
Expand Down
107 changes: 87 additions & 20 deletions packages/e2e-tests/specs/experiments/multi-entity-saving.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import {
createNewPost,
insertBlock,
disablePrePublishChecks,
publishPostWithPrePublishChecksDisabled,
publishPost,
visitAdminPage,
} from '@wordpress/e2e-test-utils';
import { addQueryArgs } from '@wordpress/url';
Expand All @@ -20,31 +19,29 @@ import {
import { trashExistingPosts } from '../../config/setup-test-framework';

describe( 'Multi-entity save flow', () => {
// Selectors.
// Selectors - usable between Post/Site editors.
const checkedBoxSelector = '.components-checkbox-control__checked';
const checkboxInputSelector = '.components-checkbox-control__input';
const demoTemplateSelector = '//button[contains(., "front-page")]';
const draftSavedSelector = '.editor-post-saved-state.is-saved';
const entitiesSaveSelector = '.editor-entities-saved-states__save-button';
const multiSaveSelector =
'.editor-post-publish-button__button.has-changes-dot';
const savePostSelector = '.editor-post-publish-button__button';
const disabledSavePostSelector = `${ savePostSelector }[aria-disabled=true]`;
const enabledSavePostSelector = `${ savePostSelector }[aria-disabled=false]`;
const saveSiteSelector = '.edit-site-save-button__button';
const activeSaveSiteSelector = `${ saveSiteSelector }[aria-disabled=false]`;
const disabledSaveSiteSelector = `${ saveSiteSelector }[aria-disabled=true]`;
const templateDropdownSelector =
'.components-dropdown-menu__toggle[aria-label="Switch Template"]';
const templatePartSelector = '*[data-type="core/template-part"]';
const activatedTemplatePartSelector = `${ templatePartSelector } .block-editor-inner-blocks`;
const savePanelSelector = '.entities-saved-states__panel';
const closePanelButtonSelector = 'button[aria-label="Close panel"]';

// Reusable assertions across Post/Site editors.
const assertAllBoxesChecked = async () => {
const checkedBoxes = await page.$$( checkedBoxSelector );
const checkboxInputs = await page.$$( checkboxInputSelector );
expect( checkedBoxes.length - checkboxInputs.length ).toBe( 0 );
};
const assertExistance = async ( selector, shouldBePresent ) => {
const element = await page.$( selector );
if ( shouldBePresent ) {
expect( element ).not.toBeNull();
} else {
expect( element ).toBeNull();
}
};
// Setup & Teardown.
const requiredExperiments = [
'#gutenberg-full-site-editing',
Expand All @@ -60,6 +57,19 @@ describe( 'Multi-entity save flow', () => {
} );

describe( 'Post Editor', () => {
// Selectors - Post editor specific.
const draftSavedSelector = '.editor-post-saved-state.is-saved';
const multiSaveSelector =
'.editor-post-publish-button__button.has-changes-dot';
const savePostSelector = '.editor-post-publish-button__button';
const disabledSavePostSelector = `${ savePostSelector }[aria-disabled=true]`;
const enabledSavePostSelector = `${ savePostSelector }[aria-disabled=false]`;
const publishA11ySelector =
'.edit-post-layout__toggle-publish-panel-button';
const saveA11ySelector =
'.edit-post-layout__toggle-entities-saved-states-panel-button';
const publishPanelSelector = '.editor-post-publish-panel';

// Reusable assertions inside Post editor.
const assertMultiSaveEnabled = async () => {
const multiSaveButton = await page.waitForSelector(
Expand All @@ -75,7 +85,6 @@ describe( 'Multi-entity save flow', () => {
describe( 'Pre-Publish state', () => {
it( 'Should not trigger multi-entity save button with only post edited', async () => {
await createNewPost();
await disablePrePublishChecks();
// Edit the page some.
await page.click( '.editor-post-title' );
await page.keyboard.type( 'Test Post...' );
Expand All @@ -84,6 +93,13 @@ describe( 'Multi-entity save flow', () => {
await assertMultiSaveDisabled();
} );

it( 'Should only have publish panel a11y button active with only post edited', async () => {
await assertExistance( publishA11ySelector, true );
await assertExistance( saveA11ySelector, false );
await assertExistance( publishPanelSelector, false );
await assertExistance( savePanelSelector, false );
} );

it( 'Should trigger multi-entity save button once template part edited', async () => {
// Create new template part.
await insertBlock( 'Template Part' );
Expand All @@ -101,14 +117,39 @@ describe( 'Multi-entity save flow', () => {
await assertMultiSaveEnabled();
} );

it( 'Clicking should open modal with boxes checked by default', async () => {
it( 'Should only have save panel a11y button active after child entities edited', async () => {
await assertExistance( publishA11ySelector, false );
await assertExistance( saveA11ySelector, true );
await assertExistance( publishPanelSelector, false );
await assertExistance( savePanelSelector, false );
} );

it( 'Clicking should open panel with boxes checked by default', async () => {
await page.click( savePostSelector );
await page.waitForSelector( savePanelSelector );
await assertAllBoxesChecked();
} );

it( 'Saving should result in items being saved', async () => {
it( 'Should not show other panels (or their a11y buttons) while save panel opened', async () => {
await assertExistance( publishA11ySelector, false );
await assertExistance( saveA11ySelector, false );
await assertExistance( publishPanelSelector, false );
} );

it( 'Publish panel should open after saving, no other panels (or their a11y buttons) should be present', async () => {
// Save entities and wait for publish panel.
await page.click( entitiesSaveSelector );
await page.waitForSelector( publishPanelSelector );

await assertExistance( publishA11ySelector, false );
await assertExistance( saveA11ySelector, false );
await assertExistance( savePanelSelector, false );

// Close publish panel.
await page.click( closePanelButtonSelector );
} );

it( 'Saving should result in items being saved', async () => {
// Verify post is saved.
const draftSaved = await page.waitForSelector(
draftSavedSelector
Expand All @@ -122,13 +163,17 @@ describe( 'Multi-entity save flow', () => {

describe( 'Published state', () => {
it( 'Update button disabled after publish', async () => {
await publishPostWithPrePublishChecksDisabled();
await publishPost();
const disabledSaveButton = await page.$(
disabledSavePostSelector
);
expect( disabledSaveButton ).not.toBeNull();
} );

it( 'should not have save a11y button when no changes', async () => {
await assertExistance( saveA11ySelector, false );
} );

it( 'Update button enabled after editing post', async () => {
await page.click( '.editor-post-title' );
await page.keyboard.type( '...more title!' );
Expand All @@ -149,10 +194,23 @@ describe( 'Multi-entity save flow', () => {
await page.keyboard.press( 'Enter' );
await assertMultiSaveEnabled();
} );

it( 'save a11y button enables after editing template part', async () => {
await assertExistance( saveA11ySelector, true );
} );
} );
} );

describe( 'Site Editor', () => {
// Selectors - Site editor specific.
const demoTemplateSelector = '//button[contains(., "front-page")]';
const saveSiteSelector = '.edit-site-save-button__button';
const activeSaveSiteSelector = `${ saveSiteSelector }[aria-disabled=false]`;
const disabledSaveSiteSelector = `${ saveSiteSelector }[aria-disabled=true]`;
const templateDropdownSelector =
'.components-dropdown-menu__toggle[aria-label="Switch Template"]';
const saveA11ySelector = '.edit-site-editor__toggle-save-panel-button';

it( 'Should be enabled after edits', async () => {
// Navigate to site editor.
const query = addQueryArgs( '', {
Expand All @@ -177,11 +235,20 @@ describe( 'Multi-entity save flow', () => {
expect( enabledButton ).not.toBeNull();
} );

it( 'Clicking button should open modal with boxes checked', async () => {
it( 'save a11y button should be present', async () => {
await assertExistance( saveA11ySelector, true );
} );

it( 'Clicking button should open panel with boxes checked', async () => {
await page.click( activeSaveSiteSelector );
await page.waitForSelector( savePanelSelector );
await assertAllBoxesChecked();
} );

it( 'save a11y button should not be present with save panel open', async () => {
await assertExistance( saveA11ySelector, false );
} );

it( 'Saving should result in items being saved', async () => {
await page.click( entitiesSaveSelector );
const disabledButton = await page.waitForSelector(
Expand Down
9 changes: 8 additions & 1 deletion packages/edit-post/src/components/header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import MoreMenu from './more-menu';
import PostPublishButtonOrToggle from './post-publish-button-or-toggle';
import { default as DevicePreview } from '../device-preview';

function Header( { onToggleInserter, isInserterOpen } ) {
function Header( {
onToggleInserter,
isInserterOpen,
setEntitiesSavedStatesCallback,
} ) {
const {
shortcut,
hasActiveMetaboxes,
Expand Down Expand Up @@ -89,6 +93,9 @@ function Header( { onToggleInserter, isInserterOpen } ) {
<PostPublishButtonOrToggle
forceIsDirty={ hasActiveMetaboxes }
forceIsSaving={ isSaving }
setEntitiesSavedStatesCallback={
setEntitiesSavedStatesCallback
}
/>
<Button
icon={ cog }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function PostPublishButtonOrToggle( {
isPublishSidebarOpened,
isScheduled,
togglePublishSidebar,
setEntitiesSavedStatesCallback,
} ) {
const IS_TOGGLE = 'toggle';
const IS_BUTTON = 'button';
Expand Down Expand Up @@ -70,6 +71,7 @@ export function PostPublishButtonOrToggle( {
isOpen={ isPublishSidebarOpened }
isToggle={ component === IS_TOGGLE }
onToggle={ togglePublishSidebar }
setEntitiesSavedStatesCallback={ setEntitiesSavedStatesCallback }
/>
);
}
Expand Down
98 changes: 98 additions & 0 deletions packages/edit-post/src/components/layout/actions-panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* WordPress dependencies
*/
import { EntitiesSavedStates, PostPublishPanel } from '@wordpress/editor';
import { useSelect, useDispatch } from '@wordpress/data';
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useCallback } from '@wordpress/element';
/**
* Internal dependencies
*/
import PluginPostPublishPanel from '../sidebar/plugin-post-publish-panel';
import PluginPrePublishPanel from '../sidebar/plugin-pre-publish-panel';

export default function ActionsPanel( {
setEntitiesSavedStatesCallback,
closeEntitiesSavedStates,
isEntitiesSavedStatesOpen,
} ) {
const { closePublishSidebar, togglePublishSidebar } = useDispatch(
'core/edit-post'
);
const {
publishSidebarOpened,
hasActiveMetaboxes,
isSavingMetaBoxes,
hasNonPostEntityChanges,
} = useSelect( ( select ) => {
return {
publishSidebarOpened: select(
'core/edit-post'
).isPublishSidebarOpened(),
hasActiveMetaboxes: select( 'core/edit-post' ).hasMetaBoxes(),
isSavingMetaBoxes: select( 'core/edit-post' ).isSavingMetaBoxes(),
hasNonPostEntityChanges: select(
'core/editor'
).hasNonPostEntityChanges(),
};
}, [] );

const openEntitiesSavedStates = useCallback(
() => setEntitiesSavedStatesCallback( true ),
[]
);

// It is ok for these components to be unmounted when not in visual use.
// We don't want more than one present at a time, decide which to render.
let unmountableContent;
if ( publishSidebarOpened ) {
unmountableContent = (
<PostPublishPanel
onClose={ closePublishSidebar }
forceIsDirty={ hasActiveMetaboxes }
forceIsSaving={ isSavingMetaBoxes }
PrePublishExtension={ PluginPrePublishPanel.Slot }
PostPublishExtension={ PluginPostPublishPanel.Slot }
/>
);
} else if ( hasNonPostEntityChanges ) {
unmountableContent = (
<div className="edit-post-layout__toggle-entities-saved-states-panel">
<Button
isSecondary
className="edit-post-layout__toggle-entities-saved-states-panel-button"
onClick={ openEntitiesSavedStates }
aria-expanded={ false }
>
{ __( 'Open save panel' ) }
</Button>
</div>
);
} else {
unmountableContent = (
<div className="edit-post-layout__toggle-publish-panel">
<Button
isSecondary
className="edit-post-layout__toggle-publish-panel-button"
onClick={ togglePublishSidebar }
aria-expanded={ false }
>
{ __( 'Open publish panel' ) }
</Button>
</div>
);
}

// Since EntitiesSavedStates controls its own panel, we can keep it
// always mounted to retain its own component state (such as checkboxes).
return (
<>
<EntitiesSavedStates
isOpen={ isEntitiesSavedStatesOpen }
close={ closeEntitiesSavedStates }
/>
{ ! isEntitiesSavedStatesOpen && unmountableContent }
</>
);
}
Loading

0 comments on commit 4830c2c

Please sign in to comment.