diff --git a/packages/block-editor/src/components/preview-options/README.md b/packages/block-editor/src/components/preview-options/README.md deleted file mode 100644 index 80182f18d243d..0000000000000 --- a/packages/block-editor/src/components/preview-options/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# Preview Options - -The `PreviewOptions` component displays the list of different preview options available in the editor. - -It returns a [`DropdownMenu`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/components/src/dropdown-menu) component with these different options. The options currently available in the editor are Desktop, Mobile, Tablet and "Preview in new tab". - -![Preview options dropdown menu](https://make.wordpress.org/core/files/2020/09/preview-options-dropdown-menu.png) - -## Table of contents - -1. [Development guidelines](#development-guidelines) -2. [Related components](#related-components) - -## Development guidelines - -### Usage - -Renders the previews options of the editor in a dropdown menu. - -```jsx -import { Icon, MenuGroup } from '@wordpress/components'; -import { PostPreviewButton } from '@wordpress/editor'; -import { __experimentalPreviewOptions as PreviewOptions } from '@wordpress/block-editor'; - -const MyPreviewOptions = () => ( - { ( { onClose } ) => ( - -
- - { __( 'Preview in new tab' ) } - - - } - onPreview={ onClose } - /> -
-
- ) } -
-); -``` - -### Props - -#### className - -The CSS classes added to the component. - -- Type: `String` -- Required: no - -#### isEnabled - -Wheter or not the preview options are enabled for the current post. -And example of when the preview options are not enabled is when the current post is not savable. - -- Type: `boolean` -- Required: no -- Default: true - -#### deviceType - -The device type in the preview options. It can be either Desktop or Tablet or Mobile among others. - -- Type: `String` -- Required: yes - -#### setDeviceType - -Used to set the device type that will be used to display the preview inside the editor. - -- Type: `func` -- Required: yes - -#### children - -A function that returns nodes to be rendered within the dropdown. - -- Type: `Function` -- Required: No - -## Related components - -Block Editor components are components that can be used to compose the UI of your block editor. Thus, they can only be used under a [`BlockEditorProvider`](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/provider/README.md) in the components tree. diff --git a/packages/block-editor/src/components/preview-options/index.js b/packages/block-editor/src/components/preview-options/index.js index 91018cc980bb2..8f540c35f6455 100644 --- a/packages/block-editor/src/components/preview-options/index.js +++ b/packages/block-editor/src/components/preview-options/index.js @@ -1,92 +1,11 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ -import { useViewportMatch } from '@wordpress/compose'; -import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { check, desktop, mobile, tablet } from '@wordpress/icons'; - -export default function PreviewOptions( { - children, - viewLabel, - className, - isEnabled = true, - deviceType, - setDeviceType, - label, - showIconLabels, -} ) { - const isMobile = useViewportMatch( 'medium', '<' ); - if ( isMobile ) return null; - - const popoverProps = { - className: classnames( - className, - 'block-editor-post-preview__dropdown-content' - ), - placement: 'bottom-end', - }; - const toggleProps = { - className: 'block-editor-post-preview__button-toggle', - disabled: ! isEnabled, - __experimentalIsFocusable: ! isEnabled, - children: viewLabel, - size: 'compact', - showTooltip: ! showIconLabels, - }; - const menuProps = { - 'aria-label': __( 'View options' ), - }; - - const deviceIcons = { - mobile, - tablet, - desktop, - }; +import deprecated from '@wordpress/deprecated'; - return ( - - { ( renderProps ) => ( - <> - - setDeviceType( 'Desktop' ) } - icon={ deviceType === 'Desktop' && check } - > - { __( 'Desktop' ) } - - setDeviceType( 'Tablet' ) } - icon={ deviceType === 'Tablet' && check } - > - { __( 'Tablet' ) } - - setDeviceType( 'Mobile' ) } - icon={ deviceType === 'Mobile' && check } - > - { __( 'Mobile' ) } - - - { children?.( renderProps ) } - - ) } - - ); +export default function PreviewOptions() { + deprecated( 'wp.blockEditor.PreviewOptions', { + version: '6.5', + } ); + return null; } diff --git a/packages/block-editor/src/components/preview-options/style.scss b/packages/block-editor/src/components/preview-options/style.scss deleted file mode 100644 index fb79926ba1dee..0000000000000 --- a/packages/block-editor/src/components/preview-options/style.scss +++ /dev/null @@ -1,64 +0,0 @@ -.block-editor-post-preview__dropdown { - padding: 0; -} - -.block-editor-post-preview__button-resize.block-editor-post-preview__button-resize { - padding-left: $button-size-small + $grid-unit-10 + $grid-unit-10; - - &.has-icon { - padding-left: $grid-unit-10; - } -} - -.block-editor-post-preview__dropdown-content { - &.edit-post-post-preview-dropdown { - .components-menu-group { - &:first-child { - padding-bottom: $grid-unit-10; - } - &:last-child { - margin-bottom: 0; - } - } - } - - .components-menu-group + .components-menu-group { - padding: $grid-unit-10; - } -} - -.edit-post-header__settings, -.edit-site-header-edit-mode__actions { - @include break-small () { - .editor-post-preview { - display: none; - } - } -} - -// Reduced UI. -.edit-post-header.has-reduced-ui { - @include break-small() { - // Apply transition to first two buttons. - .edit-post-header__settings .editor-post-save-draft, - .edit-post-header__settings .editor-post-saved-state, - .edit-post-header__settings .block-editor-post-preview__button-toggle { - transition: opacity 0.1s linear; - @include reduce-motion("transition"); - } - - // Zero out opacity unless hovered. - &:not(:hover) { - .edit-post-header__settings .editor-post-save-draft, - .edit-post-header__settings .editor-post-saved-state, - .edit-post-header__settings .block-editor-post-preview__button-toggle { - opacity: 0; - } - - // ... or opened. - .edit-post-header__settings .block-editor-post-preview__button-toggle.is-opened { - opacity: 1; - } - } - } -} diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 16de2dfdb7114..80489479724ff 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -59,7 +59,6 @@ @import "./components/block-toolbar/style.scss"; @import "./components/inserter/style.scss"; -@import "./components/preview-options/style.scss"; @import "./components/spacing-sizes-control/style.scss"; @include wordpress-admin-schemes(); diff --git a/packages/e2e-test-utils-playwright/src/editor/preview.ts b/packages/e2e-test-utils-playwright/src/editor/preview.ts index 97d3ef1d1d660..c697ca714fe96 100644 --- a/packages/e2e-test-utils-playwright/src/editor/preview.ts +++ b/packages/e2e-test-utils-playwright/src/editor/preview.ts @@ -19,9 +19,7 @@ export async function openPreviewPage( this: Editor ): Promise< Page > { const editorTopBar = this.page.locator( 'role=region[name="Editor top bar"i]' ); - const previewButton = editorTopBar.locator( - 'role=button[name="Preview"i]' - ); + const previewButton = editorTopBar.locator( 'role=button[name="View"i]' ); await previewButton.click(); diff --git a/packages/e2e-test-utils/src/preview.js b/packages/e2e-test-utils/src/preview.js index 1d96eda176675..24c5dc69dd0e2 100644 --- a/packages/e2e-test-utils/src/preview.js +++ b/packages/e2e-test-utils/src/preview.js @@ -10,13 +10,13 @@ export async function openPreviewPage( editorPage = page ) { let openTabs = await browser.pages(); const expectedTabsCount = openTabs.length + 1; await page.waitForSelector( - '.block-editor-post-preview__button-toggle:not([disabled])' + '.editor-preview-dropdown__toggle:not([disabled])' ); - await editorPage.click( '.block-editor-post-preview__button-toggle' ); + await editorPage.click( '.editor-preview-dropdown__toggle' ); await editorPage.waitForSelector( - '.edit-post-header-preview__button-external' + '.editor-preview-dropdown__button-external' ); - await editorPage.click( '.edit-post-header-preview__button-external' ); + await editorPage.click( '.editor-preview-dropdown__button-external' ); // Wait for the new tab to open. while ( openTabs.length < expectedTabsCount ) { diff --git a/packages/edit-post/src/components/device-preview/index.js b/packages/edit-post/src/components/device-preview/index.js deleted file mode 100644 index 9fc95b943609d..0000000000000 --- a/packages/edit-post/src/components/device-preview/index.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * WordPress dependencies - */ -import { Icon, MenuGroup } from '@wordpress/components'; -import { PostPreviewButton, store as editorStore } from '@wordpress/editor'; -import { external } from '@wordpress/icons'; -import { __ } from '@wordpress/i18n'; -import { __experimentalPreviewOptions as PreviewOptions } from '@wordpress/block-editor'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { store as coreStore } from '@wordpress/core-data'; - -/** - * Internal dependencies - */ -import { store as editPostStore } from '../../store'; - -export default function DevicePreview() { - const { - hasActiveMetaboxes, - isPostSaveable, - isViewable, - deviceType, - showIconLabels, - } = useSelect( ( select ) => { - const { getEditedPostAttribute } = select( editorStore ); - const { getPostType } = select( coreStore ); - const postType = getPostType( getEditedPostAttribute( 'type' ) ); - - return { - hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(), - isPostSaveable: select( editorStore ).isEditedPostSaveable(), - isViewable: postType?.viewable ?? false, - deviceType: select( editorStore ).getDeviceType(), - showIconLabels: - select( editPostStore ).isFeatureActive( 'showIconLabels' ), - }; - }, [] ); - const { setDeviceType } = useDispatch( editorStore ); - - return ( - - { ( { onClose } ) => - isViewable && ( - -
- - { __( 'Preview in new tab' ) } - - - } - onPreview={ onClose } - /> -
-
- ) - } -
- ); -} diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index b92c6c44fe49f..c86b24b4b7ccf 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -15,6 +15,7 @@ import { PostPreviewButton, store as editorStore, DocumentBar, + privateApis as editorPrivateApis, } from '@wordpress/editor'; import { useEffect, useRef, useState } from '@wordpress/element'; import { useSelect } from '@wordpress/data'; @@ -36,10 +37,12 @@ import FullscreenModeClose from './fullscreen-mode-close'; import HeaderToolbar from './header-toolbar'; import MoreMenu from './more-menu'; import PostPublishButtonOrToggle from './post-publish-button-or-toggle'; -import { default as DevicePreview } from '../device-preview'; import ViewLink from '../view-link'; import MainDashboardButton from './main-dashboard-button'; import { store as editPostStore } from '../../store'; +import { unlock } from '../../lock-unlock'; + +const { PreviewDropdown } = unlock( editorPrivateApis ); const slideY = { hidden: { y: '-50px' }, @@ -180,8 +183,14 @@ function Header( { showIconLabels={ showIconLabels } /> ) } - - + + .edit-post-header__settings > .editor-post-preview { + & > .edit-post-header__settings > .edit-post-header__post-preview-button { visibility: hidden; } & > .edit-post-header__toolbar .edit-post-header-toolbar__inserter-toggle, & > .edit-post-header__toolbar .edit-post-header-toolbar__document-overview-toggle, - & > .edit-post-header__settings > .block-editor-post-preview__dropdown, + & > .edit-post-header__settings > .editor-preview-dropdown, & > .edit-post-header__settings > .interface-pinned-items { display: none; } diff --git a/packages/edit-site/src/components/header-edit-mode/index.js b/packages/edit-site/src/components/header-edit-mode/index.js index 2d751be691a74..a18c7e3a3eaad 100644 --- a/packages/edit-site/src/components/header-edit-mode/index.js +++ b/packages/edit-site/src/components/header-edit-mode/index.js @@ -7,27 +7,26 @@ import classnames from 'classnames'; * WordPress dependencies */ import { useViewportMatch, useReducedMotion } from '@wordpress/compose'; -import { store as coreStore } from '@wordpress/core-data'; import { BlockToolbar, - __experimentalPreviewOptions as PreviewOptions, store as blockEditorStore, } from '@wordpress/block-editor'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { useEffect, useRef, useState } from '@wordpress/element'; import { PinnedItems } from '@wordpress/interface'; import { __ } from '@wordpress/i18n'; -import { external, next, previous } from '@wordpress/icons'; +import { next, previous } from '@wordpress/icons'; import { Button, __unstableMotion as motion, - MenuGroup, - MenuItem, Popover, - VisuallyHidden, } from '@wordpress/components'; import { store as preferencesStore } from '@wordpress/preferences'; -import { DocumentBar, store as editorStore } from '@wordpress/editor'; +import { + DocumentBar, + store as editorStore, + privateApis as editorPrivateApis, +} from '@wordpress/editor'; /** * Internal dependencies @@ -43,14 +42,14 @@ import { import { unlock } from '../../lock-unlock'; import { FOCUSABLE_ENTITIES } from '../../utils/constants'; +const { PreviewDropdown } = unlock( editorPrivateApis ); + export default function HeaderEditMode( { setListViewToggleElement } ) { const { - deviceType, templateType, isDistractionFree, blockEditorMode, blockSelectionStart, - homeUrl, showIconLabels, editorCanvasView, hasFixedToolbar, @@ -59,9 +58,6 @@ export default function HeaderEditMode( { setListViewToggleElement } ) { const { getEditedPostType } = select( editSiteStore ); const { getBlockSelectionStart, __unstableGetEditorMode } = select( blockEditorStore ); - const { - getUnstableBase, // Site index. - } = select( coreStore ); const { get: getPreference } = select( preferencesStore ); const { getDeviceType } = select( editorStore ); @@ -70,7 +66,6 @@ export default function HeaderEditMode( { setListViewToggleElement } ) { templateType: getEditedPostType(), blockEditorMode: __unstableGetEditorMode(), blockSelectionStart: getBlockSelectionStart(), - homeUrl: getUnstableBase()?.home, showIconLabels: getPreference( editSiteStore.name, 'showIconLabels' @@ -93,7 +88,6 @@ export default function HeaderEditMode( { setListViewToggleElement } ) { const isLargeViewport = useViewportMatch( 'medium' ); const isTopToolbar = ! isZoomOutMode && hasFixedToolbar && isLargeViewport; const blockToolbarRef = useRef(); - const { setDeviceType } = useDispatch( editorStore ); const disableMotion = useReducedMotion(); const hasDefaultEditorCanvasView = ! useHasEditorCanvasContainer(); @@ -215,34 +209,12 @@ export default function HeaderEditMode( { setListViewToggleElement } ) { { 'is-zoomed-out': isZoomedOutView } ) } > - - { ( { onClose } ) => ( - - - { __( 'View site' ) } - - { - /* translators: accessibility text */ - __( '(opens in a new tab)' ) - } - - - - ) } - + disabled={ + isFocusMode || ! hasDefaultEditorCanvasView + } + /> ) } diff --git a/packages/editor/src/components/preview-dropdown/index.js b/packages/editor/src/components/preview-dropdown/index.js new file mode 100644 index 0000000000000..b7d64f2eeebc6 --- /dev/null +++ b/packages/editor/src/components/preview-dropdown/index.js @@ -0,0 +1,136 @@ +/** + * WordPress dependencies + */ +import { useViewportMatch } from '@wordpress/compose'; +import { + DropdownMenu, + MenuGroup, + MenuItem, + VisuallyHidden, + Icon, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { check, desktop, mobile, tablet, external } from '@wordpress/icons'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import { store as editorStore } from '../../store'; +import PostPreviewButton from '../post-preview-button'; + +export default function PreviewDropdown( { + showIconLabels, + forceIsAutosaveable, + disabled, +} ) { + const { deviceType, homeUrl, isTemplate, isViewable } = useSelect( + ( select ) => { + const { getDeviceType, getCurrentPostType } = select( editorStore ); + const { getUnstableBase, getPostType } = select( coreStore ); + const _currentPostType = getCurrentPostType(); + return { + deviceType: getDeviceType(), + homeUrl: getUnstableBase()?.home, + isTemplate: _currentPostType === 'wp_template', + isViewable: getPostType( _currentPostType )?.viewable ?? false, + }; + }, + [] + ); + const { setDeviceType } = useDispatch( editorStore ); + const isMobile = useViewportMatch( 'medium', '<' ); + if ( isMobile ) return null; + + const popoverProps = { + placement: 'bottom-end', + }; + const toggleProps = { + className: 'editor-preview-dropdown__toggle', + size: 'compact', + showTooltip: ! showIconLabels, + disabled, + __experimentalIsFocusable: disabled, + }; + const menuProps = { + 'aria-label': __( 'View options' ), + }; + + const deviceIcons = { + mobile, + tablet, + desktop, + }; + + return ( + + { ( { onClose } ) => ( + <> + + setDeviceType( 'Desktop' ) } + icon={ deviceType === 'Desktop' && check } + > + { __( 'Desktop' ) } + + setDeviceType( 'Tablet' ) } + icon={ deviceType === 'Tablet' && check } + > + { __( 'Tablet' ) } + + setDeviceType( 'Mobile' ) } + icon={ deviceType === 'Mobile' && check } + > + { __( 'Mobile' ) } + + + { isTemplate && ( + + + { __( 'View site' ) } + + { + /* translators: accessibility text */ + __( '(opens in a new tab)' ) + } + + + + ) } + { isViewable && ( + + + { __( 'Preview in new tab' ) } + + + } + onPreview={ onClose } + /> + + ) } + + ) } + + ); +} diff --git a/packages/editor/src/components/preview-dropdown/style.scss b/packages/editor/src/components/preview-dropdown/style.scss new file mode 100644 index 0000000000000..43fa7cdd8ecd9 --- /dev/null +++ b/packages/editor/src/components/preview-dropdown/style.scss @@ -0,0 +1,5 @@ +.editor-preview-dropdown__button-external { + width: 100%; + display: flex; + justify-content: space-between; +} diff --git a/packages/editor/src/private-apis.js b/packages/editor/src/private-apis.js index 046feee5b9c3f..ac5bd4324946e 100644 --- a/packages/editor/src/private-apis.js +++ b/packages/editor/src/private-apis.js @@ -7,6 +7,7 @@ import { lock } from './lock-unlock'; import { EntitiesSavedStatesExtensible } from './components/entities-saved-states'; import useBlockEditorSettings from './components/provider/use-block-editor-settings'; import PostPanelRow from './components/post-panel-row'; +import PreviewDropdown from './components/preview-dropdown'; export const privateApis = {}; lock( privateApis, { @@ -14,6 +15,7 @@ lock( privateApis, { ExperimentalEditorProvider, EntitiesSavedStatesExtensible, PostPanelRow, + PreviewDropdown, // This is a temporary private API while we're updating the site editor to use EditorProvider. useBlockEditorSettings, diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss index 01e17a1a964ab..50359984af162 100644 --- a/packages/editor/src/style.scss +++ b/packages/editor/src/style.scss @@ -23,5 +23,6 @@ @import "./components/post-url/style.scss"; @import "./components/post-visibility/style.scss"; @import "./components/post-trash/style.scss"; +@import "./components/preview-dropdown/style.scss"; @import "./components/table-of-contents/style.scss"; @import "./components/template-validation-notice/style.scss"; diff --git a/test/e2e/specs/editor/plugins/block-context.spec.js b/test/e2e/specs/editor/plugins/block-context.spec.js index 1fc91debd1145..c819f29bc7383 100644 --- a/test/e2e/specs/editor/plugins/block-context.spec.js +++ b/test/e2e/specs/editor/plugins/block-context.spec.js @@ -64,7 +64,11 @@ test.describe( 'Block context', () => { .fill( '123' ); await editorPage - .getByRole( 'button', { name: 'Preview', expanded: false } ) + .getByRole( 'button', { + name: 'View', + expanded: false, + exact: true, + } ) .click(); await editorPage .getByRole( 'menuitem', { name: 'Preview in new tab' } ) diff --git a/test/e2e/specs/editor/various/new-post.spec.js b/test/e2e/specs/editor/various/new-post.spec.js index cc0243eb8e631..b3591db1ec50b 100644 --- a/test/e2e/specs/editor/various/new-post.spec.js +++ b/test/e2e/specs/editor/various/new-post.spec.js @@ -32,9 +32,9 @@ test.describe( 'new editor state', () => { await expect( title ).toBeEditable(); await expect( title ).toHaveText( '' ); - // Should display the Preview button. + // Should display the View button. await expect( - page.locator( 'role=button[name="Preview"i]' ) + page.locator( 'role=button[name="View"i]' ) ).toBeVisible(); // Should display the Post Formats UI. diff --git a/test/e2e/specs/editor/various/preview.spec.js b/test/e2e/specs/editor/various/preview.spec.js index 0666de1405fae..0657a45567baf 100644 --- a/test/e2e/specs/editor/various/preview.spec.js +++ b/test/e2e/specs/editor/various/preview.spec.js @@ -22,11 +22,6 @@ test.describe( 'Preview', () => { } ) => { const editorPage = page; - // Disabled until content present. - await expect( - editorPage.locator( 'role=button[name="Preview"i]' ) - ).toBeDisabled(); - await editor.canvas .locator( 'role=textbox[name="Add title"i]' ) .type( 'Hello World' ); @@ -301,7 +296,7 @@ test.describe( 'Preview with private custom post type', () => { } ); // Open the view menu. - await page.click( 'role=button[name="Preview"i]' ); + await page.click( 'role=button[name="View"i]' ); await expect( page.locator( 'role=menuitem[name="Preview in new tab"i]' ) @@ -316,7 +311,7 @@ class PreviewUtils { async waitForPreviewNavigation( previewPage ) { const previewToggle = this.page.locator( - 'role=button[name="Preview"i][expanded=false]' + 'role=button[name="View"i][expanded=false]' ); const isDropdownClosed = await previewToggle.isVisible(); if ( isDropdownClosed ) {