{
+ onChangeView( {
+ ...view,
+ layout: {
+ ...view.layout,
+ mediaField: field.id,
+ },
+ } );
+ } }
+ >
+
+ { field.label }
+
+
+ );
+ } ) }
+
+ }
+ />
+ );
+ }
+ return null;
+}
+
function FieldControl() {
const { view, fields, onChangeView } = useContext( DataViewsContext );
@@ -439,6 +550,11 @@ function FieldControl() {
{ !! visibleFields?.length && (
+
{ visibleFields.map( ( field ) => (
[] ): string[] {
+ return fields
+ .filter( ( { isMediaField } ) => isMediaField )
+ .map( ( { id } ) => id );
+}
+
export function getHiddenFieldIds(
view: View,
fields: Field< any >[]
@@ -107,17 +113,11 @@ export function getHiddenFieldIds(
const fieldsToExclude = [
...getCombinedFieldIds( view ),
...getVisibleFieldIds( view, fields ),
+ ...( view.type === LAYOUT_GRID || view.type === LAYOUT_LIST
+ ? getMediaFieldIds( fields )
+ : [] ),
];
- // The media field does not need to be in the view.fields to be displayed.
- if ( view.type === LAYOUT_GRID && view.layout?.mediaField ) {
- fieldsToExclude.push( view.layout?.mediaField );
- }
-
- if ( view.type === LAYOUT_LIST && view.layout?.mediaField ) {
- fieldsToExclude.push( view.layout?.mediaField );
- }
-
return fields
.filter(
( { id, enableHiding } ) =>
diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts
index 8c4276f2541ecc..5604490594cd70 100644
--- a/packages/dataviews/src/types.ts
+++ b/packages/dataviews/src/types.ts
@@ -158,6 +158,11 @@ export type Field< Item > = {
* Defaults to `item[ field.id ]`.
*/
getValue?: ( args: { item: Item } ) => any;
+
+ /**
+ * True if the field is supposed to be used as a media field.
+ */
+ isMediaField?: boolean;
};
export type NormalizedField< Item > = Field< Item > & {
diff --git a/packages/editor/README.md b/packages/editor/README.md
index ac655bd1c99d8c..9a07acb44db251 100644
--- a/packages/editor/README.md
+++ b/packages/editor/README.md
@@ -379,7 +379,7 @@ _Parameters_
- _props.post_ `[Object]`: The post object to edit. This is required.
- _props.\_\_unstableTemplate_ `[Object]`: The template object wrapper the edited post. This is optional and can only be used when the post type supports templates (like posts and pages).
- _props.settings_ `[Object]`: The settings object to use for the editor. This is optional and can be used to override the default settings.
-- _props.children_ `[Element]`: Children elements for which the BlockEditorProvider context should apply. This is optional.
+- _props.children_ `[React.ReactNode]`: Children elements for which the BlockEditorProvider context should apply. This is optional.
_Returns_
diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js
index 996e9e68954591..25d761630032ca 100644
--- a/packages/editor/src/components/provider/index.js
+++ b/packages/editor/src/components/provider/index.js
@@ -391,14 +391,14 @@ export const ExperimentalEditorProvider = withRegistryProvider(
*
* All modification and changes are performed to the `@wordpress/core-data` store.
*
- * @param {Object} props The component props.
- * @param {Object} [props.post] The post object to edit. This is required.
- * @param {Object} [props.__unstableTemplate] The template object wrapper the edited post.
- * This is optional and can only be used when the post type supports templates (like posts and pages).
- * @param {Object} [props.settings] The settings object to use for the editor.
- * This is optional and can be used to override the default settings.
- * @param {Element} [props.children] Children elements for which the BlockEditorProvider context should apply.
- * This is optional.
+ * @param {Object} props The component props.
+ * @param {Object} [props.post] The post object to edit. This is required.
+ * @param {Object} [props.__unstableTemplate] The template object wrapper the edited post.
+ * This is optional and can only be used when the post type supports templates (like posts and pages).
+ * @param {Object} [props.settings] The settings object to use for the editor.
+ * This is optional and can be used to override the default settings.
+ * @param {React.ReactNode} [props.children] Children elements for which the BlockEditorProvider context should apply.
+ * This is optional.
*
* @example
* ```jsx
diff --git a/packages/editor/src/dataviews/fields/content-preview/content-preview-view.tsx b/packages/editor/src/dataviews/fields/content-preview/content-preview-view.tsx
new file mode 100644
index 00000000000000..2d235c3ed1bc01
--- /dev/null
+++ b/packages/editor/src/dataviews/fields/content-preview/content-preview-view.tsx
@@ -0,0 +1,78 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useMemo } from '@wordpress/element';
+// @ts-ignore
+import { parse } from '@wordpress/blocks';
+import {
+ BlockPreview,
+ privateApis as blockEditorPrivateApis,
+ // @ts-ignore
+} from '@wordpress/block-editor';
+import type { BasePost } from '@wordpress/fields';
+import { useSelect } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import { EditorProvider } from '../../../components/provider';
+import { unlock } from '../../../lock-unlock';
+// @ts-ignore
+import { store as editorStore } from '../../../store';
+
+const { useGlobalStyle } = unlock( blockEditorPrivateApis );
+
+function ContentPreviewContainer( {
+ children,
+}: {
+ children: React.ReactNode;
+} ) {
+ const [ backgroundColor = 'white' ] = useGlobalStyle( 'color.background' );
+ return (
+
+ { children }
+
+ );
+}
+
+export default function ContentPreviewView( { item }: { item: BasePost } ) {
+ const settings = useSelect( ( select ) => {
+ return select( editorStore ).getEditorSettings();
+ }, [] );
+
+ const content =
+ typeof item.content === 'string' ? item.content : item.content.raw;
+ const blocks = useMemo( () => {
+ return parse( content );
+ }, [ content ] );
+ const isEmpty = ! blocks?.length;
+ // Wrap everything in a block editor provider to ensure 'styles' that are needed
+ // for the previews are synced between the site editor store and the block editor store.
+ // Additionally we need to have the `__experimentalBlockPatterns` setting in order to
+ // render patterns inside the previews.
+ // TODO: Same approach is used in the patterns list and it becomes obvious that some of
+ // the block editor settings are needed in context where we don't have the block editor.
+ // Explore how we can solve this in a better way.
+ return (
+
+
+ { isEmpty && (
+
+ { __( 'Empty content' ) }
+
+ ) }
+ { ! isEmpty && (
+
+
+
+ ) }
+
+
+ );
+}
diff --git a/packages/editor/src/dataviews/fields/content-preview/index.tsx b/packages/editor/src/dataviews/fields/content-preview/index.tsx
new file mode 100644
index 00000000000000..53c8acca396b04
--- /dev/null
+++ b/packages/editor/src/dataviews/fields/content-preview/index.tsx
@@ -0,0 +1,22 @@
+/**
+ * WordPress dependencies
+ */
+import type { Field } from '@wordpress/dataviews';
+import type { BasePost } from '@wordpress/fields';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import ContentPreviewView from './content-preview-view';
+
+const contentPreviewField: Field< BasePost > = {
+ type: 'text',
+ id: 'content-preview',
+ label: __( 'Content preview' ),
+ render: ContentPreviewView,
+ enableSorting: false,
+ isMediaField: true,
+};
+
+export default contentPreviewField;
diff --git a/packages/editor/src/dataviews/fields/content-preview/style.scss b/packages/editor/src/dataviews/fields/content-preview/style.scss
new file mode 100644
index 00000000000000..4f204dc5108c9b
--- /dev/null
+++ b/packages/editor/src/dataviews/fields/content-preview/style.scss
@@ -0,0 +1,21 @@
+.editor-fields-content-preview {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ border-radius: $radius-medium;
+
+ .dataviews-view-table & {
+ width: 96px;
+ flex-grow: 0;
+ }
+
+ .block-editor-block-preview__container,
+ .editor-fields-content-preview__empty {
+ margin-top: auto;
+ margin-bottom: auto;
+ }
+}
+
+.editor-fields-content-preview__empty {
+ text-align: center;
+}
diff --git a/packages/editor/src/dataviews/store/private-actions.ts b/packages/editor/src/dataviews/store/private-actions.ts
index 77ac131a8e2302..e72068a2de0bc2 100644
--- a/packages/editor/src/dataviews/store/private-actions.ts
+++ b/packages/editor/src/dataviews/store/private-actions.ts
@@ -34,6 +34,7 @@ import {
authorField,
titleField,
} from '@wordpress/fields';
+import contentPreviewField from '../fields/content-preview';
import duplicateTemplatePart from '../actions/duplicate-template-part';
export function registerEntityAction< Item >(
@@ -169,6 +170,7 @@ export const registerPostTypeSchema =
parentField,
commentStatusField,
passwordField,
+ contentPreviewField,
];
registry.batch( () => {
diff --git a/packages/editor/src/style.scss b/packages/editor/src/style.scss
index b62ccd2c8ac71d..4ce19144101dc8 100644
--- a/packages/editor/src/style.scss
+++ b/packages/editor/src/style.scss
@@ -57,3 +57,4 @@
@import "./components/table-of-contents/style.scss";
@import "./components/text-editor/style.scss";
@import "./components/visual-editor/style.scss";
+@import "./dataviews/fields/content-preview/style.scss";
diff --git a/packages/fields/src/fields/featured-image/index.ts b/packages/fields/src/fields/featured-image/index.ts
index 62d7e8240aded0..5df4ea404fd4c2 100644
--- a/packages/fields/src/fields/featured-image/index.ts
+++ b/packages/fields/src/fields/featured-image/index.ts
@@ -18,6 +18,7 @@ const featuredImageField: Field< BasePost > = {
Edit: FeaturedImageEdit,
render: FeaturedImageView,
enableSorting: false,
+ isMediaField: true,
};
export default featuredImageField;