diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 09e48ebda0f65e..7e359ae4bc7476 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -431,6 +431,15 @@ Display a list of all pages. ([Source](https://github.com/WordPress/gutenberg/tr
- **Supports:** ~~html~~, ~~reusable~~
- **Attributes:** parentPageID
+## Page List Item
+
+Displays a page inside a list of all pages. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/page-list-item))
+
+- **Name:** core/page-list-item
+- **Category:** widgets
+- **Supports:** ~~html~~, ~~inserter~~, ~~lock~~, ~~reusable~~
+- **Attributes:** hasChildren, id, label, link, title
+
## Paragraph
Start with the basic building block of all narrative. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/paragraph))
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index 88d2c26f02403a..34ba9fd5a2c8f1 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -67,6 +67,7 @@ import * as navigationSubmenu from './navigation-submenu';
import * as nextpage from './nextpage';
import * as pattern from './pattern';
import * as pageList from './page-list';
+import * as pageListItem from './page-list-item';
import * as paragraph from './paragraph';
import * as postAuthor from './post-author';
import * as postAuthorName from './post-author-name';
@@ -155,6 +156,7 @@ const getAllBlocks = () =>
more,
nextpage,
pageList,
+ pageListItem,
pattern,
preformatted,
pullquote,
diff --git a/packages/block-library/src/page-list-item/block.json b/packages/block-library/src/page-list-item/block.json
new file mode 100644
index 00000000000000..97c312d1b7960e
--- /dev/null
+++ b/packages/block-library/src/page-list-item/block.json
@@ -0,0 +1,51 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "core/page-list-item",
+ "title": "Page List Item",
+ "category": "widgets",
+ "parent": [ "core/page-list" ],
+ "description": "Displays a page inside a list of all pages.",
+ "keywords": [ "page", "menu", "navigation" ],
+ "textdomain": "default",
+ "attributes": {
+ "id": {
+ "type": "number"
+ },
+ "label": {
+ "type": "string"
+ },
+ "title": {
+ "type": "string"
+ },
+ "link": {
+ "type": "string"
+ },
+ "hasChildren": {
+ "type": "boolean"
+ }
+ },
+ "usesContext": [
+ "textColor",
+ "customTextColor",
+ "backgroundColor",
+ "customBackgroundColor",
+ "overlayTextColor",
+ "customOverlayTextColor",
+ "overlayBackgroundColor",
+ "customOverlayBackgroundColor",
+ "fontSize",
+ "customFontSize",
+ "showSubmenuIcon",
+ "style",
+ "openSubmenusOnClick"
+ ],
+ "supports": {
+ "reusable": false,
+ "html": false,
+ "lock": false,
+ "inserter": false
+ },
+ "editorStyle": "wp-block-page-list-editor",
+ "style": "wp-block-page-list"
+}
diff --git a/packages/block-library/src/page-list-item/edit.js b/packages/block-library/src/page-list-item/edit.js
new file mode 100644
index 00000000000000..db8e0a5c173736
--- /dev/null
+++ b/packages/block-library/src/page-list-item/edit.js
@@ -0,0 +1,94 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import { InnerBlocks } from '@wordpress/block-editor';
+import { useSelect } from '@wordpress/data';
+import { store as coreStore } from '@wordpress/core-data';
+
+/**
+ * Internal dependencies
+ */
+import { ItemSubmenuIcon } from '../navigation-link/icons';
+
+function useFrontPageId() {
+ return useSelect( ( select ) => {
+ const canReadSettings = select( coreStore ).canUser(
+ 'read',
+ 'settings'
+ );
+ if ( ! canReadSettings ) {
+ return undefined;
+ }
+
+ const site = select( coreStore ).getEntityRecord( 'root', 'site' );
+ return site?.show_on_front === 'page' && site?.page_on_front;
+ }, [] );
+}
+
+export default function PageListItemEdit( { context, attributes } ) {
+ const { id, label, link, hasChildren } = attributes;
+ const isNavigationChild = 'showSubmenuIcon' in context;
+ const frontPageId = useFrontPageId();
+ return (
+
+ { hasChildren && context.openSubmenusOnClick ? (
+ <>
+
+
+
+
+ >
+ ) : (
+
+ { label }
+
+ ) }
+ { hasChildren && (
+ <>
+ { ! context.openSubmenusOnClick &&
+ context.showSubmenuIcon && (
+
+ ) }
+
+ >
+ ) }
+
+ );
+}
diff --git a/packages/block-library/src/page-list-item/index.js b/packages/block-library/src/page-list-item/index.js
new file mode 100644
index 00000000000000..18b7fe2e40e9ea
--- /dev/null
+++ b/packages/block-library/src/page-list-item/index.js
@@ -0,0 +1,24 @@
+/**
+ * WordPress dependencies
+ */
+import { pages as icon } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import initBlock from '../utils/init-block';
+import metadata from './block.json';
+import edit from './edit.js';
+
+const { name } = metadata;
+
+export { metadata, name };
+
+export const settings = {
+ __experimentalLabel: ( { label } ) => label,
+ icon,
+ example: {},
+ edit,
+};
+
+export const init = () => initBlock( { name, metadata, settings } );
diff --git a/packages/block-library/src/page-list-item/init.js b/packages/block-library/src/page-list-item/init.js
new file mode 100644
index 00000000000000..79f0492c2cb2f8
--- /dev/null
+++ b/packages/block-library/src/page-list-item/init.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import { init } from './';
+
+export default init();
diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js
index ac7ed6162cf975..d9e541305b6976 100644
--- a/packages/block-library/src/page-list/edit.js
+++ b/packages/block-library/src/page-list/edit.js
@@ -10,6 +10,7 @@ import {
InspectorControls,
BlockControls,
useBlockProps,
+ useInnerBlocksProps,
getColorClassName,
} from '@wordpress/block-editor';
import {
@@ -20,15 +21,13 @@ import {
ComboboxControl,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
-import { useMemo, useState, memo } from '@wordpress/element';
-import { useSelect } from '@wordpress/data';
-import { store as coreStore, useEntityRecords } from '@wordpress/core-data';
+import { useMemo, useState } from '@wordpress/element';
+import { useEntityRecords } from '@wordpress/core-data';
/**
* Internal dependencies
*/
import ConvertToLinksModal from './convert-to-links-modal';
-import { ItemSubmenuIcon } from '../navigation-link/icons';
// We only show the edit option when page count is <= MAX_PAGE_COUNT
// Performance of Navigation Links is not good past this value.
@@ -65,6 +64,39 @@ export default function PageListEdit( {
style: { ...context.style?.color },
} );
+ const makeBlockTemplate = ( parentId = 0 ) => {
+ const pages = pagesByParentId.get( parentId );
+
+ if ( ! pages?.length ) {
+ return [];
+ }
+
+ return pages.reduce( ( template, page ) => {
+ const hasChildren = pagesByParentId.has( page.id );
+ const pageProps = {
+ id: page.id,
+ label: page.title?.rendered,
+ title: page.title?.rendered,
+ link: page.url,
+ hasChildren,
+ };
+ let item = null;
+ const children = makeBlockTemplate( page.id );
+ item = [ 'core/page-list-item', pageProps, children ];
+
+ template.push( item );
+
+ return template;
+ }, [] );
+ };
+
+ const pagesTemplate = useMemo( makeBlockTemplate, [ pagesByParentId ] );
+
+ const innerBlocksProps = useInnerBlocksProps( blockProps, {
+ template: pagesTemplate,
+ templateLock: 'all',
+ } );
+
const getBlockContent = () => {
if ( ! hasResolvedPages ) {
return (
@@ -95,15 +127,7 @@ export default function PageListEdit( {
}
if ( totalPages > 0 ) {
- return (
-
- );
+ return ;
}
};
@@ -155,21 +179,6 @@ export default function PageListEdit( {
);
}
-function useFrontPageId() {
- return useSelect( ( select ) => {
- const canReadSettings = select( coreStore ).canUser(
- 'read',
- 'settings'
- );
- if ( ! canReadSettings ) {
- return undefined;
- }
-
- const site = select( coreStore ).getEntityRecord( 'root', 'site' );
- return site?.show_on_front === 'page' && site?.page_on_front;
- }, [] );
-}
-
function useGetPages() {
const { records: pages, hasResolved: hasResolvedPages } = useEntityRecords(
'postType',
@@ -221,92 +230,3 @@ function usePageData( pageId = 0 ) {
};
}, [ pageId, pages, hasResolvedPages ] );
}
-
-const PageItems = memo( function PageItems( {
- context,
- pagesByParentId,
- parentId = 0,
- depth = 0,
-} ) {
- const parentPage = usePageData( parentId );
- const pages = pagesByParentId.get( parentId )
- ? pagesByParentId.get( parentId )
- : [ parentPage ];
- const frontPageId = useFrontPageId();
-
- if ( ! pages?.length ) {
- return [];
- }
-
- return pages.map( ( page ) => {
- const hasChildren = pagesByParentId.has( page.id );
- const isNavigationChild = 'showSubmenuIcon' in context;
- return (
-
- { hasChildren && context.openSubmenusOnClick ? (
- <>
-
-
-
-
- >
- ) : (
-
- { page.title?.rendered }
-
- ) }
- { hasChildren && (
- <>
- { ! context.openSubmenusOnClick &&
- context.showSubmenuIcon && (
-
- ) }
-
- >
- ) }
-
- );
- } );
-} );
diff --git a/test/integration/fixtures/blocks/core__page-list-item.html b/test/integration/fixtures/blocks/core__page-list-item.html
new file mode 100644
index 00000000000000..2b1b33d71231cf
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__page-list-item.html
@@ -0,0 +1 @@
+
diff --git a/test/integration/fixtures/blocks/core__page-list-item.json b/test/integration/fixtures/blocks/core__page-list-item.json
new file mode 100644
index 00000000000000..5e6f09eb971eb7
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__page-list-item.json
@@ -0,0 +1,11 @@
+[
+ {
+ "name": "core/page-list-item",
+ "isValid": true,
+ "attributes": {
+ "label": "Page",
+ "link": "https://example.com"
+ },
+ "innerBlocks": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__page-list-item.parsed.json b/test/integration/fixtures/blocks/core__page-list-item.parsed.json
new file mode 100644
index 00000000000000..e93cff97dd385e
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__page-list-item.parsed.json
@@ -0,0 +1,14 @@
+[
+ {
+ "blockName": "core/page-list-item",
+ "attrs": {
+ "id": "1",
+ "label": "Page",
+ "link": "https://example.com",
+ "hasChildren": "false"
+ },
+ "innerBlocks": [],
+ "innerHTML": "",
+ "innerContent": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__page-list-item.serialized.html b/test/integration/fixtures/blocks/core__page-list-item.serialized.html
new file mode 100644
index 00000000000000..d61659fe1899a9
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__page-list-item.serialized.html
@@ -0,0 +1 @@
+