From 8410f6303be0cf31b1762284f80848e3fb3f72d1 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 19 Mar 2025 15:28:14 +0400 Subject: [PATCH 01/15] Navigation Link: Don't check validity when block editing is disabled (#69627) Co-authored-by: Mamaduka Co-authored-by: fabiankaegy --- packages/block-library/src/navigation-link/edit.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index e3018c274ef787..35357e0c8a9bba 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -26,6 +26,7 @@ import { store as blockEditorStore, getColorClassName, useInnerBlocksProps, + useBlockEditingMode, } from '@wordpress/block-editor'; import { isURL, prependHTTP, safeDecodeURI } from '@wordpress/url'; import { useState, useEffect, useRef } from '@wordpress/element'; @@ -99,15 +100,25 @@ const useIsInvalidLink = ( kind, type, id ) => { const isPostType = kind === 'post-type' || type === 'post' || type === 'page'; const hasId = Number.isInteger( id ); + const blockEditingMode = useBlockEditingMode(); + const postStatus = useSelect( ( select ) => { if ( ! isPostType ) { return null; } + + // Fetching the posts status is an "expensive" operation. Especially for sites with large navigations. + // When the block is rendered in a template or other disabled contexts we can skip this check in order + // to avoid all these additional requests that don't really add any value in that mode. + if ( blockEditingMode === 'disabled' ) { + return null; + } + const { getEntityRecord } = select( coreStore ); return getEntityRecord( 'postType', type, id )?.status; }, - [ isPostType, type, id ] + [ isPostType, blockEditingMode, type, id ] ); // Check Navigation Link validity if: From 4d35d12f910220c0ff5b0710db0586a20a6b6d60 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 19 Mar 2025 18:39:35 +0400 Subject: [PATCH 02/15] Navigation Link: Optimize 'getBlockParentsByBlockName' selector call (#69631) Co-authored-by: Mamaduka Co-authored-by: youknowriad --- packages/block-library/src/navigation-link/edit.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 35357e0c8a9bba..d4e8545070296b 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -44,6 +44,10 @@ import { updateAttributes } from './update-attributes'; import { getColors } from '../navigation/edit/utils'; const DEFAULT_BLOCK = { name: 'core/navigation-link' }; +const NESTING_BLOCK_NAMES = [ + 'core/navigation-link', + 'core/navigation-submenu', +]; /** * A React hook to determine if it's dragging within the target element. @@ -336,10 +340,8 @@ export default function NavigationLinkEdit( { return { isAtMaxNesting: - getBlockParentsByBlockName( clientId, [ - 'core/navigation-link', - 'core/navigation-submenu', - ] ).length >= maxNestingLevel, + getBlockParentsByBlockName( clientId, NESTING_BLOCK_NAMES ) + .length >= maxNestingLevel, isTopLevelLink: getBlockName( getBlockRootClientId( clientId ) ) === 'core/navigation', From 54572c02941d65d30ab581139af0d51cf707b2a7 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 24 Mar 2025 13:22:45 +0400 Subject: [PATCH 03/15] Navigation Link: Improve performance by only requesting entities when selected (#69633) * Navigation Link: Improve performance by only requesting entities when selected * Update selector logic for enabling link validation Unlinked contributors: dretzlaff. Co-authored-by: Mamaduka Co-authored-by: fabiankaegy Co-authored-by: t-hamano Co-authored-by: talldan Co-authored-by: jordesign --- .../block-library/src/navigation-link/edit.js | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index d4e8545070296b..5e12bc0931d298 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -100,7 +100,7 @@ const useIsDraggingWithin = ( elementRef ) => { return isDraggingWithin; }; -const useIsInvalidLink = ( kind, type, id ) => { +const useIsInvalidLink = ( kind, type, id, enabled ) => { const isPostType = kind === 'post-type' || type === 'post' || type === 'page'; const hasId = Number.isInteger( id ); @@ -115,14 +115,14 @@ const useIsInvalidLink = ( kind, type, id ) => { // Fetching the posts status is an "expensive" operation. Especially for sites with large navigations. // When the block is rendered in a template or other disabled contexts we can skip this check in order // to avoid all these additional requests that don't really add any value in that mode. - if ( blockEditingMode === 'disabled' ) { + if ( blockEditingMode === 'disabled' || ! enabled ) { return null; } const { getEntityRecord } = select( coreStore ); return getEntityRecord( 'postType', type, id )?.status; }, - [ isPostType, blockEditingMode, type, id ] + [ isPostType, blockEditingMode, enabled, type, id ] ); // Check Navigation Link validity if: @@ -295,8 +295,6 @@ export default function NavigationLinkEdit( { clientId, } ) { const { id, label, type, url, description, kind } = attributes; - - const [ isInvalid, isDraft ] = useIsInvalidLink( kind, type, id ); const { maxNestingLevel } = context; const { @@ -328,6 +326,7 @@ export default function NavigationLinkEdit( { isTopLevelLink, isParentOfSelectedBlock, hasChildren, + validateLinkStatus, } = useSelect( ( select ) => { const { @@ -336,26 +335,48 @@ export default function NavigationLinkEdit( { getBlockRootClientId, hasSelectedInnerBlock, getBlockParentsByBlockName, + getSelectedBlockClientId, } = select( blockEditorStore ); + const rootClientId = getBlockRootClientId( clientId ); + const isTopLevel = + getBlockName( rootClientId ) === 'core/navigation'; + const selectedBlockClientId = getSelectedBlockClientId(); + const rootNavigationClientId = isTopLevel + ? rootClientId + : getBlockParentsByBlockName( + clientId, + 'core/navigation' + )[ 0 ]; + + // Enable when the root Navigation block is selected or any of its inner blocks. + const enableLinkStatusValidation = + selectedBlockClientId === rootNavigationClientId || + hasSelectedInnerBlock( rootNavigationClientId, true ); return { isAtMaxNesting: getBlockParentsByBlockName( clientId, NESTING_BLOCK_NAMES ) .length >= maxNestingLevel, - isTopLevelLink: - getBlockName( getBlockRootClientId( clientId ) ) === - 'core/navigation', + isTopLevelLink: isTopLevel, isParentOfSelectedBlock: hasSelectedInnerBlock( clientId, true ), hasChildren: !! getBlockCount( clientId ), + validateLinkStatus: enableLinkStatusValidation, }; }, [ clientId, maxNestingLevel ] ); const { getBlocks } = useSelect( blockEditorStore ); + const [ isInvalid, isDraft ] = useIsInvalidLink( + kind, + type, + id, + validateLinkStatus + ); + /** * Transform to submenu block. */ From e8a0af6beecafe6e7e4b5ece7134f58bf7f1efcd Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 7 Apr 2025 10:45:59 +0400 Subject: [PATCH 04/15] Query Loop: Don't overwrite the 'query.inherit' attribute value (#69698) * Query Loop: Don't overwrite the 'query.inherit' attribute value * A misc adjustments * Display warning next to 'Query Type' control Unlinked contributors: genemma, cheestudio. Co-authored-by: Mamaduka Co-authored-by: fabiankaegy Co-authored-by: luminuu Co-authored-by: jeflopodev Co-authored-by: carolinan Co-authored-by: rinkalpagdar --- .../query/edit/inspector-controls/index.js | 84 +++++++++++-------- .../src/query/edit/query-content.js | 10 +-- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/packages/block-library/src/query/edit/inspector-controls/index.js b/packages/block-library/src/query/edit/inspector-controls/index.js index cf0b80b6145632..66ba962a074608 100644 --- a/packages/block-library/src/query/edit/inspector-controls/index.js +++ b/packages/block-library/src/query/edit/inspector-controls/index.js @@ -5,11 +5,12 @@ import { TextControl, SelectControl, RangeControl, - __experimentalToggleGroupControl as ToggleGroupControl, - __experimentalToggleGroupControlOption as ToggleGroupControlOption, Notice, + __experimentalVStack as VStack, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, + __experimentalToggleGroupControl as ToggleGroupControl, + __experimentalToggleGroupControlOption as ToggleGroupControlOption, } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; @@ -113,8 +114,7 @@ export default function QueryInspectorControls( props ) { }, [ querySearch, onChangeDebounced ] ); const orderByOptions = useOrderByOptions( postType ); - const showInheritControl = - ! isSingular && isControlAllowed( allowedControls, 'inherit' ); + const showInheritControl = isControlAllowed( allowedControls, 'inherit' ); const showPostTypeControl = ! inherit && isControlAllowed( allowedControls, 'postType' ); const postTypeControlLabel = __( 'Post type' ); @@ -185,6 +185,10 @@ export default function QueryInspectorControls( props ) { const showDisplayPanel = showPostCountControl || showOffSetControl || showPagesControl; + // The block cannot inherit a default WordPress query in singular content (e.g., post, page, 404, blank). + // Warn users but still permit this type of query for exceptional cases in Classic and Hybrid themes. + const hasInheritanceWarning = isSingular && inherit; + return ( <> { showSettingsPanel && ( @@ -208,36 +212,48 @@ export default function QueryInspectorControls( props ) { onDeselect={ () => setQuery( { inherit: true } ) } isShownByDefault > - { - setQuery( { - inherit: value === 'default', - } ); - } } - help={ - inherit - ? __( - 'Display a list of posts or custom post types based on the current template.' - ) - : __( - 'Display a list of posts or custom post types based on specific criteria.' - ) - } - value={ !! inherit ? 'default' : 'custom' } - > - - - + + { + setQuery( { + inherit: value === 'default', + } ); + } } + help={ + inherit + ? __( + 'Display a list of posts or custom post types based on the current template.' + ) + : __( + 'Display a list of posts or custom post types based on specific criteria.' + ) + } + value={ !! inherit ? 'default' : 'custom' } + > + + + + { hasInheritanceWarning && ( + + { __( + 'Cannot inherit the current template query when placed inside the singular content (e.g., post, page, 404, blank).' + ) } + + ) } + ) } diff --git a/packages/block-library/src/query/edit/query-content.js b/packages/block-library/src/query/edit/query-content.js index 459ce2af018c2a..f4a1d3d62e166d 100644 --- a/packages/block-library/src/query/edit/query-content.js +++ b/packages/block-library/src/query/edit/query-content.js @@ -98,21 +98,15 @@ export default function QueryContent( { } else if ( ! query.perPage && postsPerPage ) { newQuery.perPage = postsPerPage; } - // We need to reset the `inherit` value if in a singular template, as queries - // are not inherited when in singular content (e.g. post, page, 404, blank). - if ( isSingular && query.inherit ) { - newQuery.inherit = false; - } + if ( !! Object.keys( newQuery ).length ) { __unstableMarkNextChangeAsNotPersistent(); updateQuery( newQuery ); } }, [ query.perPage, - query.inherit, - postsPerPage, inherit, - isSingular, + postsPerPage, __unstableMarkNextChangeAsNotPersistent, updateQuery, ] ); From 5e80e98acbff7cb79baedb3fa71ca88821bcadcc Mon Sep 17 00:00:00 2001 From: Sukhendu Sekhar Guria Date: Fri, 28 Mar 2025 18:08:01 +0530 Subject: [PATCH 05/15] Block Editor: Remove truncation from media tab preview tooltips (#69741) Unlinked contributors: claimableperch. Co-authored-by: Sukhendu2002 Co-authored-by: Mamaduka Co-authored-by: t-hamano --- .../src/components/inserter/media-tab/media-preview.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/block-editor/src/components/inserter/media-tab/media-preview.js b/packages/block-editor/src/components/inserter/media-tab/media-preview.js index 319c25c01831c8..2b746d411214e9 100644 --- a/packages/block-editor/src/components/inserter/media-tab/media-preview.js +++ b/packages/block-editor/src/components/inserter/media-tab/media-preview.js @@ -36,7 +36,6 @@ import { getBlockAndPreviewFromMedia } from './utils'; import { store as blockEditorStore } from '../../../store'; const ALLOWED_MEDIA_TYPES = [ 'image' ]; -const MAXIMUM_TITLE_LENGTH = 25; const MEDIA_OPTIONS_POPOVER_PROPS = { position: 'bottom left', className: @@ -239,12 +238,6 @@ export function MediaPreview( { media, onClick, category } ) { ? media.title : media.title?.rendered || __( 'no title' ); - let truncatedTitle; - if ( title.length > MAXIMUM_TITLE_LENGTH ) { - const omission = '...'; - truncatedTitle = - title.slice( 0, MAXIMUM_TITLE_LENGTH - omission.length ) + omission; - } const onMouseEnter = useCallback( () => setIsHovered( true ), [] ); const onMouseLeave = useCallback( () => setIsHovered( false ), [] ); return ( @@ -268,7 +261,7 @@ export function MediaPreview( { media, onClick, category } ) { onMouseEnter={ onMouseEnter } onMouseLeave={ onMouseLeave } > - + Date: Tue, 29 Apr 2025 14:01:26 +0400 Subject: [PATCH 06/15] Image: Avoid stale URL when reselecting the same image from the library (#69985) Unlinked contributors: T4ngml. Co-authored-by: Mamaduka --- packages/block-library/src/image/edit.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index e08d0baff0503d..510e66a2676da4 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -262,10 +262,6 @@ export function ImageEdit( { additionalAttributes = { sizeSlug: newSize, }; - } else { - // Keep the same url when selecting the same file, so "Resolution" - // option is not changed. - additionalAttributes = { url }; } // Check if default link setting should be used. From 08682c6395a8bf5f5c2c38ee3c913a1ca6cfddd5 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Tue, 13 May 2025 21:29:37 +0400 Subject: [PATCH 07/15] Post Template: Don't rely on the default 'ignore_sticky' REST API value (#70020) Co-authored-by: Mamaduka --- packages/block-library/src/post-template/edit.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/post-template/edit.js b/packages/block-library/src/post-template/edit.js index 35d0a95501a1b2..0fbb16b00a8e97 100644 --- a/packages/block-library/src/post-template/edit.js +++ b/packages/block-library/src/post-template/edit.js @@ -181,14 +181,15 @@ export default function PostTemplateEdit( { * Handle cases where sticky is set to `exclude` or `only`. * Which works as a `post__in/post__not_in` query for sticky posts. */ - if ( sticky && sticky !== 'ignore' ) { + if ( [ 'exclude', 'only' ].includes( sticky ) ) { query.sticky = sticky === 'only'; } - if ( sticky === 'ignore' ) { + // Empty string represents the default behavior of including sticky posts. + if ( [ '', 'ignore' ].includes( sticky ) ) { // Remove any leftover sticky query parameter. delete query.sticky; - query.ignore_sticky = true; + query.ignore_sticky = sticky === 'ignore'; } // If `inherit` is truthy, adjust conditionally the query to create a better preview. From f52edfc2ff5d0d01dc41d5ce2b48a71962f7397f Mon Sep 17 00:00:00 2001 From: Mayank Tripathi <70465598+Mayank-Tripathi32@users.noreply.github.com> Date: Mon, 5 May 2025 14:13:23 +0530 Subject: [PATCH 08/15] MediaPlaceholder: Fix Regression with media URL input type to allow a local URL path (#70043) Co-authored-by: Mayank-Tripathi32 Co-authored-by: t-hamano Co-authored-by: Mamaduka Co-authored-by: skierpage --- .../block-editor/src/components/media-placeholder/index.js | 2 +- .../src/components/media-placeholder/style.scss | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js index 3d6578dcd39c24..7cea2cc725b6f3 100644 --- a/packages/block-editor/src/components/media-placeholder/index.js +++ b/packages/block-editor/src/components/media-placeholder/index.js @@ -47,7 +47,7 @@ const InsertFromURLPopover = ( { Date: Tue, 6 May 2025 19:15:23 +0900 Subject: [PATCH 09/15] DOM: Add summary element to focusable elements (#70051) Co-authored-by: t-hamano Co-authored-by: Mamaduka --- packages/dom/src/focusable.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dom/src/focusable.js b/packages/dom/src/focusable.js index ed0326fd264139..61b3c800da1853 100644 --- a/packages/dom/src/focusable.js +++ b/packages/dom/src/focusable.js @@ -39,6 +39,7 @@ function buildSelector( sequential ) { 'iframe:not([tabindex^="-"])', 'object', 'embed', + 'summary', 'area[href]', '[contenteditable]:not([contenteditable=false])', ].join( ',' ); From 48803db1ce466c57af8bc570472c5cd02319dab0 Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Thu, 22 May 2025 15:30:52 +0900 Subject: [PATCH 10/15] Button: Limit scope of width style for link (#70054) Co-authored-by: t-hamano Co-authored-by: Mamaduka Co-authored-by: luminuu --- packages/block-library/src/button/style.scss | 1 - packages/block-library/src/buttons/style.scss | 5 +++++ packages/block-library/src/post-comments-form/style.scss | 5 ----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/block-library/src/button/style.scss b/packages/block-library/src/button/style.scss index a8f625b95c3923..faae459d5497e0 100644 --- a/packages/block-library/src/button/style.scss +++ b/packages/block-library/src/button/style.scss @@ -10,7 +10,6 @@ $blocks-block__margin: 0.5em; word-break: break-word; // overflow-wrap doesn't work well if a link is wrapped in the div, so use word-break here. box-sizing: border-box; height: 100%; - width: 100%; align-content: center; &.aligncenter { diff --git a/packages/block-library/src/buttons/style.scss b/packages/block-library/src/buttons/style.scss index e563f3957f3746..fa9c96aef1710b 100644 --- a/packages/block-library/src/buttons/style.scss +++ b/packages/block-library/src/buttons/style.scss @@ -82,6 +82,11 @@ $blocks-block__margin: 0.5em; font-size: inherit; } } + + // Needed to make the buttons stretch correctly in a vertical layout. + .wp-block-button__link { + width: 100%; + } } // Legacy buttons that did not come in a wrapping container. diff --git a/packages/block-library/src/post-comments-form/style.scss b/packages/block-library/src/post-comments-form/style.scss index 69e6420581cb2f..b400849ba83bdb 100644 --- a/packages/block-library/src/post-comments-form/style.scss +++ b/packages/block-library/src/post-comments-form/style.scss @@ -82,9 +82,4 @@ margin-left: 0.5em; } } - - // Override the 100% width derived from the Button block - input[type="submit"] { - width: auto; - } } From cacd2c815ea29d8773608e21c193e2632ea8de7c Mon Sep 17 00:00:00 2001 From: Eshaan Dabasiya <76681468+im3dabasia@users.noreply.github.com> Date: Fri, 28 Mar 2025 12:59:32 +0530 Subject: [PATCH 11/15] Details: Enable all non-interactive formats (#68741) Co-authored-by: im3dabasia Co-authored-by: carolinan Co-authored-by: Mamaduka Co-authored-by: shimotmk --- packages/block-library/src/details/edit.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/details/edit.js b/packages/block-library/src/details/edit.js index 89221c96bb8ccb..693e0fb310add0 100644 --- a/packages/block-library/src/details/edit.js +++ b/packages/block-library/src/details/edit.js @@ -91,18 +91,16 @@ function DetailsEdit( { attributes, setAttributes } ) { ) } /> -
- { - event.preventDefault(); - setIsOpen( ! isOpen ); - } } - > +
setIsOpen( event.target.open ) } + > + From 595b9972dc6b07232e2d50a656e609b3f49a3ba7 Mon Sep 17 00:00:00 2001 From: Yogesh Bhutkar Date: Mon, 2 Jun 2025 11:46:59 +0530 Subject: [PATCH 12/15] Details Block: Fix keyboard accessibility issues and allow list view selection to show up inner blocks (#70056) * fix: use key down to open/close the details block * chore: fix import order * fix: do not conditionally render details children * feat: expose `withIgnoreIMEEvents` to private api * feat: wrap handler within `withIgnoreIMEEvents` * chore(test): add e2e test for details block * fix: prevent space in rich-text from toggling details * chore: add CHANGELOG * fix: wrap key up handler with `withIgnoreIMEEvents` * chore(e2e): updated tests * Adds using inner block content wherever required * Adds an e2e test to verify list view auto expand and focus * chore: move `CHANGELOG` to unreleased * chore: revert usage of `withIgnoreIMEEvents` * fix: apply `withIgnoreIMEEvents` to key down Manual testing with IMEs showed that the HOF can safely be applied just to the key down events * feat: add visually hidden instructions for expanding/collapsing details * fix: add `aria-describedby` and add it to RichText * fix: update aria-label for RichText * fix: remove the usage of `aria-describedby` * chore: move `CHANGELOG` to unreleased Unlinked contributors: armandovias. Co-authored-by: yogeshbhutkar Co-authored-by: Mamaduka Co-authored-by: t-hamano Co-authored-by: alexstine Co-authored-by: joedolson Co-authored-by: megane9988 --- packages/block-library/src/details/edit.js | 40 +++++- packages/components/CHANGELOG.md | 4 + packages/components/src/private-apis.ts | 2 + test/e2e/specs/editor/blocks/details.spec.js | 143 +++++++++++++++++++ 4 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 test/e2e/specs/editor/blocks/details.spec.js diff --git a/packages/block-library/src/details/edit.js b/packages/block-library/src/details/edit.js index 693e0fb310add0..1205b8fa099c9c 100644 --- a/packages/block-library/src/details/edit.js +++ b/packages/block-library/src/details/edit.js @@ -6,20 +6,26 @@ import { useBlockProps, useInnerBlocksProps, InspectorControls, + store as blockEditorStore, } from '@wordpress/block-editor'; import { TextControl, ToggleControl, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, + privateApis as componentsPrivateApis, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; +import { unlock } from '../lock-unlock'; + +const { withIgnoreIMEEvents } = unlock( componentsPrivateApis ); const TEMPLATE = [ [ @@ -30,7 +36,7 @@ const TEMPLATE = [ ], ]; -function DetailsEdit( { attributes, setAttributes } ) { +function DetailsEdit( { attributes, setAttributes, clientId } ) { const { name, showContent, summary, allowedBlocks, placeholder } = attributes; const blockProps = useBlockProps(); @@ -42,6 +48,27 @@ function DetailsEdit( { attributes, setAttributes } ) { const [ isOpen, setIsOpen ] = useState( showContent ); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); + // Check if the inner blocks are selected. + const hasSelectedInnerBlock = useSelect( + ( select ) => + select( blockEditorStore ).hasSelectedInnerBlock( clientId, true ), + [ clientId ] + ); + + const handleSummaryKeyDown = ( event ) => { + if ( event.key === 'Enter' && ! event.shiftKey ) { + setIsOpen( ( prevIsOpen ) => ! prevIsOpen ); + event.preventDefault(); + } + }; + + // Prevent spacebar from toggling
while typing. + const handleSummaryKeyUp = ( event ) => { + if ( event.key === ' ' ) { + event.preventDefault(); + } + }; + return ( <> @@ -93,13 +120,18 @@ function DetailsEdit( { attributes, setAttributes } ) {
setIsOpen( event.target.open ) } > - + { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'can toggle hidden blocks by pressing enter', async ( { + editor, + page, + } ) => { + // Insert a details block with empty inner blocks. + await editor.insertBlock( { + name: 'core/details', + attributes: { + summary: 'Details summary', + }, + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { + content: 'Details content', + }, + }, + ], + } ); + + // Open the details block. + await page.keyboard.press( 'Enter' ); + + // The inner block should be visible. + await expect( + editor.canvas.getByRole( 'document', { name: 'Block: Paragraph' } ) + ).toContainText( 'Details content' ); + + // Close the details block. + await page.keyboard.press( 'Enter' ); + + // The inner block should be hidden. + await expect( + editor.canvas.getByRole( 'document', { name: 'Block: Paragraph' } ) + ).toBeHidden(); + } ); + + test( 'can create a multiline summary with Shift+Enter', async ( { + editor, + page, + } ) => { + // Insert a details block. + await editor.insertBlock( { + name: 'core/details', + } ); + + const summary = editor.canvas.getByRole( 'textbox', { + name: 'Write summary', + } ); + + // Add a multiline summary. + await summary.type( 'First line' ); + await page.keyboard.press( 'Shift+Enter' ); + await summary.type( 'Second line' ); + + // Verify the summary is multiline. + await expect( summary ).toHaveText( 'First line\nSecond line', { + useInnerText: true, + } ); + } ); + + test( 'typing space in summary rich-text should not toggle details', async ( { + editor, + } ) => { + // Insert a details block. + await editor.insertBlock( { + name: 'core/details', + } ); + + const summary = editor.canvas.getByRole( 'textbox', { + name: 'Write summary', + } ); + + // Type space in the summary rich-text. + await summary.type( ' ' ); + + // Verify the details block is not toggled. + await expect( + editor.canvas.getByRole( 'document', { name: 'Empty block' } ) + ).toBeHidden(); + } ); + + test( 'selecting hidden blocks in list view expands details and focuses content', async ( { + editor, + page, + pageUtils, + } ) => { + // Insert a details block. + await editor.insertBlock( { + name: 'core/details', + attributes: { + summary: 'Details summary', + }, + innerBlocks: [ + { + name: 'core/paragraph', + attributes: { + content: 'Details content', + }, + }, + ], + } ); + + const listView = page.getByRole( 'treegrid', { + name: 'Block navigation structure', + } ); + + // Open the list view. + await pageUtils.pressKeys( 'access+o' ); + + // Verify inner blocks appear in the list view. + await page.keyboard.press( 'ArrowRight' ); + await expect( + listView.getByRole( 'link', { + name: 'Paragraph', + } ) + ).toBeVisible(); + + // Verify the first inner block in the list view is focused. + await page.keyboard.press( 'ArrowDown' ); + await expect( + listView.getByRole( 'link', { + name: 'Paragraph', + } ) + ).toBeFocused(); + + // Verify the first inner block in the editor canvas is focused. + await page.keyboard.press( 'Enter' ); + await expect( + editor.canvas.getByRole( 'document', { name: 'Block: Paragraph' } ) + ).toBeFocused(); + } ); +} ); From 4353d71b90d49c799814bcf8e3a2d09bd734500e Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 7 May 2025 18:17:16 +0400 Subject: [PATCH 13/15] URL: Handle HTML entities and ampersand in 'cleanForSlug' (#70078) Unlinked contributors: APCgit. Co-authored-by: Mamaduka Co-authored-by: tyxla Co-authored-by: Soean Co-authored-by: t-hamano Co-authored-by: 2ndkauboy Co-authored-by: annezazu Co-authored-by: marcarmengou Co-authored-by: tharsheblows --- packages/url/README.md | 2 +- packages/url/src/clean-for-slug.js | 6 +++++- packages/url/src/test/index.js | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/url/README.md b/packages/url/README.md index 0e4628669d11f2..ae5b6bf945fe5f 100644 --- a/packages/url/README.md +++ b/packages/url/README.md @@ -68,7 +68,7 @@ _Returns_ Performs some basic cleanup of a string for use as a post slug. -This replicates some of what `sanitize_title()` does in WordPress core, but is only designed to approximate what the slug will be. +This replicates some of what `sanitize_title_with_dashes()` does in WordPress core, but is only designed to approximate what the slug will be. Converts Latin-1 Supplement and Latin Extended-A letters to basic Latin letters. Removes combining diacritical marks. Converts whitespace, periods, and forward slashes to hyphens. Removes any remaining non-word characters except hyphens. Converts remaining string to lowercase. It does not account for octets, HTML entities, or other encoded characters. diff --git a/packages/url/src/clean-for-slug.js b/packages/url/src/clean-for-slug.js index 0cee207073c3d0..e288f5949537ad 100644 --- a/packages/url/src/clean-for-slug.js +++ b/packages/url/src/clean-for-slug.js @@ -6,7 +6,7 @@ import removeAccents from 'remove-accents'; /** * Performs some basic cleanup of a string for use as a post slug. * - * This replicates some of what `sanitize_title()` does in WordPress core, but + * This replicates some of what `sanitize_title_with_dashes()` does in WordPress core, but * is only designed to approximate what the slug will be. * * Converts Latin-1 Supplement and Latin Extended-A letters to basic Latin @@ -25,8 +25,12 @@ export function cleanForSlug( string ) { } return ( removeAccents( string ) + // Convert  , &ndash, and &mdash to hyphens. + .replace( /( |–|—)/g, '-' ) // Convert each group of whitespace, periods, and forward slashes to a hyphen. .replace( /[\s\./]+/g, '-' ) + // Remove all HTML entities. + .replace( /&\S+?;/g, '' ) // Remove anything that's not a letter, number, underscore or hyphen. .replace( /[^\p{L}\p{N}_-]+/gu, '' ) // Convert to lowercase diff --git a/packages/url/src/test/index.js b/packages/url/src/test/index.js index 763c2e71c9fcbe..0be7c7542b71c8 100644 --- a/packages/url/src/test/index.js +++ b/packages/url/src/test/index.js @@ -1202,6 +1202,24 @@ describe( 'cleanForSlug', () => { expect( cleanForSlug( 'the long - cat' ) ).toBe( 'the-long-cat' ); expect( cleanForSlug( 'the----long---cat' ) ).toBe( 'the-long-cat' ); } ); + + it( 'Should remove ampersands', () => { + expect( cleanForSlug( 'the long cat & dog' ) ).toBe( + 'the-long-cat-dog' + ); + expect( + cleanForSlug( 'the long cat & a dog && fish' ) + ).toBe( 'the-long-cat-a-dog-fish' ); + expect( cleanForSlug( 'the long cat &amp; dog' ) ).toBe( + 'the-long-cat-amp-dog' + ); + } ); + + it( 'Should remove HTML entities', () => { + expect( + cleanForSlug( 'No   Entities> – Here —<' ) + ).toBe( 'no-entities-here' ); + } ); } ); describe( 'normalizePath', () => { From a7e5cb59474402b1d2af40d56ef6617f7699158b Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Sat, 21 Jun 2025 11:21:43 +1000 Subject: [PATCH 14/15] Remove screencast.com embed block variation. (#70480) Co-authored-by: peterwilsoncc Co-authored-by: fabiankaegy Co-authored-by: swissspidy --- packages/block-library/src/embed/variations.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/block-library/src/embed/variations.js b/packages/block-library/src/embed/variations.js index 82a4159e13b327..662599f1688950 100644 --- a/packages/block-library/src/embed/variations.js +++ b/packages/block-library/src/embed/variations.js @@ -251,14 +251,6 @@ const variations = [ patterns: [ /^https?:\/\/(www\.)?reverbnation\.com\/.+/i ], attributes: { providerNameSlug: 'reverbnation', responsive: true }, }, - { - name: 'screencast', - title: getTitle( 'Screencast' ), - icon: embedVideoIcon, - description: __( 'Embed Screencast content.' ), - patterns: [ /^https?:\/\/(www\.)?screencast\.com\/.+/i ], - attributes: { providerNameSlug: 'screencast', responsive: true }, - }, { name: 'scribd', title: getTitle( 'Scribd' ), From a96bc3d2a12a4eae13bef9d7e62808b595f1a45a Mon Sep 17 00:00:00 2001 From: Aki Hamano <54422211+t-hamano@users.noreply.github.com> Date: Mon, 30 Jun 2025 09:56:49 +0900 Subject: [PATCH 15/15] Fix Floating UI packages dependencies (#70553) * Fix Floating UI packages dependencies * Pin @floating-ui/react-dom version --- package-lock.json | 58 +++++++++++++++----------------- packages/components/package.json | 2 +- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index 172310d695ecd2..462734073db274 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5124,32 +5124,42 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", - "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.1.1" + "@floating-ui/utils": "^0.2.10" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", - "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" + "@floating-ui/core": "^1.7.2", + "@floating-ui/utils": "^0.2.10" } }, - "node_modules/@floating-ui/dom/node_modules/@floating-ui/utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", - "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", - "license": "MIT" + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } }, "node_modules/@floating-ui/utils": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", - "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.2.4", @@ -50452,7 +50462,7 @@ "@emotion/serialize": "^1.0.2", "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", - "@floating-ui/react-dom": "^2.0.8", + "@floating-ui/react-dom": "2.0.8", "@types/gradient-parser": "0.1.3", "@types/highlight-words-core": "1.2.1", "@use-gesture/react": "^10.3.1", @@ -50499,18 +50509,6 @@ "react-dom": "^18.0.0" } }, - "packages/components/node_modules/@floating-ui/react-dom": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.1.tgz", - "integrity": "sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==", - "dependencies": { - "@floating-ui/dom": "^1.3.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, "packages/components/node_modules/@types/gradient-parser": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@types/gradient-parser/-/gradient-parser-0.1.3.tgz", diff --git a/packages/components/package.json b/packages/components/package.json index 5119fbabbea9cc..a7ef7c9c778f7a 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -40,7 +40,7 @@ "@emotion/serialize": "^1.0.2", "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", - "@floating-ui/react-dom": "^2.0.8", + "@floating-ui/react-dom": "2.0.8", "@types/gradient-parser": "0.1.3", "@types/highlight-words-core": "1.2.1", "@use-gesture/react": "^10.3.1",