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 9 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
3 changes: 2 additions & 1 deletion assets/css/src/amp-block-editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
}

/* AMP preview button wrapper */
.wp-core-ui #amp-wrapper-post-preview {
.wp-core-ui .amp-wrapper-post-preview {
margin-left: -6px;
margin-right: 6px;
}

/* AMP preview button */
.wp-core-ui .amp-editor-post-preview {
height: 34px;
margin-right: 0 !important;
padding: 6px 12px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
/**
* External dependencies
*/
import { get } from 'lodash';
import PropTypes from 'prop-types';

/**
* WordPress dependencies
*/
import { Component, 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 { Component, createRef, renderToString } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';

/**
* Internal dependencies
*/
import ampBlackIcon from '../../../images/amp-black-icon.svg';
import ampFilledIcon from '../../../images/amp-icon.svg';
import { isAMPEnabled } from '../helpers';
import { POST_PREVIEW_CLASS } from '../constants';
import ampFilledIcon from '../../../images/amp-icon.svg';
import ampBlackIcon from '../../../images/amp-black-icon.svg';

/**
* Writes the message and graphic in the new preview window that was opened.
Expand Down Expand Up @@ -105,22 +103,19 @@ 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.
*
* @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 );
}

/**
Expand All @@ -137,25 +132,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 +145,9 @@ class AMPPreview extends Component {

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

Expand Down Expand Up @@ -234,31 +213,29 @@ class AMPPreview extends Component {
// just link to the post's URL.
const href = previewLink || currentPostLink;

return (
isEnabled && ! errorMessages.length && ! isStandardMode && (
<Button
className="amp-editor-post-preview"
href={ href }
title={ __( 'Preview AMP', 'amp' ) }
isSecondary
disabled={ ! isSaveable }
onClick={ this.openPreviewWindow }
ref={ this.buttonRef }
>
{ ampFilledIcon( { viewBox: '0 0 62 62', width: 18, height: 18 } ) }
<span className="screen-reader-text">
{
/* translators: accessibility text */
__( '(opens in a new tab)', 'amp' )
}
</span>
</Button>
)
return isEnabled && ! errorMessages.length && ! isStandardMode && (
<Button
className="amp-editor-post-preview"
href={ href }
title={ __( 'Preview AMP', 'amp' ) }
isSecondary
disabled={ ! isSaveable }
onClick={ this.openPreviewWindow }
ref={ this.buttonRef }
>
{ ampFilledIcon( { viewBox: '0 0 62 62', width: 18, height: 18 } ) }
<span className="screen-reader-text">
{
/* translators: accessibility text */
__( '(opens in a new tab)', 'amp' )
}
</span>
</Button>
);
}
}

AMPPreview.propTypes = {
AmpPreviewButton.propTypes = {
autosave: PropTypes.func.isRequired,
currentPostLink: PropTypes.string.isRequired,
postId: PropTypes.number.isRequired,
Expand Down Expand Up @@ -291,6 +268,7 @@ export default compose( [

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

const initialPreviewLink = getEditedPostPreviewLink();
const previewLink = initialPreviewLink ? addQueryArgs( initialPreviewLink, queryArgs ) : undefined;

Expand All @@ -310,4 +288,4 @@ export default compose( [
autosave: dispatch( 'core/editor' ).autosave,
savePost: dispatch( 'core/editor' ).savePost,
} ) ),
] )( AMPPreview );
] )( AmpPreviewButton );
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 );
} );
90 changes: 90 additions & 0 deletions assets/src/block-editor/plugins/wrapped-amp-preview-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* External dependencies
*/
import { get } from 'lodash';

/**
* WordPress dependencies
*/
import { Component, createPortal } from '@wordpress/element';
import { withSelect } from '@wordpress/data';
import { ifCondition, compose, pure } from '@wordpress/compose';

/**
* Internal dependencies
*/
import { POST_PREVIEW_CLASS } from '../constants';
import AmpPreviewButton from '../components/amp-preview-button';

/**
* A wrapper for the AMP preview button that renders it immediately after the 'Post' preview button, when present.
*/
class WrappedAmpPreviewButton extends Component {
/**
* Constructs the class.
*
* @param {*} args Constructor arguments.
*/
constructor( ...args ) {
super( ...args );

this.root = document.createElement( 'div' );
this.root.className = 'amp-wrapper-post-preview';

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 );
}

/**
* Renders the component.
*/
render() {
if ( ! this.postPreviewButton ) {
return null;
}

return createPortal( <AmpPreviewButton />, this.root );
}
}

export const name = 'amp-preview-button-wrapper';

export const render = pure(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've opted to make this a pure component as this does not warrant unnecessary re-rendering, unless I'm missing something. @johnwatkins0 thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a good idea. I've seen side effects from having createPortal inside components that re-render often. E.g., not applicable in this case, but if there were any CSS animations on any of the element inside the portal, the animation might restart on every rerender.

compose( [
withSelect( ( select ) => {
const { getPostType } = select( 'core' );
const { getEditedPostAttribute } = select( 'core/editor' );

const postType = getPostType( getEditedPostAttribute( 'type' ) );

return {
isViewable: get( postType, [ 'viewable' ], false ),
};
} ),
// This HOC creator renders the component only when the condition is true. At that point the 'Post' preview
// button should have already been rendered (since it also relies on the same condition for rendering).
ifCondition( ( { isViewable } ) => isViewable ),
] )( WrappedAmpPreviewButton ),
);
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
Loading