Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/block-library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Playlist: Shorten the track toolbar button label from "Add track" to "Add".
- Gallery: Rename the dynamic variation's "Convert to images" action to "Detach", and confirm it in a dialog explaining that the gallery will keep its current images but stop updating automatically ([#80727](https://github.com/WordPress/gutenberg/pull/80727)).
- Page List: Rename the "Edit" action to "Detach", and confirm it in a dialog explaining that the list will keep its current pages but stop adding new ones automatically, matching the Gallery block ([#80847](https://github.com/WordPress/gutenberg/pull/80847)).

### Bug Fixes

Expand Down
53 changes: 0 additions & 53 deletions packages/block-library/src/page-list/convert-to-links-modal.js

This file was deleted.

94 changes: 57 additions & 37 deletions packages/block-library/src/page-list/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,45 @@ import {
Button,
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
__experimentalConfirmDialog as ConfirmDialog,
} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { useMemo, useState, useEffect, useCallback } from '@wordpress/element';
import { useMemo, useState, useEffect } from '@wordpress/element';
import { useEntityRecords } from '@wordpress/core-data';
import { useSelect, useDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import { useConvertToNavigationLinks } from './use-convert-to-navigation-links';
import {
convertDescription,
ConvertToLinksModal,
} from './convert-to-links-modal';
import { useToolsPanelDropdownMenuProps } from '../utils/hooks';

// We only show the edit option when page count is <= MAX_PAGE_COUNT
// We only show the detach option when page count is <= MAX_PAGE_COUNT
// Performance of Navigation Links is not good past this value.
const MAX_PAGE_COUNT = 100;
const NOOP = () => {};

// Shown from both the block toolbar and the Settings panel so the two entry
// points explain the change identically.
function DetachPageListDialog( { isBusy, onConfirm, onCancel } ) {
return (
<ConfirmDialog
isOpen
title={ __( 'Detach Page List' ) }
__experimentalHideHeader={ false }
confirmButtonText={ __( 'Detach' ) }
onConfirm={ onConfirm }
onCancel={ onCancel }
isBusy={ isBusy }
size="medium"
Comment thread
Mamaduka marked this conversation as resolved.
>
{ __(
"This Navigation Menu displays your website's pages. Detaching will enable you to add, delete, or reorder pages. However, new pages will no longer be added automatically."
) }
</ConfirmDialog>
);
}

function BlockContent( {
blockProps,
innerBlocksProps,
Expand Down Expand Up @@ -122,9 +141,7 @@ export default function PageListEdit( {
setAttributes,
} ) {
const { parentPageID } = attributes;
const [ isOpen, setOpen ] = useState( false );
const openModal = useCallback( () => setOpen( true ), [] );
const closeModal = () => setOpen( false );
const [ isConfirmingDetach, setIsConfirmingDetach ] = useState( false );
const dropdownMenuProps = useToolsPanelDropdownMenuProps();

const { records: pages, hasResolved: hasResolvedPages } = useEntityRecords(
Expand Down Expand Up @@ -300,16 +317,10 @@ export default function PageListEdit( {

useEffect( () => {
if ( hasSelectedChild || hasDraggedChild ) {
openModal();
setIsConfirmingDetach( true );
selectBlock( parentClientId );
}
}, [
hasSelectedChild,
hasDraggedChild,
parentClientId,
selectBlock,
openModal,
] );
}, [ hasSelectedChild, hasDraggedChild, parentClientId, selectBlock ] );

return (
<>
Expand Down Expand Up @@ -349,18 +360,21 @@ export default function PageListEdit( {
) }

{ allowConvertToLinks && (
<div style={ { gridColumn: '1 / -1' } }>
<p>{ convertDescription }</p>
<Button
__next40pxDefaultSize
variant="primary"
accessibleWhenDisabled
disabled={ ! hasResolvedPages }
onClick={ convertToNavigationLinks }
>
{ __( 'Edit' ) }
</Button>
</div>
<Button
__next40pxDefaultSize
variant="secondary"
accessibleWhenDisabled
disabled={ ! hasResolvedPages }
onClick={ () => setIsConfirmingDetach( true ) }
// Not a ToolsPanelItem, so it needs to opt into
// spanning the panel's two-column grid.
style={ {
gridColumn: '1 / -1',
justifyContent: 'center',
} }
Comment thread
Mamaduka marked this conversation as resolved.
>
{ __( 'Detach Page List' ) }
</Button>
) }
</ToolsPanel>
</InspectorControls>
Expand All @@ -369,17 +383,23 @@ export default function PageListEdit( {
<>
<BlockControls group="other">
<ToolbarButton
title={ __( 'Edit' ) }
onClick={ openModal }
title={ __( 'Detach' ) }
disabled={ ! hasResolvedPages }
onClick={ () => setIsConfirmingDetach( true ) }
>
{ __( 'Edit' ) }
{ __( 'Detach' ) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional, non-blocking: Gallery has "Detach Gallery" for this button. Worth naming what gets detached here as well? E.g., "Detach Page List"

@andrewserong andrewserong Jul 30, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you probably meant this comment for the inspector controls button above rather than the toolbar button? In any case, yes I like the idea of "Detach Page List" here for the sidebar button, because of the placement of the button. It's next to the Parent control, so without the added block name in the label it might be ambiguous like the Detach button is for detaching the parent:

Image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh true! Thanks for calling that out. Proof I didn't rely on AI for this review - all dumb mistakes are my own. 😄

</ToolbarButton>
</BlockControls>
{ isOpen && (
<ConvertToLinksModal
onClick={ convertToNavigationLinks }
onClose={ closeModal }
disabled={ ! hasResolvedPages }
{ isConfirmingDetach && (
<DetachPageListDialog
// Converting a still-resolving list would map over
// an incomplete (or empty) set of pages.
isBusy={ ! hasResolvedPages }
onConfirm={ () => {
convertToNavigationLinks();
setIsConfirmingDetach( false );
} }
onCancel={ () => setIsConfirmingDetach( false ) }
/>
) }
</>
Expand Down
15 changes: 0 additions & 15 deletions packages/block-library/src/page-list/editor.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@use "@wordpress/base-styles/breakpoints" as *;
@use "@wordpress/base-styles/mixins" as *;
@use "@wordpress/base-styles/variables" as *;

.wp-block-navigation {
Expand Down Expand Up @@ -29,19 +27,6 @@
pointer-events: none;
}

// Modal that shows conversion option.
.wp-block-page-list-modal {
@include break-small() {
max-width: $break-mobile;
}
}

.wp-block-page-list-modal-buttons {
display: flex;
justify-content: flex-end;
gap: $grid-unit-15;
}

// Simulate open on click behaviour in the editor by opening on focus instead.

.wp-block-page-list {
Expand Down
25 changes: 14 additions & 11 deletions test/e2e/specs/editor/blocks/navigation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1541,24 +1541,27 @@ test.describe( 'Navigation block', () => {
const itemCount = await pageItems.count();
expect( itemCount ).toBeGreaterThan( 0 );

// Step 4: Convert Page List using Edit button
// Step 4: Convert Page List using the Detach button
// Select the Page List block
await editor.selectBlocks( pageListBlock );

// Try using the toolbar Edit button instead
const editButton = page
.getByRole( 'button', { name: 'Edit' } )
// Try using the toolbar Detach button instead
const detachButton = page
.getByRole( 'button', { name: 'Detach' } )
.first();
await expect( editButton ).toBeVisible();
await expect( detachButton ).toBeVisible();

await editButton.click();
await detachButton.click();

// Wait for modal and approve conversion
await expect(
page.getByRole( 'dialog', { name: 'Edit Page List' } )
).toBeVisible();
// Wait for the confirmation dialog and approve conversion
const detachDialog = page.getByRole( 'dialog', {
name: 'Detach Page List',
} );
await expect( detachDialog ).toBeVisible();

await page.getByRole( 'button', { name: 'Edit' } ).last().click();
await detachDialog
.getByRole( 'button', { name: 'Detach' } )
.click();

// Wait for conversion - check that Page List is gone
await expect( pageListBlock ).toBeHidden();
Expand Down
Loading