+# **PluginPreview**
+
+Component used by a plugin to define the contents of a "custom
+preview". The children of this component will be displayed in the main editor
+screen when this "custom preview" is chosen from the preview menu.
+
+_Parameters_
+
+- _props_ `Object`: Component properties.
+- _props.previewId_ `string`: The internal name of this custom preview. Must match the _previewId_ given to `PluginPreviewMenuItem`.
+- _props.children_ `WPElement`: Children to be rendered.
+
+# **PluginPreviewMenuItem**
+
+Component used by a plugin to define the contents of a menu item that
+selects a "custom preview". The children of this component will be displayed
+inside the preview menu. Typically a single string is good enough.
+
+_Parameters_
+
+- _props_ `Object`: Component properties.
+- _props.previewId_ `string`: The internal name of this custom preview. Must match the _previewId_ given to `PluginPreview`.
+- _props.children_ `WPElement`: Children to be rendered.
+
# **PreserveScrollInReorder**
Undocumented declaration.
diff --git a/packages/block-editor/src/components/preview-options/index.js b/packages/block-editor/src/components/preview-options/index.js
index 122c85d852d0d..ab60cdcb6fb45 100644
--- a/packages/block-editor/src/components/preview-options/index.js
+++ b/packages/block-editor/src/components/preview-options/index.js
@@ -7,10 +7,24 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { useViewportMatch } from '@wordpress/compose';
-import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components';
+import { DropdownMenu, MenuGroup, MenuItem, Slot } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { check } from '@wordpress/icons';
+/*
+ * coreDeviceTypes: An array of strings. The strings returned represent
+ * deviceType values that belong to the core system. When the deviceType,
+ * returned by `__experimentalGetPreviewDeviceType()`, is one of these values,
+ * the built in VisualEditor is responsible for rendering a preview of that
+ * type.
+
+ * When the deviceType is something other than one of the coreDeviceTypes, we are
+ * rendering a custom deviceType registered by the and
+ * components, and defer to a filled by the plugin to
+ * draw the preview.
+ */
+export const coreDeviceTypes = [ 'Desktop', 'Tablet', 'Mobile' ];
+
export default function PreviewOptions( {
children,
className,
@@ -67,6 +81,15 @@ export default function PreviewOptions( {
{ __( 'Mobile' ) }
+
+
+ { ( fills ) =>
+ ! fills || fills.length === 0 ? null : (
+ { fills }
+ )
+ }
+
+
{ children }
>
) }
diff --git a/packages/block-editor/src/components/preview-options/plugin-preview-menu-item/index.js b/packages/block-editor/src/components/preview-options/plugin-preview-menu-item/index.js
new file mode 100644
index 0000000000000..538cb217d47b9
--- /dev/null
+++ b/packages/block-editor/src/components/preview-options/plugin-preview-menu-item/index.js
@@ -0,0 +1,54 @@
+/**
+ * WordPress dependencies
+ */
+import { Fill, MenuItem } from '@wordpress/components';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { check } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import { coreDeviceTypes } from '../index';
+
+/**
+ * Component used by a plugin to define the contents of a menu item that
+ * selects a "custom preview". The children of this component will be displayed
+ * inside the preview menu. Typically a single string is good enough.
+ *
+ * @param {Object} props Component properties.
+ * @param {string} props.previewId The internal name of this custom preview. Must match the _previewId_ given to `PluginPreview`.
+ * @param {WPElement} props.children Children to be rendered.
+ */
+export default function PluginPreviewMenuItem( {
+ children,
+ previewId,
+ ...props
+} ) {
+ const {
+ __experimentalSetPreviewDeviceType: setPreviewDeviceType,
+ } = useDispatch( 'core/edit-post' );
+
+ const { deviceType } = useSelect(
+ ( select ) => ( {
+ deviceType: select(
+ 'core/edit-post'
+ ).__experimentalGetPreviewDeviceType(),
+ } ),
+ []
+ );
+
+ if ( coreDeviceTypes.includes( previewId ) ) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/packages/block-editor/src/components/preview-options/plugin-preview/index.js b/packages/block-editor/src/components/preview-options/plugin-preview/index.js
new file mode 100644
index 0000000000000..02025afd19ee0
--- /dev/null
+++ b/packages/block-editor/src/components/preview-options/plugin-preview/index.js
@@ -0,0 +1,24 @@
+/**
+ * WordPress dependencies
+ */
+import { Fill } from '@wordpress/components';
+
+/**
+ * Component used by a plugin to define the contents of a "custom
+ * preview". The children of this component will be displayed in the main editor
+ * screen when this "custom preview" is chosen from the preview menu.
+ *
+ * @param {Object} props Component properties.
+ * @param {string} props.previewId The internal name of this custom preview. Must match the _previewId_ given to `PluginPreviewMenuItem`.
+ * @param {WPElement} props.children Children to be rendered.
+ */
+export default function PluginPreview( { children, previewId, ...props } ) {
+ return (
+
+ { children }
+
+ );
+}
diff --git a/packages/block-editor/src/index.js b/packages/block-editor/src/index.js
index add6f5a5f9910..8d54971d0cfa8 100644
--- a/packages/block-editor/src/index.js
+++ b/packages/block-editor/src/index.js
@@ -16,3 +16,6 @@ export * from './components';
export * from './utils';
export { storeConfig } from './store';
export { SETTINGS_DEFAULTS } from './store/defaults';
+export { default as PluginPreviewMenuItem } from './components/preview-options/plugin-preview-menu-item';
+export { default as PluginPreview } from './components/preview-options/plugin-preview';
+export { coreDeviceTypes } from './components/preview-options';
diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js
index f7b2384e787b7..eb94c58e5695f 100644
--- a/packages/edit-post/src/components/layout/index.js
+++ b/packages/edit-post/src/components/layout/index.js
@@ -39,7 +39,7 @@ import { close } from '@wordpress/icons';
* Internal dependencies
*/
import TextEditor from '../text-editor';
-import VisualEditor from '../visual-editor';
+import VisualEditorOrPluginPreview from '../visual-editor/visual-editor-or-plugin-preview';
import EditPostKeyboardShortcuts from '../keyboard-shortcuts';
import KeyboardShortcutHelpModal from '../keyboard-shortcut-help-modal';
import ManageBlocksModal from '../manage-blocks-modal';
@@ -250,7 +250,7 @@ function Layout() {
) }
{ isRichEditingEnabled && mode === 'visual' && (
-
+
) }
diff --git a/packages/edit-post/src/components/visual-editor/visual-editor-or-plugin-preview.js b/packages/edit-post/src/components/visual-editor/visual-editor-or-plugin-preview.js
new file mode 100644
index 0000000000000..14dd9695cb98c
--- /dev/null
+++ b/packages/edit-post/src/components/visual-editor/visual-editor-or-plugin-preview.js
@@ -0,0 +1,25 @@
+/**
+ * WordPress dependencies
+ */
+import { useSelect } from '@wordpress/data';
+import { Slot } from '@wordpress/components';
+import { coreDeviceTypes } from '@wordpress/block-editor';
+
+/**
+ * Internal dependencies
+ */
+import VisualEditor from './index';
+
+function VisualEditorOrPluginPreview() {
+ const deviceType = useSelect( ( select ) => {
+ return select( 'core/edit-post' ).__experimentalGetPreviewDeviceType();
+ }, [] );
+
+ if ( ! coreDeviceTypes.includes( deviceType ) ) {
+ return (
+
+ );
+ }
+ return ;
+}
+export default VisualEditorOrPluginPreview;