Skip to content
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

Fix the AMP Preview button not being shown in the block editor #5441

Merged
merged 15 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion assets/src/block-editor/components/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { default as AMPPreview } from './amp-preview';
export { default as MediaPlaceholder } from './media-placeholder';
export { default as LayoutControls } from './layout-controls';
export { default as withMediaLibraryNotice } from './with-media-library-notice';
30 changes: 2 additions & 28 deletions assets/src/block-editor/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ import { ReactElement } from 'react';
* WordPress dependencies
*/
import { __, _x } from '@wordpress/i18n';
import { cloneElement, render } from '@wordpress/element';
import { cloneElement } from '@wordpress/element';
import { TextControl, SelectControl, ToggleControl, Notice, PanelBody, FontSizePicker } from '@wordpress/components';
import { InspectorControls } from '@wordpress/block-editor';
import { select } from '@wordpress/data';

/**
* Internal dependencies
*/
import { TEXT_BLOCKS, MEDIA_BLOCKS, DEFAULT_HEIGHT, DEFAULT_WIDTH, POST_PREVIEW_CLASS } from '../constants';
import { TEXT_BLOCKS, MEDIA_BLOCKS, DEFAULT_HEIGHT, DEFAULT_WIDTH } from '../constants';
import { MIN_FONT_SIZE, MAX_FONT_SIZE } from '../../common/constants';

const ampLayoutOptions = [
Expand Down Expand Up @@ -719,29 +719,3 @@ export const isAMPEnabled = () => {
const { getEditedPostAttribute } = select( 'core/editor' );
return getEditedPostAttribute( 'amp_enabled' ) || false;
};

/**
* Renders the 'Preview AMP' button in the DOM right after the non-AMP 'Preview' button.
*
* @param {Object} PreviewComponent The 'Preview AMP' component to render into the DOM.
*/
export const renderPreviewButton = ( PreviewComponent ) => {
const postPreviewButton = document.querySelector( `.${ POST_PREVIEW_CLASS }` );
const ampPreviewButtonWrapperId = 'amp-wrapper-post-preview';

// Exit if the non-AMP 'Preview' button doesn't exist.
if ( ! postPreviewButton || ! postPreviewButton.nextSibling ) {
return;
}

const buttonWrapper = document.createElement( 'div' );
buttonWrapper.id = ampPreviewButtonWrapperId;

render(
<PreviewComponent />,
buttonWrapper,
);

// Insert the new AMP preview button after the non-AMP 'Preview' button.
postPreviewButton.parentNode.insertBefore( buttonWrapper, postPreviewButton.nextSibling );
};
33 changes: 0 additions & 33 deletions assets/src/block-editor/helpers/test/renderPreviewButton.js

This file was deleted.

10 changes: 2 additions & 8 deletions assets/src/block-editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ import { addFilter } from '@wordpress/hooks';
import { registerPlugin } from '@wordpress/plugins';
import { registerBlockType } from '@wordpress/blocks';
import { select } from '@wordpress/data';
import domReady from '@wordpress/dom-ready';

/**
* Internal dependencies
*/
import { withFeaturedImageNotice } from '../common/components';
import { getMinimumFeaturedImageDimensions } from '../common/helpers';
import { withMediaLibraryNotice, AMPPreview } from './components';
import { addAMPAttributes, filterBlocksEdit, filterBlocksSave, renderPreviewButton } from './helpers';
import { withMediaLibraryNotice } from './components';
import { addAMPAttributes, filterBlocksEdit, filterBlocksSave } from './helpers';
import './store';

const {
Expand Down Expand Up @@ -60,8 +59,3 @@ blocks.keys().forEach( ( modulePath ) => {
registerBlockType( name, settings );
}
} );

// Render the 'Preview AMP' button, and move it to after the (non-AMP) 'Preview' button.
domReady( () => {
renderPreviewButton( AMPPreview );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import PropTypes from 'prop-types';
/**
* WordPress dependencies
*/
import { Component, createRef, renderToString } from '@wordpress/element';
import { Component, createPortal, createRef, renderToString } from '@wordpress/element';
import { Button, Icon } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { withSelect, withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
import { withDispatch, withSelect } from '@wordpress/data';
import { ifCondition, compose } from '@wordpress/compose';
import { addQueryArgs } from '@wordpress/url';

/**
Expand Down Expand Up @@ -105,22 +105,49 @@ function writeInterstitialMessage( targetDocument ) {
/**
* A 'Preview AMP' button, forked from the Core 'Preview' button: <PostPreviewButton>.
*
* Rendered into the DOM with renderPreviewButton() in helpers/index.js.
* This also moves the (non-AMP) 'Preview' button to before this, if it's not already there.
* Inserted into the DOM immediately after the 'Preview' button.
*
* @see https://github.com/WordPress/gutenberg/blob/95e769df1f82f6b0ef587d81af65dd2f48cd1c38/packages/editor/src/components/post-preview-button/index.js#L95-L200
*/
class AMPPreview extends Component {
class AmpPreviewButton extends Component {
/**
* Constructs the class.
*
* @param {*} args Constructor arguments.
*/
constructor( ...args ) {
super( ...args );
this.moveButton = this.moveButton.bind( this );
this.openPreviewWindow = this.openPreviewWindow.bind( this );

this.buttonRef = createRef();
this.openPreviewWindow = this.openPreviewWindow.bind( this );

this.root = document.createElement( 'div' );
this.root.id = 'amp-wrapper-post-preview';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be a class instead of an ID. While unlikely, it's technically possible for this component to be rendered multiple times on a page.


this.postPreviewButton = document.querySelector( `.${ POST_PREVIEW_CLASS }` );
}

/**
* Invoked immediately after a component is mounted (inserted into the tree).
*/
componentDidMount() {
if ( ! this.postPreviewButton ) {
return;
}

// Insert the AMP preview button immediately after the post preview button.
this.postPreviewButton.parentNode.insertBefore( this.root, this.postPreviewButton.nextSibling );
}

/**
* Invoked immediately before a component is unmounted and destroyed.
*/
componentWillUnmount() {
if ( ! this.postPreviewButton ) {
return;
}

this.postPreviewButton.parentNode.removeChild( this.root );
}

/**
Expand All @@ -137,25 +164,6 @@ class AMPPreview extends Component {
if ( previewLink && ! prevProps.previewLink ) {
this.setPreviewWindowLink( previewLink );
}

this.moveButton();
}

/**
* Moves the (non-AMP) 'Preview' button to before this 'Preview AMP' button, if it's not there already.
*/
moveButton() {
const buttonWrapper = get( this, [ 'buttonRef', 'current', 'parentNode' ], false );
if ( ! buttonWrapper ) {
return;
}

if ( ! buttonWrapper.previousSibling || ! buttonWrapper.previousSibling.classList.contains( POST_PREVIEW_CLASS ) ) {
const postPreviewButton = document.querySelector( `.${ POST_PREVIEW_CLASS }` );
if ( get( postPreviewButton, 'nextSibling' ) ) {
buttonWrapper.parentNode.insertBefore( buttonWrapper, postPreviewButton.nextSibling );
}
}
}

/**
Expand All @@ -169,6 +177,9 @@ class AMPPreview extends Component {

if ( previewWindow && ! previewWindow.closed ) {
previewWindow.location = url;
if ( this.buttonRef.current ) {
this.buttonRef.current.focus();
}
}
}

Expand Down Expand Up @@ -227,14 +238,18 @@ class AMPPreview extends Component {
* Renders the component.
*/
render() {
if ( ! this.postPreviewButton ) {
return null;
}

const { previewLink, currentPostLink, errorMessages, isEnabled, isSaveable, isStandardMode } = this.props;

// Link to the `?preview=true` URL if we have it, since this lets us see
// changes that were autosaved since the post was last published. Otherwise,
// just link to the post's URL.
const href = previewLink || currentPostLink;

return (
return createPortal(
isEnabled && ! errorMessages.length && ! isStandardMode && (
<Button
className="amp-editor-post-preview"
Expand All @@ -253,12 +268,13 @@ class AMPPreview extends Component {
}
</span>
</Button>
)
),
this.root,
);
}
}

AMPPreview.propTypes = {
AmpPreviewButton.propTypes = {
autosave: PropTypes.func.isRequired,
currentPostLink: PropTypes.string.isRequired,
postId: PropTypes.number.isRequired,
Expand All @@ -272,8 +288,12 @@ AMPPreview.propTypes = {
isStandardMode: PropTypes.bool,
};

export default compose( [
export const name = 'amp-preview-button';

export const render = compose( [
withSelect( ( select, { forcePreviewLink, forceIsAutosaveable } ) => {
const { getPostType } = select( 'core' );

const {
getCurrentPostId,
getCurrentPostAttribute,
Expand All @@ -291,15 +311,18 @@ export default compose( [

const queryArgs = {};
queryArgs[ getAmpSlug() ] = 1;

const initialPreviewLink = getEditedPostPreviewLink();
const previewLink = initialPreviewLink ? addQueryArgs( initialPreviewLink, queryArgs ) : undefined;
const postType = getPostType( getEditedPostAttribute( 'type' ) );

return {
postId: getCurrentPostId(),
currentPostLink: addQueryArgs( getCurrentPostAttribute( 'link' ), queryArgs ),
previewLink: forcePreviewLink !== undefined ? forcePreviewLink : previewLink,
isSaveable: isEditedPostSaveable(),
isAutosaveable: forceIsAutosaveable || isEditedPostAutosaveable(),
isViewable: get( postType, [ 'viewable' ], false ),
isDraft: [ 'draft', 'auto-draft' ].indexOf( getEditedPostAttribute( 'status' ) ) !== -1,
isEnabled: isAMPEnabled(),
errorMessages: getErrorMessages(),
Expand All @@ -310,4 +333,5 @@ export default compose( [
autosave: dispatch( 'core/editor' ).autosave,
savePost: dispatch( 'core/editor' ).savePost,
} ) ),
] )( AMPPreview );
ifCondition( ( { isViewable } ) => isViewable ),
] )( AmpPreviewButton );
2 changes: 1 addition & 1 deletion bin/local-env/install-wordpress.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ wp plugin activate amp --quiet

# Install & activate Gutenberg plugin.
echo -e $(status_message "Installing and activating Gutenberg plugin...")
wp plugin install gutenberg --activate --force --quiet --version=7.1.0
wp plugin install gutenberg --activate --force --quiet

# Set pretty permalinks.
echo -e $(status_message "Setting permalink structure...")
Expand Down
59 changes: 59 additions & 0 deletions tests/e2e/specs/block-editor/amp-preview-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* WordPress dependencies
*/
import { createNewPost, visitAdminPage } from '@wordpress/e2e-test-utils';

/**
* Internal dependencies
*/
import { cleanUpSettings } from '../../utils/onboarding-wizard-utils';
import { activatePlugin, deactivatePlugin } from '../../utils';

const postPreviewBtnSelector = '.components-button.editor-post-preview';
const ampPreviewBtnSelector = `${ postPreviewBtnSelector } + #amp-wrapper-post-preview > .amp-editor-post-preview`;

describe( 'AMP Preview button', () => {
it( 'is rendered on a new post', async () => {
await createNewPost();
await page.waitForSelector( postPreviewBtnSelector );

await expect( page ).toMatchElement( ampPreviewBtnSelector );
} );

it( 'is rendered when Gutenberg is disabled', async () => {
await deactivatePlugin( 'gutenberg' );

await createNewPost();
await page.waitForSelector( postPreviewBtnSelector );

await expect( page ).toMatchElement( ampPreviewBtnSelector );

await activatePlugin( 'gutenberg' );
} );

it( 'is rendered when a post has content', async () => {
await createNewPost( {
title: 'The Ballad of the Lost Preview Button',
content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Consectetur fugiat, impedit.',
} );
await page.waitForSelector( postPreviewBtnSelector );

await expect( page ).toMatchElement( ampPreviewBtnSelector );
} );

it( 'does not render the button when in Standard mode', async () => {
// Set theme support to Standard mode.
await visitAdminPage( 'admin.php', 'page=amp-options' );
await page.waitForSelector( '.settings-footer' );
await page.evaluate( async () => {
await wp.apiFetch( { path: '/amp/v1/options', method: 'POST', data: { theme_support: 'standard' } } );
} );

await createNewPost();
await page.waitForSelector( postPreviewBtnSelector );

await expect( page ).not.toMatchElement( ampPreviewBtnSelector );

await cleanUpSettings();
} );
} );
2 changes: 1 addition & 1 deletion tests/e2e/specs/block-editor/featured-image-notice.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const cropImageText = 'Crop Image';
describe( 'Featured Image Notice', () => {
beforeEach( async () => {
await createNewPost( { postType: 'post' } );
await clickButton( 'Document' );
await clickButton( 'Post' );
await clickButton( 'Featured image' );
await clickButton( 'Set featured image' );
} );
Expand Down
1 change: 0 additions & 1 deletion tests/php/test-class-amp-meta-box.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ public function test_enqueue_block_assets() {
'wp-components',
'wp-compose',
'wp-data',
'wp-dom-ready',
'wp-edit-post',
'wp-element',
'wp-hooks',
Expand Down