diff --git a/package-lock.json b/package-lock.json
index dc95bcdb05e2f6..f0e5ba3b04e462 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18769,6 +18769,7 @@
"@wordpress/date": "file:packages/date",
"@wordpress/deprecated": "file:packages/deprecated",
"@wordpress/dom": "file:packages/dom",
+ "@wordpress/editor": "file:packages/editor",
"@wordpress/element": "file:packages/element",
"@wordpress/escape-html": "file:packages/escape-html",
"@wordpress/hooks": "file:packages/hooks",
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index ef95510c01421f..16b2afa6d99203 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -46,6 +46,7 @@
"@wordpress/date": "file:../date",
"@wordpress/deprecated": "file:../deprecated",
"@wordpress/dom": "file:../dom",
+ "@wordpress/editor": "file:../editor",
"@wordpress/element": "file:../element",
"@wordpress/escape-html": "file:../escape-html",
"@wordpress/hooks": "file:../hooks",
diff --git a/packages/block-library/src/page-list/block.json b/packages/block-library/src/page-list/block.json
index 146f236fca84f8..f654922dcb4afb 100644
--- a/packages/block-library/src/page-list/block.json
+++ b/packages/block-library/src/page-list/block.json
@@ -3,10 +3,13 @@
"name": "core/page-list",
"title": "Page List",
"category": "widgets",
- "description": "Display a list of all pages.",
+ "description": "Display a list of pages.",
"keywords": [ "menu", "navigation" ],
"textdomain": "default",
"attributes": {
+ "showOnlyChildPages": {
+ "type": "boolean"
+ }
},
"usesContext": [
"textColor",
diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js
index 7d44a376ce0429..49ea5580e64c54 100644
--- a/packages/block-library/src/page-list/edit.js
+++ b/packages/block-library/src/page-list/edit.js
@@ -11,10 +11,19 @@ import {
BlockControls,
useBlockProps,
getColorClassName,
+ InspectorControls,
} from '@wordpress/block-editor';
-import { ToolbarButton, Placeholder, Spinner } from '@wordpress/components';
+import {
+ PanelBody,
+ Placeholder,
+ Spinner,
+ ToggleControl,
+ ToolbarButton,
+} from '@wordpress/components';
+import { useSelect } from '@wordpress/data';
+import { store as editorStore } from '@wordpress/editor';
import { __ } from '@wordpress/i18n';
-import { useEffect, useState, memo } from '@wordpress/element';
+import { memo, useEffect, useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
@@ -28,7 +37,12 @@ import { ItemSubmenuIcon } from '../navigation-link/icons';
// Performance of Navigation Links is not good past this value.
const MAX_PAGE_COUNT = 100;
-export default function PageListEdit( { context, clientId } ) {
+export default function PageListEdit( {
+ attributes,
+ clientId,
+ context,
+ setAttributes,
+} ) {
const { pagesByParentId, totalPages } = usePagesByParentId();
const isNavigationChild = 'showSubmenuIcon' in context;
@@ -39,6 +53,13 @@ export default function PageListEdit( { context, clientId } ) {
const openModal = () => setOpen( true );
const closeModal = () => setOpen( false );
+ const showChildPageToggle = useSelect( ( select ) => {
+ const { getCurrentPostType } = select( editorStore );
+ const currentPostType = getCurrentPostType();
+ const hideToggleFrom = [ 'post' ];
+ return ! hideToggleFrom.includes( currentPostType );
+ } );
+
const blockProps = useBlockProps( {
className: classnames( 'wp-block-page-list', {
'has-text-color': !! context.textColor,
@@ -57,6 +78,24 @@ export default function PageListEdit( { context, clientId } ) {
return (
<>
+
+ { showChildPageToggle && (
+
+
+ setAttributes( {
+ showOnlyChildPages: ! attributes.showOnlyChildPages,
+ } )
+ }
+ help={ __(
+ 'When enabled, the block lists only child pages of the current page.'
+ ) }
+ />
+
+ ) }
+
{ allowConvertToLinks && (
diff --git a/packages/block-library/src/page-list/index.php b/packages/block-library/src/page-list/index.php
index 733d2e0e4354ee..3b39e48e19aa8d 100644
--- a/packages/block-library/src/page-list/index.php
+++ b/packages/block-library/src/page-list/index.php
@@ -226,6 +226,31 @@ function block_core_page_list_nest_pages( $current_level, $children ) {
return $current_level;
}
+/**
+ * Determines if page should be classified as top-level or child page. Broken
+ * out to this function to explain instead of a complex if statement.
+ * When only_child_pages is true, force child pages to be considered top level.
+ * Thy are top level since the parent is not shown (only child pages).
+ *
+ * @param int $page_parent_id Parent id of the page being tested.
+ * @param boolean $only_child_pages Only showing child pages.
+ * @param int $parent_id Top level parent id for child pages, needed so we can nest grand children.
+ * @return boolean True if page should be considered a child page.
+ */
+function block_core_page_list_treat_as_child( $page_parent_id, $only_child_pages, $parent_id ) {
+ if ( ! $page_parent_id ) {
+ return false;
+ }
+
+ // Check only child pages, and id matches parent, then it is top level and
+ // should not be treated like a child page.
+ if ( $only_child_pages && ( $page_parent_id === $parent_id ) ) {
+ return false;
+ }
+
+ return true;
+}
+
/**
* Renders the `core/page-list` block on server.
*
@@ -236,16 +261,30 @@ function block_core_page_list_nest_pages( $current_level, $children ) {
* @return string Returns the page list markup.
*/
function render_block_core_page_list( $attributes, $content, $block ) {
+ global $post;
static $block_id = 0;
$block_id++;
- $all_pages = get_pages(
- array(
- 'sort_column' => 'menu_order,post_title',
- 'order' => 'asc',
- )
+ $only_child_pages = isset( $attributes['showOnlyChildPages'] ) && $attributes['showOnlyChildPages'];
+ // The pages will be siblings (same parent) or set parent id equal to self if no children.
+ $parent_id = ( $post->post_parent ) ? $post->post_parent : $post->ID;
+
+ // TODO: When https://core.trac.wordpress.org/ticket/39037 REST API support for multiple orderby values is resolved,
+ // update 'sort_column' to 'menu_order, post_title'. Sorting by both menu_order and post_title ensures a stable sort.
+ // Otherwise with pages that have the same menu_order value, we can see different ordering depending on how DB
+ // queries are constructed internally. For example we might see a different order when a limit is set to <499
+ // versus >= 500.
+ $query_args = array(
+ 'sort_column' => 'menu_order',
+ 'order' => 'asc',
);
+ if ( $only_child_pages && $parent_id ) {
+ $query_args['child_of'] = $parent_id;
+ }
+
+ $all_pages = get_pages( $query_args );
+
// If thare are no pages, there is nothing to show.
if ( empty( $all_pages ) ) {
return;
@@ -264,7 +303,8 @@ function render_block_core_page_list( $attributes, $content, $block ) {
$active_page_ancestor_ids = get_post_ancestors( $page->ID );
}
- if ( $page->post_parent ) {
+ // See function for logic when pages are treated like child pages.
+ if ( block_core_page_list_treat_as_child( $page->post_parent, $only_child_pages, $parent_id ) ) {
$pages_with_children[ $page->post_parent ][ $page->ID ] = array(
'page_id' => $page->ID,
'title' => $page->post_title,