From 9ec599804382de27b527866b54719929b7651511 Mon Sep 17 00:00:00 2001
From: Aki Hamano <54422211+t-hamano@users.noreply.github.com>
Date: Fri, 29 Nov 2024 22:06:55 +0900
Subject: [PATCH 01/52] Fix color of disabled buttons in dark toolbar (#67348)
Co-authored-by: t-hamano
Co-authored-by: jasmussen
---
packages/block-editor/src/components/block-tools/style.scss | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss
index 0ed9c138519d2..80fe4c420d1e1 100644
--- a/packages/block-editor/src/components/block-tools/style.scss
+++ b/packages/block-editor/src/components/block-tools/style.scss
@@ -157,6 +157,11 @@
&:focus::before {
box-shadow: inset 0 0 0 1px $gray-900, 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
}
+
+ &:disabled,
+ &[aria-disabled="true"] {
+ color: $gray-700;
+ }
}
.block-editor-block-parent-selector .block-editor-block-parent-selector__button {
From f189eab30fc68b71728e17b50a089b2aa42a2277 Mon Sep 17 00:00:00 2001
From: Aki Hamano <54422211+t-hamano@users.noreply.github.com>
Date: Fri, 29 Nov 2024 23:02:17 +0900
Subject: [PATCH 02/52] BorderBoxControl: Reduce gap value when unlinked
(#67049)
* BorderBoxControl: Reduce input field width when unlinked
* Update changelog
* 108px
* Reduce gap value
* Fix changelog entry
Co-authored-by: t-hamano
Co-authored-by: tyxla
Co-authored-by: jasmussen
Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: jameskoster
---
packages/components/CHANGELOG.md | 4 ++++
.../border-box-control-split-controls/component.tsx | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 8fc7aff329b03..ce151116df5bf 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -7,6 +7,10 @@
- `BoxControl`: Passive deprecate `onMouseOver`/`onMouseOut`. Pass to the `inputProps` prop instead ([#67332](https://github.com/WordPress/gutenberg/pull/67332)).
- `BoxControl`: Deprecate 36px default size ([#66704](https://github.com/WordPress/gutenberg/pull/66704)).
+### Enhancements
+
+- `BorderBoxControl`: Reduce gap value when unlinked ([#67049](https://github.com/WordPress/gutenberg/pull/67049)).
+
### Experimental
- `Menu`: throw when subcomponents are not rendered inside top level `Menu` ([#67411](https://github.com/WordPress/gutenberg/pull/67411)).
diff --git a/packages/components/src/border-box-control/border-box-control-split-controls/component.tsx b/packages/components/src/border-box-control/border-box-control-split-controls/component.tsx
index 0c887ab5f701c..94e1728076b18 100644
--- a/packages/components/src/border-box-control/border-box-control-split-controls/component.tsx
+++ b/packages/components/src/border-box-control/border-box-control-split-controls/component.tsx
@@ -73,7 +73,7 @@ const BorderBoxControlSplitControls = (
const mergedRef = useMergeRefs( [ setPopoverAnchor, forwardedRef ] );
return (
-
+
Date: Fri, 29 Nov 2024 23:48:46 +0900
Subject: [PATCH 03/52] FontFamilyControl: Restore margin bottom (#67424)
Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: tyxla
Co-authored-by: matiasbenedetto
---
.../block-editor/src/components/font-family/index.js | 9 +++++++++
.../block-editor/src/components/font-family/style.scss | 5 +++++
packages/block-editor/src/style.scss | 1 +
3 files changed, 15 insertions(+)
create mode 100644 packages/block-editor/src/components/font-family/style.scss
diff --git a/packages/block-editor/src/components/font-family/index.js b/packages/block-editor/src/components/font-family/index.js
index 1e6c137daedb0..045d4d5c73ed3 100644
--- a/packages/block-editor/src/components/font-family/index.js
+++ b/packages/block-editor/src/components/font-family/index.js
@@ -1,3 +1,8 @@
+/**
+ * External dependencies
+ */
+import clsx from 'clsx';
+
/**
* WordPress dependencies
*/
@@ -18,6 +23,7 @@ export default function FontFamilyControl( {
value = '',
onChange,
fontFamilies,
+ className,
...props
} ) {
const [ blockLevelFontFamilies ] = useSettings( 'typography.fontFamilies' );
@@ -59,6 +65,9 @@ export default function FontFamilyControl( {
value={ value }
onChange={ ( { selectedItem } ) => onChange( selectedItem.key ) }
options={ options }
+ className={ clsx( 'block-editor-font-family-control', className, {
+ 'is-next-has-no-margin-bottom': __nextHasNoMarginBottom,
+ } ) }
{ ...props }
/>
);
diff --git a/packages/block-editor/src/components/font-family/style.scss b/packages/block-editor/src/components/font-family/style.scss
new file mode 100644
index 0000000000000..7ee181ebb7953
--- /dev/null
+++ b/packages/block-editor/src/components/font-family/style.scss
@@ -0,0 +1,5 @@
+.block-editor-font-family-control {
+ &:not(.is-next-has-no-margin-bottom) {
+ margin-bottom: $grid-unit-10;
+ }
+}
diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss
index 213498c797aee..6b2ebf5cd841f 100644
--- a/packages/block-editor/src/style.scss
+++ b/packages/block-editor/src/style.scss
@@ -28,6 +28,7 @@
@import "./components/date-format-picker/style.scss";
@import "./components/duotone-control/style.scss";
@import "./components/font-appearance-control/style.scss";
+@import "./components/font-family/style.scss";
@import "./components/global-styles/style.scss";
@import "./components/grid/style.scss";
@import "./components/height-control/style.scss";
From 2c88f6a1b674c78bbbea5dc41aac998970ef9c2e Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Fri, 29 Nov 2024 15:16:15 +0000
Subject: [PATCH 04/52] Fix: Caption with Link in Wide-Width and Full-Width
Images Appears on two lines (#67392)
Co-authored-by: jorgefilipecosta
Co-authored-by: youknowriad
Co-authored-by: t-hamano
---
packages/block-library/src/image/style.scss | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss
index 1bb19bf29da69..a7fcb8f175e4e 100644
--- a/packages/block-library/src/image/style.scss
+++ b/packages/block-library/src/image/style.scss
@@ -42,8 +42,8 @@
text-align: center;
}
- &.alignfull a,
- &.alignwide a {
+ &.alignfull > a,
+ &.alignwide > a {
width: 100%;
}
From edd6328b3ff9cefc5550878f90fae885f33c8b27 Mon Sep 17 00:00:00 2001
From: Hit Bhalodia <58802366+hbhalodia@users.noreply.github.com>
Date: Fri, 29 Nov 2024 20:52:27 +0530
Subject: [PATCH 05/52] UnitControl : Deprecate 36px default size (#66791)
* Add the console warning for 36px size variation
* Add the changelog for the deprecation
* Update the unit test for the unitcontrol to use default 40px size
* Use __shouldNotWarnDeprecated36pxSize to not throw redundant warning from parent component used
* Add the missing prop for __next40pxDefaultSize on the index file and updated readme as well
* Add changelog to unreleased section
* Add __shouldNotWarnDeprecated36pxSize prop to supress console warning from child component
* Update tools panel storybook and docs to use __next40pxDefaultSize for UnitControl
* Updated the unit test to minimise the file changes
* Revert changes on mobile FontSizePicker
---------
Co-authored-by: hbhalodia
Co-authored-by: mirka <0mirka00@git.wordpress.org>
---
packages/components/CHANGELOG.md | 1 +
.../src/border-control/border-control/component.tsx | 2 ++
.../components/src/box-control/all-input-control.tsx | 1 +
.../src/box-control/axial-input-controls.tsx | 1 +
.../components/src/box-control/input-controls.tsx | 1 +
packages/components/src/font-size-picker/index.tsx | 1 +
.../src/tools-panel/stories/index.story.tsx | 12 ++++++++++++
.../components/src/tools-panel/tools-panel/README.md | 2 ++
.../src/tools-panel/tools-panel/component.tsx | 2 ++
packages/components/src/unit-control/README.md | 6 +++---
packages/components/src/unit-control/index.tsx | 11 ++++++++++-
.../src/unit-control/stories/index.story.tsx | 1 +
packages/components/src/unit-control/test/index.tsx | 6 +++++-
packages/components/src/unit-control/types.ts | 7 +++++++
14 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index ce151116df5bf..7865993d4e995 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -6,6 +6,7 @@
- `BoxControl`: Passive deprecate `onMouseOver`/`onMouseOut`. Pass to the `inputProps` prop instead ([#67332](https://github.com/WordPress/gutenberg/pull/67332)).
- `BoxControl`: Deprecate 36px default size ([#66704](https://github.com/WordPress/gutenberg/pull/66704)).
+- `UnitControl`: Deprecate 36px default size ([#66791](https://github.com/WordPress/gutenberg/pull/66791)).
### Enhancements
diff --git a/packages/components/src/border-control/border-control/component.tsx b/packages/components/src/border-control/border-control/component.tsx
index 2ba338c2bb30c..f71599b274778 100644
--- a/packages/components/src/border-control/border-control/component.tsx
+++ b/packages/components/src/border-control/border-control/component.tsx
@@ -75,6 +75,8 @@ const UnconnectedBorderControl = (
/>
= ( {
isShownByDefault
>
setWidth( next ) }
@@ -86,6 +87,7 @@ export const Default: StoryFn< typeof ToolsPanel > = ( {
isShownByDefault
>
setHeight( next ) }
@@ -98,6 +100,7 @@ export const Default: StoryFn< typeof ToolsPanel > = ( {
isShownByDefault
>
setMinHeight( next ) }
@@ -167,6 +170,7 @@ export const WithNonToolsPanelItems: StoryFn< typeof ToolsPanel > = ( {
isShownByDefault
>
setWidth( next ) }
@@ -179,6 +183,7 @@ export const WithNonToolsPanelItems: StoryFn< typeof ToolsPanel > = ( {
isShownByDefault
>
setHeight( next ) }
@@ -237,6 +242,7 @@ export const WithOptionalItemsPlusIcon: StoryFn< typeof ToolsPanel > = ( {
}
>
setMinWidth( next ) }
@@ -249,6 +255,7 @@ export const WithOptionalItemsPlusIcon: StoryFn< typeof ToolsPanel > = ( {
isShownByDefault={ false }
>
setWidth( next ) }
@@ -261,6 +268,7 @@ export const WithOptionalItemsPlusIcon: StoryFn< typeof ToolsPanel > = ( {
isShownByDefault={ false }
>
setHeight( next ) }
@@ -341,6 +349,7 @@ export const WithSlotFillItems: StoryFn< typeof ToolsPanel > = ( {
panelId={ panelId }
>
@@ -356,6 +365,7 @@ export const WithSlotFillItems: StoryFn< typeof ToolsPanel > = ( {
panelId={ panelId }
>
@@ -441,6 +451,7 @@ export const WithConditionalDefaultControl: StoryFn< typeof ToolsPanel > = ( {
isShownByDefault
>
@@ -541,6 +552,7 @@ export const WithConditionallyRenderedControl: StoryFn<
isShownByDefault
>
diff --git a/packages/components/src/tools-panel/tools-panel/README.md b/packages/components/src/tools-panel/tools-panel/README.md
index 1895f5ccc843e..b5e6860e2bd07 100644
--- a/packages/components/src/tools-panel/tools-panel/README.md
+++ b/packages/components/src/tools-panel/tools-panel/README.md
@@ -101,6 +101,7 @@ export function DimensionPanel() {
isShownByDefault
>
setHeight() }
* >
* setWidth() }
* >
* {
const [ value, setValue ] = useState( '10px' );
- return ;
+ return ;
};
```
@@ -128,7 +128,7 @@ const Example = () => {
];
return (
-
+
);
};
```
@@ -143,7 +143,7 @@ For example, a `value` of `50%` will set the current unit to `%`.
Example:
```jsx
-
+
```
- Required: No
diff --git a/packages/components/src/unit-control/index.tsx b/packages/components/src/unit-control/index.tsx
index 2dd08cc155225..9845c4eb04ef2 100644
--- a/packages/components/src/unit-control/index.tsx
+++ b/packages/components/src/unit-control/index.tsx
@@ -27,6 +27,7 @@ import { useControlledState } from '../utils/hooks';
import { escapeRegExp } from '../utils/strings';
import type { UnitControlProps, UnitControlOnChangeCallback } from './types';
import { useDeprecated36pxDefaultSizeProp } from '../utils/use-deprecated-props';
+import { maybeWarnDeprecated36pxSize } from '../utils/deprecated-36px-size';
function UnforwardedUnitControl(
unitControlProps: WordPressComponentProps<
@@ -55,9 +56,17 @@ function UnforwardedUnitControl(
units: unitsProp = CSS_UNITS,
value: valueProp,
onFocus: onFocusProp,
+ __shouldNotWarnDeprecated36pxSize,
...props
} = useDeprecated36pxDefaultSizeProp( unitControlProps );
+ maybeWarnDeprecated36pxSize( {
+ componentName: 'UnitControl',
+ __next40pxDefaultSize: props.__next40pxDefaultSize,
+ size,
+ __shouldNotWarnDeprecated36pxSize,
+ } );
+
if ( 'unit' in unitControlProps ) {
deprecated( 'UnitControl unit prop', {
since: '5.6',
@@ -246,7 +255,7 @@ function UnforwardedUnitControl(
* const Example = () => {
* const [ value, setValue ] = useState( '10px' );
*
- * return ;
+ * return ;
* };
* ```
*/
diff --git a/packages/components/src/unit-control/stories/index.story.tsx b/packages/components/src/unit-control/stories/index.story.tsx
index de8f476e26e5c..87628c4644116 100644
--- a/packages/components/src/unit-control/stories/index.story.tsx
+++ b/packages/components/src/unit-control/stories/index.story.tsx
@@ -59,6 +59,7 @@ export const Default: StoryFn< typeof UnitControl > = DefaultTemplate.bind(
);
Default.args = {
label: 'Label',
+ __next40pxDefaultSize: true,
};
/**
diff --git a/packages/components/src/unit-control/test/index.tsx b/packages/components/src/unit-control/test/index.tsx
index d91498d46478b..ad98d57cae640 100644
--- a/packages/components/src/unit-control/test/index.tsx
+++ b/packages/components/src/unit-control/test/index.tsx
@@ -12,9 +12,13 @@ import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
-import UnitControl from '..';
+import _UnitControl from '..';
import { CSS_UNITS, parseQuantityAndUnitFromRawValue } from '../utils';
+const UnitControl = ( props: React.ComponentProps< typeof _UnitControl > ) => (
+ <_UnitControl __next40pxDefaultSize { ...props } />
+);
+
const getInput = ( {
isInputTypeText = false,
}: {
diff --git a/packages/components/src/unit-control/types.ts b/packages/components/src/unit-control/types.ts
index 9164502668a2b..891945b422862 100644
--- a/packages/components/src/unit-control/types.ts
+++ b/packages/components/src/unit-control/types.ts
@@ -107,4 +107,11 @@ export type UnitControlProps = Pick< InputControlProps, 'size' > &
* Callback when either the quantity or the unit inputs gains focus.
*/
onFocus?: FocusEventHandler< HTMLInputElement | HTMLSelectElement >;
+ /**
+ * Do not throw a warning for the deprecated 36px default size.
+ * For internal components of other components that already throw the warning.
+ *
+ * @ignore
+ */
+ __shouldNotWarnDeprecated36pxSize?: boolean;
};
From 11262acadc190ca5715424728cafb1939d17483e Mon Sep 17 00:00:00 2001
From: Raj Patel <71687258+imrraaj@users.noreply.github.com>
Date: Fri, 29 Nov 2024 21:27:08 +0530
Subject: [PATCH 06/52] Pullquote block having design issue when
text-decoration is choosen strikethrough (#66707)
Co-authored-by: imrraaj
Co-authored-by: NidhiDhandhukiya74
Co-authored-by: jorgefilipecosta
---
packages/block-library/src/pullquote/style.scss | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/block-library/src/pullquote/style.scss b/packages/block-library/src/pullquote/style.scss
index b9e28b7cdcfaa..ff5fe6068dfac 100644
--- a/packages/block-library/src/pullquote/style.scss
+++ b/packages/block-library/src/pullquote/style.scss
@@ -72,4 +72,5 @@
.wp-block-pullquote cite {
color: inherit;
+ display: block;
}
From 9ffbd9c215b2b43e527355d83b8d3f313b68582f Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Fri, 29 Nov 2024 19:19:13 +0000
Subject: [PATCH 07/52] Block editor: try direct drag (outside text editable)
(#67305)
Co-authored-by: ellatrix
Co-authored-by: youknowriad
Co-authored-by: jasmussen
Co-authored-by: draganescu
---
.../components/block-draggable/content.scss | 16 ++-
.../src/components/block-list/block.js | 1 +
.../src/components/block-list/content.scss | 6 +
.../block-list/use-block-props/index.js | 7 +
.../use-firefox-draggable-compatibility.js | 25 ++++
.../use-selected-block-event-handlers.js | 120 ++++++++++++++++--
.../src/components/iframe/content.scss | 4 +
.../src/components/rich-text/index.js | 5 +
.../components/use-block-drop-zone/index.js | 19 ++-
.../writing-flow/use-drag-selection.js | 11 ++
packages/components/CHANGELOG.md | 4 +
.../components/src/resizable-box/index.tsx | 10 ++
.../components/src/resizable-box/style.scss | 8 ++
test/e2e/specs/editor/blocks/spacer.spec.js | 4 +-
.../block-bindings/custom-sources.spec.js | 10 +-
.../editor/various/draggable-blocks.spec.js | 100 ++++++++++-----
16 files changed, 300 insertions(+), 50 deletions(-)
create mode 100644 packages/block-editor/src/components/block-list/use-block-props/use-firefox-draggable-compatibility.js
diff --git a/packages/block-editor/src/components/block-draggable/content.scss b/packages/block-editor/src/components/block-draggable/content.scss
index 102230168e213..25a0f5c256595 100644
--- a/packages/block-editor/src/components/block-draggable/content.scss
+++ b/packages/block-editor/src/components/block-draggable/content.scss
@@ -1,13 +1,12 @@
// This creates a "slot" where the block you're dragging appeared.
// We use !important as one of the rules are meant to be overridden.
.block-editor-block-list__layout .is-dragging {
- background-color: currentColor !important;
- opacity: 0.05 !important;
+ opacity: 0.1 !important;
border-radius: $radius-small !important;
- // Disabling pointer events during the drag event is necessary,
- // lest the block might affect your drag operation.
- pointer-events: none !important;
+ iframe {
+ pointer-events: none;
+ }
// Hide the multi selection indicator when dragging.
&::selection {
@@ -18,3 +17,10 @@
content: none !important;
}
}
+
+// Images are draggable by default, so disable drag for them if not explicitly
+// set. This is done so that the block can capture the drag event instead.
+.wp-block img:not([draggable]),
+.wp-block svg:not([draggable]) {
+ pointer-events: none;
+}
diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js
index 6d4655189d972..0e3a5be5150de 100644
--- a/packages/block-editor/src/components/block-list/block.js
+++ b/packages/block-editor/src/components/block-list/block.js
@@ -797,6 +797,7 @@ function BlockListBlockProvider( props ) {
mayDisplayParentControls,
originalBlockClientId,
themeSupportsLayout,
+ canMove,
};
// Here we separate between the props passed to BlockListBlock and any other
diff --git a/packages/block-editor/src/components/block-list/content.scss b/packages/block-editor/src/components/block-list/content.scss
index 3d3b8517ca09c..cd517fced833e 100644
--- a/packages/block-editor/src/components/block-list/content.scss
+++ b/packages/block-editor/src/components/block-list/content.scss
@@ -427,3 +427,9 @@ _::-webkit-full-page-media, _:future, :root [data-has-multi-selection="true"] .b
// Additional -1px is required to avoid sub pixel rounding errors allowing background to show.
margin: 0 calc(-1 * var(--wp--style--root--padding-right) - 1px) 0 calc(-1 * var(--wp--style--root--padding-left) - 1px) !important;
}
+
+// This only works in Firefox, Chrome and Safari don't accept a custom cursor
+// during drag.
+.is-dragging {
+ cursor: grabbing;
+}
diff --git a/packages/block-editor/src/components/block-list/use-block-props/index.js b/packages/block-editor/src/components/block-list/use-block-props/index.js
index 25b9a21f0d286..4696149dc3875 100644
--- a/packages/block-editor/src/components/block-list/use-block-props/index.js
+++ b/packages/block-editor/src/components/block-list/use-block-props/index.js
@@ -30,6 +30,7 @@ import { useIntersectionObserver } from './use-intersection-observer';
import { useScrollIntoView } from './use-scroll-into-view';
import { useFlashEditableBlocks } from '../../use-flash-editable-blocks';
import { canBindBlock } from '../../../hooks/use-bindings-attributes';
+import { useFirefoxDraggableCompatibility } from './use-firefox-draggable-compatibility';
/**
* This hook is used to lightly mark an element as a block element. The element
@@ -100,11 +101,15 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
isTemporarilyEditingAsBlocks,
defaultClassName,
isSectionBlock,
+ canMove,
} = useContext( PrivateBlockContext );
+ const canDrag = canMove && ! hasChildSelected;
+
// translators: %s: Type of block (i.e. Text, Image etc)
const blockLabel = sprintf( __( 'Block: %s' ), blockTitle );
const htmlSuffix = mode === 'html' && ! __unstableIsHtml ? '-visual' : '';
+ const ffDragRef = useFirefoxDraggableCompatibility();
const mergedRefs = useMergeRefs( [
props.ref,
useFocusFirstElement( { clientId, initialPosition } ),
@@ -120,6 +125,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
isEnabled: isSectionBlock,
} ),
useScrollIntoView( { isSelected } ),
+ canDrag ? ffDragRef : undefined,
] );
const blockEditContext = useBlockEditContext();
@@ -152,6 +158,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
return {
tabIndex: blockEditingMode === 'disabled' ? -1 : 0,
+ draggable: canDrag ? true : undefined,
...wrapperProps,
...props,
ref: mergedRefs,
diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-firefox-draggable-compatibility.js b/packages/block-editor/src/components/block-list/use-block-props/use-firefox-draggable-compatibility.js
new file mode 100644
index 0000000000000..5fa07fb9be604
--- /dev/null
+++ b/packages/block-editor/src/components/block-list/use-block-props/use-firefox-draggable-compatibility.js
@@ -0,0 +1,25 @@
+/**
+ * WordPress dependencies
+ */
+import { useRefEffect } from '@wordpress/compose';
+
+/**
+ * In Firefox, the `draggable` and `contenteditable` attributes don't play well
+ * together. When `contenteditable` is within a `draggable` element, selection
+ * doesn't get set in the right place. The only solution is to temporarily
+ * remove the `draggable` attribute clicking inside `contenteditable` elements.
+ *
+ * @return {Function} Cleanup function.
+ */
+export function useFirefoxDraggableCompatibility() {
+ return useRefEffect( ( node ) => {
+ function onDown( event ) {
+ node.draggable = ! event.target.isContentEditable;
+ }
+ const { ownerDocument } = node;
+ ownerDocument.addEventListener( 'pointerdown', onDown );
+ return () => {
+ ownerDocument.removeEventListener( 'pointerdown', onDown );
+ };
+ }, [] );
+}
diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-selected-block-event-handlers.js b/packages/block-editor/src/components/block-list/use-block-props/use-selected-block-event-handlers.js
index 68f8a671adbe9..0a13ce6700b8e 100644
--- a/packages/block-editor/src/components/block-list/use-block-props/use-selected-block-event-handlers.js
+++ b/packages/block-editor/src/components/block-list/use-block-props/use-selected-block-event-handlers.js
@@ -5,12 +5,15 @@ import { isTextField } from '@wordpress/dom';
import { ENTER, BACKSPACE, DELETE } from '@wordpress/keycodes';
import { useSelect, useDispatch } from '@wordpress/data';
import { useRefEffect } from '@wordpress/compose';
+import { createRoot } from '@wordpress/element';
+import { store as blocksStore } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../../store';
import { unlock } from '../../../lock-unlock';
+import BlockDraggableChip from '../../../components/block-draggable/draggable-chip';
/**
* Adds block behaviour:
@@ -21,12 +24,16 @@ import { unlock } from '../../../lock-unlock';
* @param {string} clientId Block client ID.
*/
export function useEventHandlers( { clientId, isSelected } ) {
- const { getBlockRootClientId, getBlockIndex, isZoomOut } = unlock(
- useSelect( blockEditorStore )
- );
- const { insertAfterBlock, removeBlock, resetZoomLevel } = unlock(
- useDispatch( blockEditorStore )
- );
+ const { getBlockType } = useSelect( blocksStore );
+ const { getBlockRootClientId, isZoomOut, hasMultiSelection, getBlockName } =
+ unlock( useSelect( blockEditorStore ) );
+ const {
+ insertAfterBlock,
+ removeBlock,
+ resetZoomLevel,
+ startDraggingBlocks,
+ stopDraggingBlocks,
+ } = unlock( useDispatch( blockEditorStore ) );
return useRefEffect(
( node ) => {
@@ -76,7 +83,102 @@ export function useEventHandlers( { clientId, isSelected } ) {
* @param {DragEvent} event Drag event.
*/
function onDragStart( event ) {
- event.preventDefault();
+ if (
+ node !== event.target ||
+ node.isContentEditable ||
+ node.ownerDocument.activeElement !== node ||
+ hasMultiSelection()
+ ) {
+ event.preventDefault();
+ return;
+ }
+ const data = JSON.stringify( {
+ type: 'block',
+ srcClientIds: [ clientId ],
+ srcRootClientId: getBlockRootClientId( clientId ),
+ } );
+ event.dataTransfer.effectAllowed = 'move'; // remove "+" cursor
+ event.dataTransfer.clearData();
+ event.dataTransfer.setData( 'wp-blocks', data );
+ const { ownerDocument } = node;
+ const { defaultView } = ownerDocument;
+ const selection = defaultView.getSelection();
+ selection.removeAllRanges();
+
+ const domNode = document.createElement( 'div' );
+ const root = createRoot( domNode );
+ root.render(
+
+ );
+ document.body.appendChild( domNode );
+ domNode.style.position = 'absolute';
+ domNode.style.top = '0';
+ domNode.style.left = '0';
+ domNode.style.zIndex = '1000';
+ domNode.style.pointerEvents = 'none';
+
+ // Setting the drag chip as the drag image actually works, but
+ // the behaviour is slightly different in every browser. In
+ // Safari, it animates, in Firefox it's slightly transparent...
+ // So we set a fake drag image and have to reposition it
+ // ourselves.
+ const dragElement = ownerDocument.createElement( 'div' );
+ // Chrome will show a globe icon if the drag element does not
+ // have dimensions.
+ dragElement.style.width = '1px';
+ dragElement.style.height = '1px';
+ dragElement.style.position = 'fixed';
+ dragElement.style.visibility = 'hidden';
+ ownerDocument.body.appendChild( dragElement );
+ event.dataTransfer.setDragImage( dragElement, 0, 0 );
+
+ let offset = { x: 0, y: 0 };
+
+ if ( document !== ownerDocument ) {
+ const frame = defaultView.frameElement;
+ if ( frame ) {
+ const rect = frame.getBoundingClientRect();
+ offset = { x: rect.left, y: rect.top };
+ }
+ }
+
+ // chip handle offset
+ offset.x -= 58;
+
+ function over( e ) {
+ domNode.style.transform = `translate( ${
+ e.clientX + offset.x
+ }px, ${ e.clientY + offset.y }px )`;
+ }
+
+ over( event );
+
+ function end() {
+ ownerDocument.removeEventListener( 'dragover', over );
+ ownerDocument.removeEventListener( 'dragend', end );
+ domNode.remove();
+ dragElement.remove();
+ stopDraggingBlocks();
+ document.body.classList.remove(
+ 'is-dragging-components-draggable'
+ );
+ ownerDocument.documentElement.classList.remove(
+ 'is-dragging'
+ );
+ }
+
+ ownerDocument.addEventListener( 'dragover', over );
+ ownerDocument.addEventListener( 'dragend', end );
+ ownerDocument.addEventListener( 'drop', end );
+
+ startDraggingBlocks( [ clientId ] );
+ // Important because it hides the block toolbar.
+ document.body.classList.add(
+ 'is-dragging-components-draggable'
+ );
+ ownerDocument.documentElement.classList.add( 'is-dragging' );
}
node.addEventListener( 'keydown', onKeyDown );
@@ -91,11 +193,13 @@ export function useEventHandlers( { clientId, isSelected } ) {
clientId,
isSelected,
getBlockRootClientId,
- getBlockIndex,
insertAfterBlock,
removeBlock,
isZoomOut,
resetZoomLevel,
+ hasMultiSelection,
+ startDraggingBlocks,
+ stopDraggingBlocks,
]
);
}
diff --git a/packages/block-editor/src/components/iframe/content.scss b/packages/block-editor/src/components/iframe/content.scss
index 9b02716671de7..74efb63c0e077 100644
--- a/packages/block-editor/src/components/iframe/content.scss
+++ b/packages/block-editor/src/components/iframe/content.scss
@@ -60,5 +60,9 @@
}
}
}
+
+ .wp-block[draggable] {
+ cursor: grab;
+ }
}
}
diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js
index 8f179d08570ad..bc8eca6ea94d0 100644
--- a/packages/block-editor/src/components/rich-text/index.js
+++ b/packages/block-editor/src/components/rich-text/index.js
@@ -431,6 +431,11 @@ export function RichTextWrapper(
aria-multiline={ ! disableLineBreaks }
aria-readonly={ shouldDisableEditing }
{ ...props }
+ // Unset draggable (coming from block props) for contentEditable
+ // elements because it will interfere with multi block selection
+ // when the contentEditable and draggable elements are the same
+ // element.
+ draggable={ undefined }
aria-label={
bindingsLabel || props[ 'aria-label' ] || placeholder
}
diff --git a/packages/block-editor/src/components/use-block-drop-zone/index.js b/packages/block-editor/src/components/use-block-drop-zone/index.js
index 2a3e4948d40b3..221e5ab74ebb2 100644
--- a/packages/block-editor/src/components/use-block-drop-zone/index.js
+++ b/packages/block-editor/src/components/use-block-drop-zone/index.js
@@ -332,6 +332,7 @@ export default function useBlockDropZone( {
isGroupable,
isZoomOut,
getSectionRootClientId,
+ getBlockParents,
} = unlock( useSelect( blockEditorStore ) );
const {
showInsertionPoint,
@@ -358,13 +359,29 @@ export default function useBlockDropZone( {
// So, ensure that the drag state is set when the user drags over a drop zone.
startDragging();
}
+
+ const draggedBlockClientIds = getDraggedBlockClientIds();
+ const targetParents = [
+ targetRootClientId,
+ ...getBlockParents( targetRootClientId, true ),
+ ];
+
+ // Check if the target is within any of the dragged blocks.
+ const isTargetWithinDraggedBlocks = draggedBlockClientIds.some(
+ ( clientId ) => targetParents.includes( clientId )
+ );
+
+ if ( isTargetWithinDraggedBlocks ) {
+ return;
+ }
+
const allowedBlocks = getAllowedBlocks( targetRootClientId );
const targetBlockName = getBlockNamesByClientId( [
targetRootClientId,
] )[ 0 ];
const draggedBlockNames = getBlockNamesByClientId(
- getDraggedBlockClientIds()
+ draggedBlockClientIds
);
const isBlockDroppingAllowed = isDropTargetValid(
getBlockType,
diff --git a/packages/block-editor/src/components/writing-flow/use-drag-selection.js b/packages/block-editor/src/components/writing-flow/use-drag-selection.js
index 1569c45a7c676..ea4c09b3dc957 100644
--- a/packages/block-editor/src/components/writing-flow/use-drag-selection.js
+++ b/packages/block-editor/src/components/writing-flow/use-drag-selection.js
@@ -80,7 +80,17 @@ export default function useDragSelection() {
} );
}
+ let lastMouseDownTarget;
+
+ function onMouseDown( { target } ) {
+ lastMouseDownTarget = target;
+ }
+
function onMouseLeave( { buttons, target, relatedTarget } ) {
+ if ( ! target.contains( lastMouseDownTarget ) ) {
+ return;
+ }
+
// If we're moving into a child element, ignore. We're tracking
// the mouse leaving the element to a parent, no a child.
if ( target.contains( relatedTarget ) ) {
@@ -141,6 +151,7 @@ export default function useDragSelection() {
}
node.addEventListener( 'mouseout', onMouseLeave );
+ node.addEventListener( 'mousedown', onMouseDown );
return () => {
node.removeEventListener( 'mouseout', onMouseLeave );
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 7865993d4e995..2785447227416 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -21,6 +21,10 @@
- Upgraded `@ariakit/react` (v0.4.13) and `@ariakit/test` (v0.4.5) ([#65907](https://github.com/WordPress/gutenberg/pull/65907)).
- Upgraded `@ariakit/react` (v0.4.15) and `@ariakit/test` (v0.4.7) ([#67404](https://github.com/WordPress/gutenberg/pull/67404)).
+### Bug Fixes
+
+- `ResizableBox`: Make drag handles focusable ([#67305](https://github.com/WordPress/gutenberg/pull/67305)).
+
## 28.13.0 (2024-11-27)
### Deprecations
diff --git a/packages/components/src/resizable-box/index.tsx b/packages/components/src/resizable-box/index.tsx
index 1b05270ea0bf2..3bf3d36aa0d5c 100644
--- a/packages/components/src/resizable-box/index.tsx
+++ b/packages/components/src/resizable-box/index.tsx
@@ -112,6 +112,16 @@ function UnforwardedResizableBox(
showHandle && 'has-show-handle',
className
) }
+ // Add a focusable element within the drag handle. Unfortunately,
+ // `re-resizable` does not make them properly focusable by default,
+ // causing focus to move the the block wrapper which triggers block
+ // drag.
+ handleComponent={ Object.fromEntries(
+ Object.keys( HANDLE_CLASSES ).map( ( key ) => [
+ key,
+ ,
+ ] )
+ ) }
handleClasses={ HANDLE_CLASSES }
handleStyles={ HANDLE_STYLES }
ref={ ref }
diff --git a/packages/components/src/resizable-box/style.scss b/packages/components/src/resizable-box/style.scss
index 3c9efd2713646..4db3d27b5fab6 100644
--- a/packages/components/src/resizable-box/style.scss
+++ b/packages/components/src/resizable-box/style.scss
@@ -15,6 +15,14 @@ $resize-handler-container-size: $resize-handler-size + ($grid-unit-05 * 2); // M
.components-resizable-box__container.has-show-handle & {
display: block;
}
+
+ > div {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ z-index: z-index(".components-resizable-box__handle");
+ outline: none;
+ }
}
// Make the image inside the resize to get the full width
diff --git a/test/e2e/specs/editor/blocks/spacer.spec.js b/test/e2e/specs/editor/blocks/spacer.spec.js
index f089402514623..da262c9b4e26d 100644
--- a/test/e2e/specs/editor/blocks/spacer.spec.js
+++ b/test/e2e/specs/editor/blocks/spacer.spec.js
@@ -43,7 +43,9 @@ test.describe( 'Spacer', () => {
expect( await editor.getEditedPostContent() ).toMatchSnapshot();
await expect(
- editor.canvas.locator( 'role=document[name="Block: Spacer"i]' )
+ editor.canvas.locator(
+ 'role=document[name="Block: Spacer"i] >> css=.components-resizable-box__handle >> [tabindex]'
+ )
).toBeFocused();
} );
} );
diff --git a/test/e2e/specs/editor/various/block-bindings/custom-sources.spec.js b/test/e2e/specs/editor/various/block-bindings/custom-sources.spec.js
index d6563ce9cb5f5..033a69e2d6170 100644
--- a/test/e2e/specs/editor/various/block-bindings/custom-sources.spec.js
+++ b/test/e2e/specs/editor/various/block-bindings/custom-sources.spec.js
@@ -168,7 +168,10 @@ test.describe( 'Registered sources', () => {
name: 'Block: Image',
} )
.locator( 'img' );
- await imageBlockImg.click();
+ // Playwright will complain that the pointer events are captured by
+ // the parent, but that's fine.
+ // eslint-disable-next-line playwright/no-force-option
+ await imageBlockImg.click( { force: true } );
// Image src is the custom field value.
await expect( imageBlockImg ).toHaveAttribute(
@@ -735,7 +738,10 @@ test.describe( 'Registered sources', () => {
name: 'Block: Image',
} )
.locator( 'img' );
- await imageBlockImg.click();
+ // Playwright will complain that the pointer events are captured by
+ // the parent, but that's fine.
+ // eslint-disable-next-line playwright/no-force-option
+ await imageBlockImg.click( { force: true } );
// Edit the custom field value in the alt textarea.
const altInputArea = page
diff --git a/test/e2e/specs/editor/various/draggable-blocks.spec.js b/test/e2e/specs/editor/various/draggable-blocks.spec.js
index e08030191dd60..704817f4a2c38 100644
--- a/test/e2e/specs/editor/various/draggable-blocks.spec.js
+++ b/test/e2e/specs/editor/various/draggable-blocks.spec.js
@@ -18,6 +18,14 @@ test.use( {
},
} );
+async function dragTo( page, x, y ) {
+ // Call the move function twice to make sure the `dragOver` event is sent.
+ // @see https://github.com/microsoft/playwright/issues/17153
+ for ( let i = 0; i < 2; i += 1 ) {
+ await page.mouse.move( x, y );
+ }
+}
+
test.describe( 'Draggable block', () => {
test.beforeEach( async ( { admin } ) => {
await admin.createNewPost();
@@ -60,14 +68,7 @@ test.describe( 'Draggable block', () => {
'role=document[name="Block: Paragraph"i] >> text=1'
);
const firstParagraphBound = await firstParagraph.boundingBox();
- // Call the move function twice to make sure the `dragOver` event is sent.
- // @see https://github.com/microsoft/playwright/issues/17153
- for ( let i = 0; i < 2; i += 1 ) {
- await page.mouse.move(
- firstParagraphBound.x,
- firstParagraphBound.y
- );
- }
+ await dragTo( page, firstParagraphBound.x, firstParagraphBound.y );
await expect(
page.locator( 'data-testid=block-draggable-chip >> visible=true' )
@@ -132,15 +133,11 @@ test.describe( 'Draggable block', () => {
'role=document[name="Block: Paragraph"i] >> text=2'
);
const secondParagraphBound = await secondParagraph.boundingBox();
- // Call the move function twice to make sure the `dragOver` event is sent.
- // @see https://github.com/microsoft/playwright/issues/17153
- // Make sure mouse is > 30px within the block for bottom drop indicator to appear.
- for ( let i = 0; i < 2; i += 1 ) {
- await page.mouse.move(
- secondParagraphBound.x + 32,
- secondParagraphBound.y + secondParagraphBound.height * 0.75
- );
- }
+ await dragTo(
+ page,
+ secondParagraphBound.x + 32,
+ secondParagraphBound.y + secondParagraphBound.height * 0.75
+ );
await expect(
page.locator( 'data-testid=block-draggable-chip >> visible=true' )
@@ -216,14 +213,11 @@ test.describe( 'Draggable block', () => {
'role=document[name="Block: Paragraph"i] >> text=1'
);
const firstParagraphBound = await firstParagraph.boundingBox();
- // Call the move function twice to make sure the `dragOver` event is sent.
- // @see https://github.com/microsoft/playwright/issues/17153
- for ( let i = 0; i < 2; i += 1 ) {
- await page.mouse.move(
- firstParagraphBound.x + firstParagraphBound.width * 0.25,
- firstParagraphBound.y
- );
- }
+ await dragTo(
+ page,
+ firstParagraphBound.x + firstParagraphBound.width * 0.25,
+ firstParagraphBound.y
+ );
await expect(
page.locator( 'data-testid=block-draggable-chip >> visible=true' )
@@ -297,14 +291,11 @@ test.describe( 'Draggable block', () => {
'role=document[name="Block: Paragraph"i] >> text=2'
);
const secondParagraphBound = await secondParagraph.boundingBox();
- // Call the move function twice to make sure the `dragOver` event is sent.
- // @see https://github.com/microsoft/playwright/issues/17153
- for ( let i = 0; i < 2; i += 1 ) {
- await page.mouse.move(
- secondParagraphBound.x + secondParagraphBound.width * 0.75,
- secondParagraphBound.y
- );
- }
+ await dragTo(
+ page,
+ secondParagraphBound.x + secondParagraphBound.width * 0.75,
+ secondParagraphBound.y
+ );
await expect(
page.locator( 'data-testid=block-draggable-chip >> visible=true' )
@@ -465,4 +456,47 @@ test.describe( 'Draggable block', () => {
] );
}
} );
+
+ test( 'can directly drag an image', async ( { page, editor } ) => {
+ await editor.insertBlock( { name: 'core/image' } );
+ await editor.insertBlock( {
+ name: 'core/group',
+ attributes: { layout: { type: 'constrained' } },
+ innerBlocks: [ { name: 'core/paragraph' } ],
+ } );
+
+ const imageBlock = editor.canvas.getByRole( 'document', {
+ name: 'Block: Image',
+ } );
+
+ const groupBlock = editor.canvas.getByRole( 'document', {
+ name: 'Block: Group',
+ } );
+
+ await imageBlock.hover();
+ await page.mouse.down();
+ const groupBlockBox = await groupBlock.boundingBox();
+ await dragTo(
+ page,
+ groupBlockBox.x + groupBlockBox.width * 0.5,
+ groupBlockBox.y + groupBlockBox.height * 0.5
+ );
+ await page.mouse.up();
+
+ await expect.poll( editor.getBlocks ).toMatchObject( [
+ {
+ name: 'core/group',
+ attributes: {
+ tagName: 'div',
+ layout: { type: 'constrained' },
+ },
+ innerBlocks: [
+ {
+ name: 'core/image',
+ attributes: { alt: '', caption: '' },
+ },
+ ],
+ },
+ ] );
+ } );
} );
From 9defe9f7b6f7147756d1dba26dc0a8f8c07052d0 Mon Sep 17 00:00:00 2001
From: Ramon
Date: Sat, 30 Nov 2024 08:24:55 +1100
Subject: [PATCH 08/52] Fix misc type compilation errors in editor and block
editor packages (#67410)
Co-authored-by: ramonjd
Co-authored-by: Mamaduka
---
packages/components/CHANGELOG.md | 1 +
packages/components/src/index.ts | 1 +
packages/editor/README.md | 7 ++++---
packages/editor/src/components/post-trash/check.js | 6 +++---
packages/editor/src/components/post-url/index.js | 3 ++-
5 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 2785447227416..c9295e7ef861e 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -20,6 +20,7 @@
- Upgraded `@ariakit/react` (v0.4.13) and `@ariakit/test` (v0.4.5) ([#65907](https://github.com/WordPress/gutenberg/pull/65907)).
- Upgraded `@ariakit/react` (v0.4.15) and `@ariakit/test` (v0.4.7) ([#67404](https://github.com/WordPress/gutenberg/pull/67404)).
+- Exported the `WPCompleter` type as it was being used in block-editor/autocompleters ([#67410](https://github.com/WordPress/gutenberg/pull/67410)).
### Bug Fixes
diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts
index e82d6da70279e..0558584fe5418 100644
--- a/packages/components/src/index.ts
+++ b/packages/components/src/index.ts
@@ -108,6 +108,7 @@ export { Heading as __experimentalHeading } from './heading';
export { HStack as __experimentalHStack } from './h-stack';
export { default as Icon } from './icon';
export type { IconType } from './icon';
+export type { WPCompleter } from './autocomplete/types.ts';
export { default as IconButton } from './button/deprecated';
export {
ItemGroup as __experimentalItemGroup,
diff --git a/packages/editor/README.md b/packages/editor/README.md
index 36126cb8eaee3..8b48d773acb26 100644
--- a/packages/editor/README.md
+++ b/packages/editor/README.md
@@ -1462,11 +1462,11 @@ Wrapper component that renders its children only if the post can trashed.
_Parameters_
- _props_ `Object`: - The component props.
-- _props.children_ `React.ReactEl`: - The child components to render.
+- _props.children_ `React.ReactNode`: - The child components to render.
_Returns_
-- `React.ReactElement`: The rendered child components or null if the post can not trashed.
+- `React.ReactNode`: The rendered child components or null if the post can not trashed.
### PostTypeSupportCheck
@@ -1494,7 +1494,8 @@ _Usage_
_Parameters_
-- _onClose_ `Function`: Callback function to be executed when the popover is closed.
+- _props_ `{ onClose: () => void }`: The props for the component.
+- _props.onClose_ `() => void`: Callback function to be executed when the popover is closed.
_Returns_
diff --git a/packages/editor/src/components/post-trash/check.js b/packages/editor/src/components/post-trash/check.js
index 893d46ef9a086..d4a9139bfee05 100644
--- a/packages/editor/src/components/post-trash/check.js
+++ b/packages/editor/src/components/post-trash/check.js
@@ -13,10 +13,10 @@ import { GLOBAL_POST_TYPES } from '../../store/constants';
/**
* Wrapper component that renders its children only if the post can trashed.
*
- * @param {Object} props - The component props.
- * @param {React.ReactEl} props.children - The child components to render.
+ * @param {Object} props - The component props.
+ * @param {React.ReactNode} props.children - The child components to render.
*
- * @return {React.ReactElement} The rendered child components or null if the post can not trashed.
+ * @return {React.ReactNode} The rendered child components or null if the post can not trashed.
*/
export default function PostTrashCheck( { children } ) {
const { canTrashPost } = useSelect( ( select ) => {
diff --git a/packages/editor/src/components/post-url/index.js b/packages/editor/src/components/post-url/index.js
index c72ca5825f6fe..f55ac973be50e 100644
--- a/packages/editor/src/components/post-url/index.js
+++ b/packages/editor/src/components/post-url/index.js
@@ -32,7 +32,8 @@ import { store as editorStore } from '../../store';
*
* ```
*
- * @param {Function} onClose Callback function to be executed when the popover is closed.
+ * @param {{ onClose: () => void }} props The props for the component.
+ * @param {() => void} props.onClose Callback function to be executed when the popover is closed.
*
* @return {React.ReactNode} The rendered PostURL component.
*/
From 961000076e74a6eea419d79ff8e16ae54a73463b Mon Sep 17 00:00:00 2001
From: Manzoor Wani
Date: Fri, 29 Nov 2024 23:55:22 -0800
Subject: [PATCH 09/52] Add types for shortcode package (#67416)
* Add types for shortcode package
* Remove WP Prefix
* Update CHANGELOG.md
Co-authored-by: manzoorwanijk
Co-authored-by: gziolo
---
packages/shortcode/CHANGELOG.md | 4 +
packages/shortcode/README.md | 18 ++-
packages/shortcode/package.json | 1 +
packages/shortcode/src/index.js | 51 ++------
packages/shortcode/src/types.ts | 210 +++++++++++++++++++++++++++++++
packages/shortcode/tsconfig.json | 11 ++
tsconfig.json | 1 +
7 files changed, 245 insertions(+), 51 deletions(-)
create mode 100644 packages/shortcode/src/types.ts
create mode 100644 packages/shortcode/tsconfig.json
diff --git a/packages/shortcode/CHANGELOG.md b/packages/shortcode/CHANGELOG.md
index 8ee4e9aa3168d..2e461f1bc85c1 100644
--- a/packages/shortcode/CHANGELOG.md
+++ b/packages/shortcode/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+## Enhancements
+
+- The package now has built-in TypeScript definitions 🎉 ([#67416](https://github.com/WordPress/gutenberg/pull/67416))
+
## 4.13.0 (2024-11-27)
## 4.12.0 (2024-11-16)
diff --git a/packages/shortcode/README.md b/packages/shortcode/README.md
index a1a016e75e755..b6042ab284fe4 100644
--- a/packages/shortcode/README.md
+++ b/packages/shortcode/README.md
@@ -32,7 +32,7 @@ _Parameters_
_Returns_
-- `WPShortcodeAttrs`: Parsed shortcode attributes.
+- `import('./types').ShortcodeAttrs`: Parsed shortcode attributes.
### default
@@ -40,13 +40,9 @@ Creates a shortcode instance.
To access a raw representation of a shortcode, pass an `options` object, containing a `tag` string, a string or object of `attrs`, a string indicating the `type` of the shortcode ('single', 'self-closing', or 'closed'), and a `content` string.
-_Parameters_
-
-- _options_ `Object`: Options as described.
-
-_Returns_
+_Type_
-- `WPShortcode`: Shortcode instance.
+- `import('./types').shortcode`Shortcode instance.
### fromMatch
@@ -56,11 +52,11 @@ Accepts a `match` object from calling `regexp.exec()` on a `RegExp` generated by
_Parameters_
-- _match_ `Array`: Match array.
+- _match_ `import('./types').Match`: Match array.
_Returns_
-- `WPShortcode`: Shortcode instance.
+- `InstanceType`: Shortcode instance.
### next
@@ -74,7 +70,7 @@ _Parameters_
_Returns_
-- `WPShortcodeMatch | undefined`: Matched information.
+- `import('./types').ShortcodeMatch | undefined`: Matched information.
### regexp
@@ -108,7 +104,7 @@ _Parameters_
- _tag_ `string`: Shortcode tag.
- _text_ `string`: Text to search.
-- _callback_ `Function`: Function to process the match and return replacement string.
+- _callback_ `import('./types').ReplaceCallback`: Function to process the match and return replacement string.
_Returns_
diff --git a/packages/shortcode/package.json b/packages/shortcode/package.json
index f87fe06d9bb42..c5495364a03c4 100644
--- a/packages/shortcode/package.json
+++ b/packages/shortcode/package.json
@@ -26,6 +26,7 @@
"module": "build-module/index.js",
"react-native": "src/index",
"wpScript": true,
+ "types": "build-types",
"dependencies": {
"@babel/runtime": "7.25.7",
"memize": "^2.0.1"
diff --git a/packages/shortcode/src/index.js b/packages/shortcode/src/index.js
index 04e69c272378b..4d99086033e95 100644
--- a/packages/shortcode/src/index.js
+++ b/packages/shortcode/src/index.js
@@ -3,34 +3,7 @@
*/
import memize from 'memize';
-/**
- * Shortcode attributes object.
- *
- * @typedef {Object} WPShortcodeAttrs
- *
- * @property {Object} named Object with named attributes.
- * @property {Array} numeric Array with numeric attributes.
- */
-
-/**
- * Shortcode object.
- *
- * @typedef {Object} WPShortcode
- *
- * @property {string} tag Shortcode tag.
- * @property {WPShortcodeAttrs} attrs Shortcode attributes.
- * @property {string} content Shortcode content.
- * @property {string} type Shortcode type: `self-closing`,
- * `closed`, or `single`.
- */
-
-/**
- * @typedef {Object} WPShortcodeMatch
- *
- * @property {number} index Index the shortcode is found at.
- * @property {string} content Matched content.
- * @property {WPShortcode} shortcode Shortcode instance of the match.
- */
+export * from './types';
/**
* Find the next matching shortcode.
@@ -39,7 +12,7 @@ import memize from 'memize';
* @param {string} text Text to search.
* @param {number} index Index to start search from.
*
- * @return {WPShortcodeMatch | undefined} Matched information.
+ * @return {import('./types').ShortcodeMatch | undefined} Matched information.
*/
export function next( tag, text, index = 0 ) {
const re = regexp( tag );
@@ -81,10 +54,10 @@ export function next( tag, text, index = 0 ) {
/**
* Replace matching shortcodes in a block of text.
*
- * @param {string} tag Shortcode tag.
- * @param {string} text Text to search.
- * @param {Function} callback Function to process the match and return
- * replacement string.
+ * @param {string} tag Shortcode tag.
+ * @param {string} text Text to search.
+ * @param {import('./types').ReplaceCallback} callback Function to process the match and return
+ * replacement string.
*
* @return {string} Text with shortcodes replaced.
*/
@@ -169,7 +142,7 @@ export function regexp( tag ) {
*
* @param {string} text Serialised shortcode attributes.
*
- * @return {WPShortcodeAttrs} Parsed shortcode attributes.
+ * @return {import('./types').ShortcodeAttrs} Parsed shortcode attributes.
*/
export const attrs = memize( ( text ) => {
const named = {};
@@ -224,9 +197,9 @@ export const attrs = memize( ( text ) => {
* by `regexp()`. `match` can also be set to the `arguments` from a callback
* passed to `regexp.replace()`.
*
- * @param {Array} match Match array.
+ * @param {import('./types').Match} match Match array.
*
- * @return {WPShortcode} Shortcode instance.
+ * @return {InstanceType} Shortcode instance.
*/
export function fromMatch( match ) {
let type;
@@ -255,9 +228,7 @@ export function fromMatch( match ) {
* the `type` of the shortcode ('single', 'self-closing', or 'closed'), and a
* `content` string.
*
- * @param {Object} options Options as described.
- *
- * @return {WPShortcode} Shortcode instance.
+ * @type {import('./types').shortcode} Shortcode instance.
*/
const shortcode = Object.assign(
function ( options ) {
@@ -328,7 +299,7 @@ Object.assign( shortcode.prototype, {
* @param {(number|string)} attr Attribute key.
* @param {string} value Attribute value.
*
- * @return {WPShortcode} Shortcode instance.
+ * @return {InstanceType< import('./types').shortcode >} Shortcode instance.
*/
set( attr, value ) {
this.attrs[ typeof attr === 'number' ? 'numeric' : 'named' ][ attr ] =
diff --git a/packages/shortcode/src/types.ts b/packages/shortcode/src/types.ts
new file mode 100644
index 0000000000000..2b9ae084cc31a
--- /dev/null
+++ b/packages/shortcode/src/types.ts
@@ -0,0 +1,210 @@
+/**
+ * Shortcode attributes object.
+ */
+export type ShortcodeAttrs = {
+ /**
+ * Object with named attributes.
+ */
+ named: Record< string, string | undefined >;
+
+ /**
+ * Array with numeric attributes.
+ */
+ numeric: string[];
+};
+
+export type ShortcodeMatch = {
+ /**
+ * Index the shortcode is found at.
+ */
+ index: number;
+
+ /**
+ * Matched content.
+ */
+ content: string;
+
+ /**
+ * Shortcode instance of the match.
+ */
+ shortcode: Shortcode;
+};
+
+/**
+ * Shortcode options.
+ */
+export interface ShortcodeOptions {
+ /**
+ * Shortcode tag.
+ */
+ tag: string;
+
+ /**
+ * Shortcode attributes.
+ */
+ attrs?: Partial< ShortcodeAttrs > | string;
+
+ /**
+ * Shortcode content.
+ */
+ content?: string;
+
+ /**
+ * Shortcode type: `self-closing`, `closed`, or `single`.
+ */
+ type?: 'self-closing' | 'closed' | 'single';
+}
+
+/**
+ * Shortcode object.
+ */
+export interface Shortcode extends ShortcodeOptions {
+ /**
+ * Shortcode attributes.
+ */
+ attrs: ShortcodeAttrs;
+}
+
+export type Match =
+ | NonNullable< ReturnType< RegExp[ 'exec' ] > >
+ | Array< string >;
+
+export type ReplaceCallback = ( shortcode: Shortcode ) => string;
+
+/**
+ * WordPress Shortcode instance.
+ */
+export interface shortcode {
+ new ( options: Partial< ShortcodeOptions > ): Shortcode & {
+ /**
+ * Transform the shortcode into a string.
+ *
+ * @return {string} String representation of the shortcode.
+ */
+ string: () => string;
+
+ /**
+ * Get a shortcode attribute.
+ *
+ * Automatically detects whether `attr` is named or numeric and routes it
+ * accordingly.
+ *
+ * @param {(number|string)} attr Attribute key.
+ *
+ * @return {string} Attribute value.
+ */
+ get: ( attr: string | number ) => string | undefined;
+
+ /**
+ * Set a shortcode attribute.
+ *
+ * Automatically detects whether `attr` is named or numeric and routes it
+ * accordingly.
+ *
+ * @param {(number|string)} attr Attribute key.
+ * @param {string} value Attribute value.
+ *
+ * @return {InstanceType< shortcode >} Shortcode instance.
+ */
+ set: (
+ attr: string | number,
+ value: string
+ ) => InstanceType< shortcode >;
+ };
+
+ /**
+ * Parse shortcode attributes.
+ *
+ * Shortcodes accept many types of attributes. These can chiefly be divided into
+ * named and numeric attributes:
+ *
+ * Named attributes are assigned on a key/value basis, while numeric attributes
+ * are treated as an array.
+ *
+ * Named attributes can be formatted as either `name="value"`, `name='value'`,
+ * or `name=value`. Numeric attributes can be formatted as `"value"` or just
+ * `value`.
+ *
+ * @param text Serialised shortcode attributes.
+ *
+ * @return Parsed shortcode attributes.
+ */
+ attrs: ( text: string ) => ShortcodeAttrs;
+
+ /**
+ * Generate a Shortcode Object from a RegExp match.
+ *
+ * Accepts a `match` object from calling `regexp.exec()` on a `RegExp` generated
+ * by `regexp()`. `match` can also be set to the `arguments` from a callback
+ * passed to `regexp.replace()`.
+ *
+ * @param match Match array.
+ *
+ * @return Shortcode instance.
+ */
+ fromMatch: ( match: Match ) => InstanceType< shortcode >;
+
+ /**
+ * Find the next matching shortcode.
+ *
+ * @param tag Shortcode tag.
+ * @param text Text to search.
+ * @param index Index to start search from.
+ *
+ * @return Matched information.
+ */
+ next: (
+ tag: string,
+ text: string,
+ index?: number
+ ) => ShortcodeMatch | undefined;
+
+ /**
+ * Generate a RegExp to identify a shortcode.
+ *
+ * The base regex is functionally equivalent to the one found in
+ * `get_shortcode_regex()` in `wp-includes/shortcodes.php`.
+ *
+ * Capture groups:
+ *
+ * 1. An extra `[` to allow for escaping shortcodes with double `[[]]`
+ * 2. The shortcode name
+ * 3. The shortcode argument list
+ * 4. The self closing `/`
+ * 5. The content of a shortcode when it wraps some content.
+ * 6. The closing tag.
+ * 7. An extra `]` to allow for escaping shortcodes with double `[[]]`
+ *
+ * @param tag Shortcode tag.
+ *
+ * @return Shortcode RegExp.
+ */
+ regexp: ( tag: string ) => RegExp;
+
+ /**
+ * Replace matching shortcodes in a block of text.
+ *
+ * @param tag Shortcode tag.
+ * @param text Text to search.
+ * @param callback Function to process the match and return
+ * replacement string.
+ *
+ * @return Text with shortcodes replaced.
+ */
+ replace: ( tag: string, text: string, callback: ReplaceCallback ) => string;
+
+ /**
+ * Generate a string from shortcode parameters.
+ *
+ * Creates a shortcode instance and returns a string.
+ *
+ * Accepts the same `options` as the `shortcode()` constructor, containing a
+ * `tag` string, a string or object of `attrs`, a boolean indicating whether to
+ * format the shortcode using a `single` tag, and a `content` string.
+ *
+ * @param options
+ *
+ * @return String representation of the shortcode.
+ */
+ string: ( options: ShortcodeOptions ) => string;
+}
diff --git a/packages/shortcode/tsconfig.json b/packages/shortcode/tsconfig.json
new file mode 100644
index 0000000000000..79aa09d0ad56e
--- /dev/null
+++ b/packages/shortcode/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig.json",
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "declarationDir": "build-types",
+ "checkJs": false
+ },
+ "references": [],
+ "include": [ "src" ]
+}
diff --git a/tsconfig.json b/tsconfig.json
index 51bb7f2d68924..1010054ea512e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -50,6 +50,7 @@
{ "path": "packages/report-flaky-tests" },
{ "path": "packages/rich-text" },
{ "path": "packages/router" },
+ { "path": "packages/shortcode" },
{ "path": "packages/style-engine" },
{ "path": "packages/sync" },
{ "path": "packages/token-list" },
From d0383fef7532779a27afbd226c51b535232201d4 Mon Sep 17 00:00:00 2001
From: Jorge Costa
Date: Sun, 1 Dec 2024 02:04:43 +0000
Subject: [PATCH 10/52] Fix: Styles section does not moves stylebook to
typography. (#67423)
Co-authored-by: jorgefilipecosta
Co-authored-by: ramonjd
---
packages/edit-site/src/components/style-book/index.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/packages/edit-site/src/components/style-book/index.js b/packages/edit-site/src/components/style-book/index.js
index de4c38bd40c05..6a044d8055300 100644
--- a/packages/edit-site/src/components/style-book/index.js
+++ b/packages/edit-site/src/components/style-book/index.js
@@ -89,6 +89,11 @@ const scrollToSection = ( anchorId, iframe ) => {
*/
const getStyleBookNavigationFromPath = ( path ) => {
if ( path && typeof path === 'string' ) {
+ if ( path.startsWith( '/typography' ) ) {
+ return {
+ block: 'typography',
+ };
+ }
let block = path.includes( '/blocks/' )
? decodeURIComponent( path.split( '/blocks/' )[ 1 ] )
: null;
From 988f259c5c32d312bd09a25789c6b658069ccb90 Mon Sep 17 00:00:00 2001
From: Shail Mehta
Date: Sun, 1 Dec 2024 19:38:30 +0530
Subject: [PATCH 11/52] Updated old URL in Documentation (#67446)
* Updated Old URL
Co-authored-by: shail-mehta
Co-authored-by: Mamaduka
---
docs/reference-guides/block-api/block-transforms.md | 12 ++++++------
platform-docs/docs/create-block/transforms.md | 2 +-
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/docs/reference-guides/block-api/block-transforms.md b/docs/reference-guides/block-api/block-transforms.md
index c2c5ed49d1b19..9055ed0a3b45b 100644
--- a/docs/reference-guides/block-api/block-transforms.md
+++ b/docs/reference-guides/block-api/block-transforms.md
@@ -44,7 +44,7 @@ A transformation of type `block` is an object that takes the following parameter
- **transform** _(function)_: a callback that receives the attributes and inner blocks of the block being processed. It should return a block object or an array of block objects.
- **isMatch** _(function, optional)_: a callback that receives the block attributes as the first argument and the block object as the second argument and should return a boolean. Returning `false` from this function will prevent the transform from being available and displayed as an option to the user.
- **isMultiBlock** _(boolean, optional)_: whether the transformation can be applied when multiple blocks are selected. If true, the `transform` function's first parameter will be an array containing each selected block's attributes, and the second an array of each selected block's inner blocks. False by default.
-- **priority** _(number, optional)_: controls the priority with which a transformation is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://codex.wordpress.org/Plugin_API#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
+- **priority** _(number, optional)_: controls the priority with which a transformation is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://developer.wordpress.org/reference/#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
**Example: from Paragraph block to Heading block**
@@ -97,7 +97,7 @@ A transformation of type `enter` is an object that takes the following parameter
- **type** _(string)_: the value `enter`.
- **regExp** _(RegExp)_: the Regular Expression to use as a matcher. If the value matches, the transformation will be applied.
- **transform** _(function)_: a callback that receives an object with a `content` field containing the value that has been entered. It should return a block object or an array of block objects.
-- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://codex.wordpress.org/Plugin_API#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
+- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://developer.wordpress.org/reference/#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
**Example: from --- to Separator block**
@@ -124,7 +124,7 @@ A transformation of type `files` is an object that takes the following parameter
- **type** _(string)_: the value `files`.
- **transform** _(function)_: a callback that receives the array of files being processed. It should return a block object or an array of block objects.
- **isMatch** _(function, optional)_: a callback that receives the array of files being processed and should return a boolean. Returning `false` from this function will prevent the transform from being applied.
-- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://codex.wordpress.org/Plugin_API#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
+- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://developer.wordpress.org/reference/#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
**Example: from file to File block**
@@ -164,7 +164,7 @@ A transformation of type `prefix` is an object that takes the following paramete
- **type** _(string)_: the value `prefix`.
- **prefix** _(string)_: the character or sequence of characters that match this transform.
- **transform** _(function)_: a callback that receives the content introduced. It should return a block object or an array of block objects.
-- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://codex.wordpress.org/Plugin_API#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
+- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://developer.wordpress.org/reference/#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
**Example: from text to custom block**
@@ -197,7 +197,7 @@ A transformation of type `raw` is an object that takes the following parameters:
- **schema** _(object|function, optional)_: defines an [HTML content model](https://html.spec.whatwg.org/multipage/dom.html#content-models) used to detect and process pasted contents. See [below](#schemas-and-content-models).
- **selector** _(string, optional)_: a CSS selector string to determine whether the element matches according to the [element.matches](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches) method. The transform won't be executed if the element doesn't match. This is a shorthand and alternative to using `isMatch`, which, if present, will take precedence.
- **isMatch** _(function, optional)_: a callback that receives the node being processed and should return a boolean. Returning `false` from this function will prevent the transform from being applied.
-- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://codex.wordpress.org/Plugin_API#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
+- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://developer.wordpress.org/reference/#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
**Example: from URLs to Embed block**
@@ -273,7 +273,7 @@ A transformation of type `shortcode` is an object that takes the following param
- **transform** _(function, optional)_: a callback that receives the shortcode attributes as the first argument and the [WPShortcodeMatch](/packages/shortcode/README.md#next) as the second. It should return a block object or an array of block objects. When this parameter is defined, it will take precedence over the `attributes` parameter.
- **attributes** _(object, optional)_: object representing where the block attributes should be sourced from, according to the attributes shape defined by the [block configuration object](./block-registration.md). If a particular attribute contains a `shortcode` key, it should be a function that receives the shortcode attributes as the first arguments and the [WPShortcodeMatch](/packages/shortcode/README.md#next) as second, and returns a value for the attribute that will be sourced in the block's comment.
- **isMatch** _(function, optional)_: a callback that receives the shortcode attributes per the [Shortcode API](https://codex.wordpress.org/Shortcode_API) and should return a boolean. Returning `false` from this function will prevent the shortcode to be transformed into this block.
-- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://codex.wordpress.org/Plugin_API#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
+- **priority** _(number, optional)_: controls the priority with which a transform is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://developer.wordpress.org/reference/#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
**Example: from shortcode to block using `transform`**
diff --git a/platform-docs/docs/create-block/transforms.md b/platform-docs/docs/create-block/transforms.md
index fd235b669cd72..4a4118d6850b9 100644
--- a/platform-docs/docs/create-block/transforms.md
+++ b/platform-docs/docs/create-block/transforms.md
@@ -37,7 +37,7 @@ A transformation of type `block` is an object that takes the following parameter
- **transform** _(function)_: a callback that receives the attributes and inner blocks of the block being processed. It should return a block object or an array of block objects.
- **isMatch** _(function, optional)_: a callback that receives the block attributes as the first argument and the block object as the second argument and should return a boolean. Returning `false` from this function will prevent the transform from being available and displayed as an option to the user.
- **isMultiBlock** _(boolean, optional)_: whether the transformation can be applied when multiple blocks are selected. If `true`, the `transform` function's first parameter will be an array containing each selected block's attributes, and the second an array of each selected block's inner blocks. Returns `false` by default.
-- **priority** _(number, optional)_: controls the priority with which a transformation is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://codex.wordpress.org/Plugin_API#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
+- **priority** _(number, optional)_: controls the priority with which a transformation is applied, where a lower value will take precedence over higher values. This behaves much like a [WordPress hook](https://developer.wordpress.org/reference/#Hook_to_WordPress). Like hooks, the default priority is `10` when not otherwise set.
**Example: Let's declare a transform from our Gutenpride block to Heading block**
From 4775e7052b9e2ed7df46429e6e738de3faf2fb18 Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Sun, 1 Dec 2024 20:00:10 +0000
Subject: [PATCH 12/52] Preload: fix settings fields order (#67450)
Co-authored-by: ellatrix
Co-authored-by: Mamaduka
---
lib/compat/wordpress-6.8/preload.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/compat/wordpress-6.8/preload.php b/lib/compat/wordpress-6.8/preload.php
index ae6c738c6627c..6d92913b41411 100644
--- a/lib/compat/wordpress-6.8/preload.php
+++ b/lib/compat/wordpress-6.8/preload.php
@@ -31,9 +31,9 @@ function gutenberg_block_editor_preload_paths_6_8( $paths, $context ) {
'site_icon_url',
'site_logo',
'timezone_string',
- 'url',
'default_template_part_areas',
'default_template_types',
+ 'url',
)
);
$paths[] = '/wp/v2/templates/lookup?slug=front-page';
From c5b33b86f27dd6b6e3b4450b7c72cb949e4bd95b Mon Sep 17 00:00:00 2001
From: Sunil Prajapati <61308756+akasunil@users.noreply.github.com>
Date: Mon, 2 Dec 2024 10:59:13 +0530
Subject: [PATCH 13/52] Implement image size option for featured image in cover
block (#67273)
Co-authored-by: akasunil
Co-authored-by: aaronrobertshaw
Co-authored-by: carolinan
---
packages/block-library/src/cover/edit/index.js | 5 ++++-
.../src/cover/edit/inspector-controls.js | 13 ++++++++++---
packages/block-library/src/cover/index.php | 4 ++--
3 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/packages/block-library/src/cover/edit/index.js b/packages/block-library/src/cover/edit/index.js
index 3ad7039b55892..ced3097320329 100644
--- a/packages/block-library/src/cover/edit/index.js
+++ b/packages/block-library/src/cover/edit/index.js
@@ -120,7 +120,9 @@ function CoverEdit( {
select( coreStore ).getMedia( featuredImage, { context: 'view' } ),
[ featuredImage ]
);
- const mediaUrl = media?.source_url;
+ const mediaUrl =
+ media?.media_details?.sizes?.[ sizeSlug ]?.source_url ??
+ media?.source_url;
// User can change the featured image outside of the block, but we still
// need to update the block when that happens. This effect should only
@@ -451,6 +453,7 @@ function CoverEdit( {
toggleUseFeaturedImage={ toggleUseFeaturedImage }
updateDimRatio={ onUpdateDimRatio }
onClearMedia={ onClearMedia }
+ featuredImage={ media }
/>
);
diff --git a/packages/block-library/src/cover/edit/inspector-controls.js b/packages/block-library/src/cover/edit/inspector-controls.js
index c0807869ee1a5..b0d4b435163b7 100644
--- a/packages/block-library/src/cover/edit/inspector-controls.js
+++ b/packages/block-library/src/cover/edit/inspector-controls.js
@@ -96,6 +96,7 @@ export default function CoverInspectorControls( {
coverRef,
currentSettings,
updateDimRatio,
+ featuredImage,
} ) {
const {
useFeaturedImage,
@@ -132,8 +133,12 @@ export default function CoverInspectorControls( {
[ id, isImageBackground ]
);
+ const currentBackgroundImage = useFeaturedImage ? featuredImage : image;
+
function updateImage( newSizeSlug ) {
- const newUrl = image?.media_details?.sizes?.[ newSizeSlug ]?.source_url;
+ const newUrl =
+ currentBackgroundImage?.media_details?.sizes?.[ newSizeSlug ]
+ ?.source_url;
if ( ! newUrl ) {
return null;
}
@@ -146,7 +151,9 @@ export default function CoverInspectorControls( {
const imageSizeOptions = imageSizes
?.filter(
- ( { slug } ) => image?.media_details?.sizes?.[ slug ]?.source_url
+ ( { slug } ) =>
+ currentBackgroundImage?.media_details?.sizes?.[ slug ]
+ ?.source_url
)
?.map( ( { name, slug } ) => ( { value: slug, label: name } ) );
@@ -321,7 +328,7 @@ export default function CoverInspectorControls( {
/>
) }
- { ! useFeaturedImage && !! imageSizeOptions?.length && (
+ { !! imageSizeOptions?.length && (
Date: Mon, 2 Dec 2024 14:43:15 +0800
Subject: [PATCH 14/52] Fix List View not updating when switching editor modes
(#67379)
* Add back the editorTool dependency to getEnabledClientIdsTree
* Add e2e test
* Switch to getEditorMode selector
----
Co-authored-by: talldan
Co-authored-by: aaronrobertshaw
---
.../src/store/private-selectors.js | 8 +--
.../editor/various/write-design-mode.spec.js | 55 +++++++++++++++++++
2 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js
index 9779ae1300fb5..61b17a3625d15 100644
--- a/packages/block-editor/src/store/private-selectors.js
+++ b/packages/block-editor/src/store/private-selectors.js
@@ -109,16 +109,16 @@ function getEnabledClientIdsTreeUnmemoized( state, rootClientId ) {
*
* @return {Object[]} Tree of block objects with only clientID and innerBlocks set.
*/
-export const getEnabledClientIdsTree = createSelector(
- getEnabledClientIdsTreeUnmemoized,
- ( state ) => [
+export const getEnabledClientIdsTree = createRegistrySelector( ( select ) =>
+ createSelector( getEnabledClientIdsTreeUnmemoized, ( state ) => [
state.blocks.order,
state.derivedBlockEditingModes,
state.derivedNavModeBlockEditingModes,
state.blockEditingModes,
state.settings.templateLock,
state.blockListSettings,
- ]
+ select( STORE_NAME ).__unstableGetEditorMode( state ),
+ ] )
);
/**
diff --git a/test/e2e/specs/editor/various/write-design-mode.spec.js b/test/e2e/specs/editor/various/write-design-mode.spec.js
index 053f4cb8ff092..2892b4aea89e9 100644
--- a/test/e2e/specs/editor/various/write-design-mode.spec.js
+++ b/test/e2e/specs/editor/various/write-design-mode.spec.js
@@ -121,4 +121,59 @@ test.describe( 'Write/Design mode', () => {
editorSettings.getByRole( 'button', { name: 'Content' } )
).toBeVisible();
} );
+
+ test( 'hides the blocks that cannot be interacted with in List View', async ( {
+ editor,
+ page,
+ pageUtils,
+ } ) => {
+ await editor.setContent( '' );
+
+ // Insert a section with a nested block and an editable block.
+ await editor.insertBlock( {
+ name: 'core/group',
+ attributes: {},
+ innerBlocks: [
+ {
+ name: 'core/group',
+ attributes: {
+ metadata: {
+ name: 'Non-content block',
+ },
+ },
+ innerBlocks: [
+ {
+ name: 'core/paragraph',
+ attributes: {
+ content: 'Something',
+ },
+ },
+ ],
+ },
+ ],
+ } );
+
+ // Select the inner paragraph block so that List View is expanded.
+ await editor.canvas
+ .getByRole( 'document', {
+ name: 'Block: Paragraph',
+ } )
+ .click();
+
+ // Open List View.
+ await pageUtils.pressKeys( 'access+o' );
+ const listView = page.getByRole( 'treegrid', {
+ name: 'Block navigation structure',
+ } );
+ const nonContentBlock = listView.getByRole( 'link', {
+ name: 'Non-content block',
+ } );
+
+ await expect( nonContentBlock ).toBeVisible();
+
+ // Switch to write mode.
+ await editor.switchEditorTool( 'Write' );
+
+ await expect( nonContentBlock ).toBeHidden();
+ } );
} );
From d251f755481bdfe6eac99ab85f6410e3334622cc Mon Sep 17 00:00:00 2001
From: Eshaan Dabasiya <76681468+im3dabasia@users.noreply.github.com>
Date: Mon, 2 Dec 2024 14:10:03 +0530
Subject: [PATCH 15/52] Experiments: Remove trailing space in Color randomizer
(#67457)
Co-authored-by: im3dabasia
Co-authored-by: ramonjd
---
lib/experiments-page.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/experiments-page.php b/lib/experiments-page.php
index 9033e3c2d0c1f..256a185a3af69 100644
--- a/lib/experiments-page.php
+++ b/lib/experiments-page.php
@@ -69,7 +69,7 @@ function gutenberg_initialize_experiments_settings() {
add_settings_field(
'gutenberg-color-randomizer',
- __( 'Color randomizer ', 'gutenberg' ),
+ __( 'Color randomizer', 'gutenberg' ),
'gutenberg_display_experiment_field',
'gutenberg-experiments',
'gutenberg_experiments_section',
From 2f670d56405a6c43800e5a5068bb8ee75b9205d9 Mon Sep 17 00:00:00 2001
From: Yogesh Bhutkar
Date: Mon, 2 Dec 2024 15:30:08 +0530
Subject: [PATCH 16/52] Remove inline-block display from image anchor in
style.scss (#67368)
* Remove inline-block display from image anchor in style.scss
* Refactor: Set image anchor display to inline-block in style.scss
Co-authored-by: yogeshbhutkar
Co-authored-by: t-hamano
Co-authored-by: Infinite-Null
Co-authored-by: hanneslsm
Co-authored-by: frkly
---
packages/block-library/src/image/style.scss | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/block-library/src/image/style.scss b/packages/block-library/src/image/style.scss
index a7fcb8f175e4e..8ca5795cfd911 100644
--- a/packages/block-library/src/image/style.scss
+++ b/packages/block-library/src/image/style.scss
@@ -1,6 +1,7 @@
.wp-block-image {
- a {
+ > a,
+ > figure > a {
display: inline-block;
}
From 358fb8e04445ebf29140ba875e273bba8fd43913 Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Mon, 2 Dec 2024 10:07:39 +0000
Subject: [PATCH 17/52] Inserter: Patterns: remove loading indicator (#67072)
---
.../inserter/block-patterns-tab/index.js | 18 +-----------------
.../src/store/private-selectors.js | 15 ---------------
2 files changed, 1 insertion(+), 32 deletions(-)
diff --git a/packages/block-editor/src/components/inserter/block-patterns-tab/index.js b/packages/block-editor/src/components/inserter/block-patterns-tab/index.js
index 01e41111b7c89..45db4732aa9c6 100644
--- a/packages/block-editor/src/components/inserter/block-patterns-tab/index.js
+++ b/packages/block-editor/src/components/inserter/block-patterns-tab/index.js
@@ -3,9 +3,8 @@
*/
import { useState } from '@wordpress/element';
import { useViewportMatch } from '@wordpress/compose';
-import { Button, Spinner } from '@wordpress/components';
+import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
-import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
@@ -16,8 +15,6 @@ import { PatternCategoryPreviews } from './pattern-category-previews';
import { usePatternCategories } from './use-pattern-categories';
import CategoryTabs from '../category-tabs';
import InserterNoResults from '../no-results';
-import { store as blockEditorStore } from '../../../store';
-import { unlock } from '../../../lock-unlock';
function BlockPatternsTab( {
onSelectCategory,
@@ -31,19 +28,6 @@ function BlockPatternsTab( {
const categories = usePatternCategories( rootClientId );
const isMobile = useViewportMatch( 'medium', '<' );
- const isResolvingPatterns = useSelect(
- ( select ) =>
- unlock( select( blockEditorStore ) ).isResolvingPatterns(),
- []
- );
-
- if ( isResolvingPatterns ) {
- return (
-
-
-
- );
- }
if ( ! categories.length ) {
return ;
diff --git a/packages/block-editor/src/store/private-selectors.js b/packages/block-editor/src/store/private-selectors.js
index 61b17a3625d15..c46778d889b3e 100644
--- a/packages/block-editor/src/store/private-selectors.js
+++ b/packages/block-editor/src/store/private-selectors.js
@@ -406,21 +406,6 @@ export const getAllPatterns = createRegistrySelector( ( select ) =>
}, getAllPatternsDependants( select ) )
);
-export const isResolvingPatterns = createRegistrySelector( ( select ) =>
- createSelector( ( state ) => {
- const blockPatternsSelect = state.settings[ selectBlockPatternsKey ];
- const reusableBlocksSelect = state.settings[ reusableBlocksSelectKey ];
- return (
- ( blockPatternsSelect
- ? blockPatternsSelect( select ) === undefined
- : false ) ||
- ( reusableBlocksSelect
- ? reusableBlocksSelect( select ) === undefined
- : false )
- );
- }, getAllPatternsDependants( select ) )
-);
-
const EMPTY_ARRAY = [];
export const getReusableBlocks = createRegistrySelector(
From 22c43ff9e687eb5752b790316f2b1eeb99976dbb Mon Sep 17 00:00:00 2001
From: Dave Smith
Date: Mon, 2 Dec 2024 10:40:27 +0000
Subject: [PATCH 18/52] Disable Zoom Out if no section root to allow for Theme
opt in (#67232)
* Disable toolbar and auto inserter behaviour if no section root
* Remove useless coercion
Co-authored-by: Ramon
* Remove more coercion copy/paste
Co-authored-by: Ramon
* Add section root to Zoom Out e2e test
* Add test coverage
* Add some test coverage
* Try e2e test fix by reverting all template part changes in Theme
* Remove need to exit Zoom Out
* Ensure a main tag
* Update tests to expect the click-through behavior
* Simplify selection
---------
Co-authored-by: getdave
Co-authored-by: talldan
Co-authored-by: jeryj
Co-authored-by: ramonjd
Co-authored-by: draganescu
---
.../src/components/inserter/menu.js | 9 ++-
.../editor/src/components/header/index.js | 16 ++++-
.../editor/various/parsing-patterns.spec.js | 3 +-
.../editor/various/pattern-overrides.spec.js | 70 ++++++++++++++++---
test/e2e/specs/site-editor/zoom-out.spec.js | 45 +++++++++++-
5 files changed, 127 insertions(+), 16 deletions(-)
diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js
index 8dc2f64063c8e..ef260beb85906 100644
--- a/packages/block-editor/src/components/inserter/menu.js
+++ b/packages/block-editor/src/components/inserter/menu.js
@@ -58,6 +58,11 @@ function InserterMenu(
( select ) => unlock( select( blockEditorStore ) ).isZoomOut(),
[]
);
+ const hasSectionRootClientId = useSelect(
+ ( select ) =>
+ !! unlock( select( blockEditorStore ) ).getSectionRootClientId(),
+ []
+ );
const [ filterValue, setFilterValue, delayedFilterValue ] =
useDebouncedInput( __experimentalFilterValue );
const [ hoveredItem, setHoveredItem ] = useState( null );
@@ -81,7 +86,9 @@ function InserterMenu(
const [ selectedTab, setSelectedTab ] = useState( getInitialTab() );
const shouldUseZoomOut =
- selectedTab === 'patterns' || selectedTab === 'media';
+ hasSectionRootClientId &&
+ ( selectedTab === 'patterns' || selectedTab === 'media' );
+
useZoomOut( shouldUseZoomOut && isLargeViewport );
const [ destinationRootClientId, onInsertBlocks, onToggleInsertionPoint ] =
diff --git a/packages/editor/src/components/header/index.js b/packages/editor/src/components/header/index.js
index 51c341f2c1bd1..79199b15b1ad1 100644
--- a/packages/editor/src/components/header/index.js
+++ b/packages/editor/src/components/header/index.js
@@ -30,6 +30,7 @@ import {
PATTERN_POST_TYPE,
NAVIGATION_POST_TYPE,
} from '../../store/constants';
+import { unlock } from '../../lock-unlock';
const toolbarVariations = {
distractionFreeDisabled: { y: '-50px' },
@@ -102,6 +103,13 @@ function Header( {
( hasFixedToolbar &&
( ! hasBlockSelection || isBlockToolsCollapsed ) ) );
const hasBackButton = useHasBackButton();
+
+ const hasSectionRootClientId = useSelect(
+ ( select ) =>
+ !! unlock( select( blockEditorStore ) ).getSectionRootClientId(),
+ []
+ );
+
/*
* The edit-post-header classname is only kept for backward compatability
* as some plugins might be relying on its presence.
@@ -169,9 +177,11 @@ function Header( {
forceIsAutosaveable={ forceIsDirty }
/>
- { canBeZoomedOut && isWideViewport && (
-
- ) }
+ { canBeZoomedOut &&
+ isWideViewport &&
+ hasSectionRootClientId && (
+
+ ) }
{ ( isWideViewport || ! showIconLabels ) && (
diff --git a/test/e2e/specs/editor/various/parsing-patterns.spec.js b/test/e2e/specs/editor/various/parsing-patterns.spec.js
index d8edc544ffa03..98261804acb58 100644
--- a/test/e2e/specs/editor/various/parsing-patterns.spec.js
+++ b/test/e2e/specs/editor/various/parsing-patterns.spec.js
@@ -37,9 +37,8 @@ test.describe( 'Parsing patterns', () => {
} );
} );
- // Exit zoom out mode and select the inner buttons block to ensure
+ // Select the inner buttons block to ensure
// the correct insertion point is selected.
- await page.getByRole( 'button', { name: 'Zoom Out' } ).click();
await editor.selectBlocks(
editor.canvas.locator( 'role=document[name="Block: Button"i]' )
);
diff --git a/test/e2e/specs/editor/various/pattern-overrides.spec.js b/test/e2e/specs/editor/various/pattern-overrides.spec.js
index 7069b4cec258a..7d2be84187ef6 100644
--- a/test/e2e/specs/editor/various/pattern-overrides.spec.js
+++ b/test/e2e/specs/editor/various/pattern-overrides.spec.js
@@ -268,12 +268,25 @@ test.describe( 'Pattern Overrides', () => {
} );
await editor.setContent( '' );
+ await editor.switchEditorTool( 'Design' );
+ // Insert a `` group block.
+ // In zoomed out and write mode it acts as the section root.
+ // Inside is a pattern that acts as a section.
await editor.insertBlock( {
- name: 'core/block',
- attributes: { ref: id },
+ name: 'core/group',
+ attributes: { tagName: 'main' },
+ innerBlocks: [
+ {
+ name: 'core/block',
+ attributes: { ref: id },
+ },
+ ],
} );
+ const groupBlock = editor.canvas.getByRole( 'document', {
+ name: 'Block: Group',
+ } );
const patternBlock = editor.canvas.getByRole( 'document', {
name: 'Block: Pattern',
} );
@@ -290,14 +303,35 @@ test.describe( 'Pattern Overrides', () => {
hasText: 'No Overrides or Binding',
} );
- await test.step( 'Zoomed in / Design mode', async () => {
- await editor.switchEditorTool( 'Design' );
- // In zoomed in and design mode the pattern block and child blocks
- // with bindings are editable.
+ await test.step( 'Click-through behavior', async () => {
+ // With the group block selected, all the inner blocks of the pattern
+ // are inert due to the 'click-through' behavior, that requires the
+ // pattern block be selected first before its inner blocks are selectable.
+ await editor.selectBlocks( groupBlock );
await expect( patternBlock ).not.toHaveAttribute(
'inert',
'true'
);
+ await expect( blockWithOverrides ).toHaveAttribute(
+ 'inert',
+ 'true'
+ );
+ await expect( blockWithBindings ).toHaveAttribute(
+ 'inert',
+ 'true'
+ );
+ await expect( blockWithoutOverridesOrBindings ).toHaveAttribute(
+ 'inert',
+ 'true'
+ );
+ } );
+
+ await test.step( 'Zoomed in / Design mode', async () => {
+ await editor.selectBlocks( patternBlock );
+
+ // Once selected and in zoomed in/design mode the child blocks
+ // of the pattern with bindings are editable, but unbound
+ // blocks are inert.
await expect( blockWithOverrides ).not.toHaveAttribute(
'inert',
'true'
@@ -314,11 +348,16 @@ test.describe( 'Pattern Overrides', () => {
await test.step( 'Zoomed in / Write mode - pattern as a section', async () => {
await editor.switchEditorTool( 'Write' );
+
// The pattern block is still editable as a section.
await expect( patternBlock ).not.toHaveAttribute(
'inert',
'true'
);
+
+ // Ensure the pattern block is selected.
+ await editor.selectBlocks( patternBlock );
+
// Child blocks of the pattern with bindings are editable.
await expect( blockWithOverrides ).not.toHaveAttribute(
'inert',
@@ -336,11 +375,18 @@ test.describe( 'Pattern Overrides', () => {
await test.step( 'Zoomed out / Write mode - pattern as a section', async () => {
await page.getByLabel( 'Zoom Out' ).click();
- // In zoomed out only the pattern block is editable, as in this scenario it's a section.
+ // In zoomed out only the pattern block is editable,
+ // as in this scenario it's a section.
await expect( patternBlock ).not.toHaveAttribute(
'inert',
'true'
);
+
+ // Ensure the pattern block is selected before checking the child blocks
+ // to ensure the click-through behavior isn't interfering.
+ await editor.selectBlocks( patternBlock );
+
+ // None of the child blocks are editable in zoomed out mode.
await expect( blockWithOverrides ).toHaveAttribute(
'inert',
'true'
@@ -357,11 +403,17 @@ test.describe( 'Pattern Overrides', () => {
await test.step( 'Zoomed out / Design mode - pattern as a section', async () => {
await editor.switchEditorTool( 'Design' );
- // In zoomed out only the pattern block is editable, as in this scenario it's a section.
+ // In zoomed out only the pattern block is editable,
+ // as in this scenario it's a section.
await expect( patternBlock ).not.toHaveAttribute(
'inert',
'true'
);
+
+ // Ensure the pattern block is selected before checking the child blocks
+ // to ensure the click-through behavior isn't interfering.
+ await editor.selectBlocks( patternBlock );
+
await expect( blockWithOverrides ).toHaveAttribute(
'inert',
'true'
@@ -376,7 +428,7 @@ test.describe( 'Pattern Overrides', () => {
);
} );
- // Zoom out and group the pattern.
+ // Zoom out and group the pattern so that it's no longer a section.
await page.getByLabel( 'Zoom Out' ).click();
await editor.selectBlocks( patternBlock );
await editor.clickBlockOptionsMenuItem( 'Group' );
diff --git a/test/e2e/specs/site-editor/zoom-out.spec.js b/test/e2e/specs/site-editor/zoom-out.spec.js
index e698a94b7cf0d..77d121e199939 100644
--- a/test/e2e/specs/site-editor/zoom-out.spec.js
+++ b/test/e2e/specs/site-editor/zoom-out.spec.js
@@ -4,7 +4,8 @@
const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
const EDITOR_ZOOM_OUT_CONTENT = `
-
+
+
First Section Start
@@ -58,6 +59,21 @@ const EDITOR_ZOOM_OUT_CONTENT = `
Fourth Section End
+
+`;
+
+const EDITOR_ZOOM_OUT_CONTENT_NO_SECTION_ROOT = `
+
+
First Section Start
+
+
+
+
First Section Center
+
+
+
+
First Section End
+
`;
test.describe( 'Zoom Out', () => {
@@ -67,6 +83,8 @@ test.describe( 'Zoom Out', () => {
test.afterAll( async ( { requestUtils } ) => {
await requestUtils.activateTheme( 'twentytwentyone' );
+ await requestUtils.deleteAllTemplates( 'wp_template' );
+ await requestUtils.deleteAllTemplates( 'wp_template_part' );
} );
test.beforeEach( async ( { admin } ) => {
@@ -215,4 +233,29 @@ test.describe( 'Zoom Out', () => {
await expect( thirdSectionEnd ).toBeInViewport();
await expect( fourthSectionStart ).not.toBeInViewport();
} );
+
+ test( 'Zoom Out cannot be activated when the section root is missing', async ( {
+ page,
+ editor,
+ } ) => {
+ await editor.setContent( EDITOR_ZOOM_OUT_CONTENT_NO_SECTION_ROOT );
+
+ // Check that the Zoom Out toggle button is not visible.
+ await expect(
+ page.getByRole( 'button', { name: 'Zoom Out' } )
+ ).toBeHidden();
+
+ // Check that activating the Patterns tab in the Inserter does not activate
+ // Zoom Out.
+ await page
+ .getByRole( 'button', {
+ name: 'Block Inserter',
+ exact: true,
+ } )
+ .click();
+
+ await page.getByRole( 'tab', { name: 'Patterns' } ).click();
+
+ await expect( page.locator( '.is-zoomed-out' ) ).toBeHidden();
+ } );
} );
From e92d57743b68753ee47f9c21645524e2a5b86ea4 Mon Sep 17 00:00:00 2001
From: Andrea Fercia
Date: Mon, 2 Dec 2024 11:47:20 +0100
Subject: [PATCH 19/52] Remove one occurrence of incorrect usage of ItemGroup.
(#67427)
* Remove one occurrence of incorret usage of ItemGroup.
* Fix toggling visibility of the help text.
Co-authored-by: afercia
Co-authored-by: gziolo
Co-authored-by: Mamaduka
Co-authored-by: SantosGuillamot
---
packages/block-editor/src/hooks/block-bindings.js | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js
index 615804a311c0f..e10696cc1257d 100644
--- a/packages/block-editor/src/hooks/block-bindings.js
+++ b/packages/block-editor/src/hooks/block-bindings.js
@@ -300,13 +300,17 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => {
/>
) }
-
-
+ { /*
+ Use a div element to make the ToolsPanelHiddenInnerWrapper
+ toggle the visibility of this help text automatically.
+ */ }
+
+
{ __(
'Attributes connected to custom fields or other dynamic data.'
) }
-
-
+
+
);
From aef323a70e74fcf676036139bd25f657ad2a0b02 Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Mon, 2 Dec 2024 10:59:01 +0000
Subject: [PATCH 20/52] PR template: add before/after table (#62739)
Co-authored-by: ellatrix
Co-authored-by: t-hamano
---
.github/PULL_REQUEST_TEMPLATE.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index fd63e5e2e5312..69fd34d709bdc 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -20,3 +20,9 @@ https://github.com/WordPress/gutenberg/blob/trunk/CONTRIBUTING.md -->
## Screenshots or screencast
+
+
+
+|Before|After|
+|-|-|
+|||
From 8d343d155c7577d46aea33e708dceb39c571cc80 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Mon, 2 Dec 2024 12:26:27 +0100
Subject: [PATCH 21/52] Site Editor: Fix focus mode navigation (#67458)
Co-authored-by: youknowriad
Co-authored-by: Mamaduka
Co-authored-by: carolinan
---
.../use-navigate-to-entity-record.js | 2 +-
packages/router/src/router.tsx | 1 +
.../template-part-focus-mode.spec.js | 50 +++++++++++++++++++
3 files changed, 52 insertions(+), 1 deletion(-)
create mode 100644 test/e2e/specs/site-editor/template-part-focus-mode.spec.js
diff --git a/packages/edit-site/src/components/block-editor/use-navigate-to-entity-record.js b/packages/edit-site/src/components/block-editor/use-navigate-to-entity-record.js
index 8cc7fdaefe2d9..66be70fcd4e2e 100644
--- a/packages/edit-site/src/components/block-editor/use-navigate-to-entity-record.js
+++ b/packages/edit-site/src/components/block-editor/use-navigate-to-entity-record.js
@@ -17,7 +17,7 @@ export default function useNavigateToEntityRecord() {
const onNavigateToEntityRecord = useCallback(
( params ) => {
history.navigate(
- `/${ params.postType }/${ params.id }?canvas=edit&focusMode=true`
+ `/${ params.postType }/${ params.postId }?canvas=edit&focusMode=true`
);
},
[ history ]
diff --git a/packages/router/src/router.tsx b/packages/router/src/router.tsx
index 34cc542c7b573..2ac7974b4dfbc 100644
--- a/packages/router/src/router.tsx
+++ b/packages/router/src/router.tsx
@@ -146,6 +146,7 @@ export function useHistory() {
return useMemo(
() => ( {
navigate,
+ back: history.back,
} ),
[ navigate ]
);
diff --git a/test/e2e/specs/site-editor/template-part-focus-mode.spec.js b/test/e2e/specs/site-editor/template-part-focus-mode.spec.js
new file mode 100644
index 0000000000000..29e6788779ed9
--- /dev/null
+++ b/test/e2e/specs/site-editor/template-part-focus-mode.spec.js
@@ -0,0 +1,50 @@
+/**
+ * WordPress dependencies
+ */
+const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
+
+test.describe( 'Template Part Focus mode', () => {
+ test.beforeAll( async ( { requestUtils } ) => {
+ await requestUtils.activateTheme( 'twentytwentyfour' );
+ } );
+
+ test.afterEach( async ( { requestUtils } ) => {
+ await requestUtils.activateTheme( 'twentytwentyone' );
+ } );
+
+ test( 'Should navigate to template part and back.', async ( {
+ admin,
+ page,
+ editor,
+ } ) => {
+ await admin.visitAdminPage( 'site-editor.php?canvas=edit' );
+ await editor.setPreferences( 'core/edit-site', {
+ welcomeGuide: false,
+ } );
+
+ // Check that we're editing the template
+ await expect( page.locator( 'h1' ) ).toContainText( 'Blog Home' );
+ await expect( page.locator( 'h1' ) ).toContainText( 'Template' );
+
+ // Click Template Part
+ await editor.canvas
+ .getByRole( 'document', {
+ name: 'Header',
+ } )
+ .click();
+
+ // Navigate to Focus mode
+ await editor.clickBlockToolbarButton( 'Edit' );
+
+ // Check if focus mode is active
+ await expect( page.locator( 'h1' ) ).toContainText( 'Header' );
+ await expect( page.locator( 'h1' ) ).toContainText( 'Template Part' );
+
+ // Go back
+ await page.getByRole( 'button', { name: 'Back' } ).click();
+
+ // Check that we're editing the template
+ await expect( page.locator( 'h1' ) ).toContainText( 'Blog Home' );
+ await expect( page.locator( 'h1' ) ).toContainText( 'Template' );
+ } );
+} );
From 141e9cd884053db9baec26e43297162841f9e7a7 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Mon, 2 Dec 2024 12:51:58 +0100
Subject: [PATCH 22/52] Site editor: Allow access to quick edit (#67469)
Co-authored-by: youknowriad
Co-authored-by: jsnajdr
---
packages/edit-site/src/components/site-editor-routes/pages.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/edit-site/src/components/site-editor-routes/pages.js b/packages/edit-site/src/components/site-editor-routes/pages.js
index e8c55cd10307e..5f2a4e341e0dc 100644
--- a/packages/edit-site/src/components/site-editor-routes/pages.js
+++ b/packages/edit-site/src/components/site-editor-routes/pages.js
@@ -44,7 +44,7 @@ export const pagesRoute = {
mobile: ,
edit( { query } ) {
const hasQuickEdit =
- ( query.layout ?? 'list' ) === 'list' && !! query.quickEdit;
+ ( query.layout ?? 'list' ) !== 'list' && !! query.quickEdit;
return hasQuickEdit ? (
) : undefined;
@@ -59,7 +59,7 @@ export const pagesRoute = {
},
edit( { query } ) {
const hasQuickEdit =
- ( query.layout ?? 'list' ) === 'list' && !! query.quickEdit;
+ ( query.layout ?? 'list' ) !== 'list' && !! query.quickEdit;
return hasQuickEdit ? 380 : undefined;
},
},
From e07fe5cd9c17702e242df8ab547bddd1c4d79f52 Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Mon, 2 Dec 2024 12:01:28 +0000
Subject: [PATCH 23/52] Preload: parse post ID from p (path) (#67465)
Co-authored-by: ellatrix
Co-authored-by: youknowriad
---
backport-changelog/6.8/7695.md | 1 +
lib/compat/wordpress-6.8/preload.php | 12 ++++++++++--
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/backport-changelog/6.8/7695.md b/backport-changelog/6.8/7695.md
index 095c058e6fd10..f45b2039e3027 100644
--- a/backport-changelog/6.8/7695.md
+++ b/backport-changelog/6.8/7695.md
@@ -1,3 +1,4 @@
https://github.com/WordPress/wordpress-develop/pull/7695
* https://github.com/WordPress/gutenberg/pull/66631
+* https://github.com/WordPress/gutenberg/pull/67465
diff --git a/lib/compat/wordpress-6.8/preload.php b/lib/compat/wordpress-6.8/preload.php
index 6d92913b41411..494e3ad32ec02 100644
--- a/lib/compat/wordpress-6.8/preload.php
+++ b/lib/compat/wordpress-6.8/preload.php
@@ -10,8 +10,16 @@
*/
function gutenberg_block_editor_preload_paths_6_8( $paths, $context ) {
if ( 'core/edit-site' === $context->name ) {
- if ( ! empty( $_GET['postId'] ) ) {
- $route_for_post = rest_get_route_for_post( $_GET['postId'] );
+ $post_id = null;
+ if ( isset( $_GET['postId'] ) && is_numeric( $_GET['postId'] ) ) {
+ $post_id = (int) $_GET['postId'];
+ }
+ if ( isset( $_GET['p'] ) && preg_match( '/^\/page\/(\d+)$/', $_GET['p'], $matches ) ) {
+ $post_id = (int) $matches[1];
+ }
+
+ if ( $post_id ) {
+ $route_for_post = rest_get_route_for_post( $post_id );
if ( $route_for_post ) {
$paths[] = add_query_arg( 'context', 'edit', $route_for_post );
}
From 39a4d1c93fd3f9bee19db3566e92ce4a03e67544 Mon Sep 17 00:00:00 2001
From: Riad Benguella
Date: Mon, 2 Dec 2024 13:14:14 +0100
Subject: [PATCH 24/52] DataViews: Better handling of missing onClickItem prop
(#67402)
Co-authored-by: youknowriad
Co-authored-by: jsnajdr
---
.../src/components/dataviews-context/index.ts | 5 ++---
.../src/components/dataviews/index.tsx | 5 ++---
.../src/dataviews-layouts/grid/index.tsx | 14 +++++++-------
.../src/dataviews-layouts/table/index.tsx | 16 ++++++++--------
.../utils/get-clickable-item-props.ts | 19 ++++++++++++-------
packages/dataviews/src/types.ts | 2 +-
6 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/packages/dataviews/src/components/dataviews-context/index.ts b/packages/dataviews/src/components/dataviews-context/index.ts
index 19f6b4178b7b5..4bef3ecdbcbb4 100644
--- a/packages/dataviews/src/components/dataviews-context/index.ts
+++ b/packages/dataviews/src/components/dataviews-context/index.ts
@@ -26,7 +26,7 @@ type DataViewsContextType< Item > = {
openedFilter: string | null;
setOpenedFilter: ( openedFilter: string | null ) => void;
getItemId: ( item: Item ) => string;
- onClickItem: ( item: Item ) => void;
+ onClickItem?: ( item: Item ) => void;
isItemClickable: ( item: Item ) => boolean;
};
@@ -44,8 +44,7 @@ const DataViewsContext = createContext< DataViewsContextType< any > >( {
setOpenedFilter: () => {},
openedFilter: null,
getItemId: ( item ) => item.id,
- onClickItem: () => {},
- isItemClickable: () => false,
+ isItemClickable: () => true,
} );
export default DataViewsContext;
diff --git a/packages/dataviews/src/components/dataviews/index.tsx b/packages/dataviews/src/components/dataviews/index.tsx
index ee6073f40bf3d..99d9b6d684b08 100644
--- a/packages/dataviews/src/components/dataviews/index.tsx
+++ b/packages/dataviews/src/components/dataviews/index.tsx
@@ -52,8 +52,7 @@ type DataViewsProps< Item > = {
: { getItemId: ( item: Item ) => string } );
const defaultGetItemId = ( item: ItemWithId ) => item.id;
-const defaultIsItemClickable = () => false;
-const defaultOnClickItem = () => {};
+const defaultIsItemClickable = () => true;
const EMPTY_ARRAY: any[] = [];
export default function DataViews< Item >( {
@@ -70,7 +69,7 @@ export default function DataViews< Item >( {
defaultLayouts,
selection: selectionProperty,
onChangeSelection,
- onClickItem = defaultOnClickItem,
+ onClickItem,
isItemClickable = defaultIsItemClickable,
header,
}: DataViewsProps< Item > ) {
diff --git a/packages/dataviews/src/dataviews-layouts/grid/index.tsx b/packages/dataviews/src/dataviews-layouts/grid/index.tsx
index 2a09fb68efab8..17053e01604a5 100644
--- a/packages/dataviews/src/dataviews-layouts/grid/index.tsx
+++ b/packages/dataviews/src/dataviews-layouts/grid/index.tsx
@@ -31,7 +31,7 @@ interface GridItemProps< Item > {
selection: string[];
onChangeSelection: SetSelection;
getItemId: ( item: Item ) => string;
- onClickItem: ( item: Item ) => void;
+ onClickItem?: ( item: Item ) => void;
isItemClickable: ( item: Item ) => boolean;
item: Item;
actions: Action< Item >[];
@@ -66,19 +66,19 @@ function GridItem< Item >( {
) : null;
- const clickableMediaItemProps = getClickableItemProps(
+ const clickableMediaItemProps = getClickableItemProps( {
item,
isItemClickable,
onClickItem,
- 'dataviews-view-grid__media'
- );
+ className: 'dataviews-view-grid__media',
+ } );
- const clickablePrimaryItemProps = getClickableItemProps(
+ const clickablePrimaryItemProps = getClickableItemProps( {
item,
isItemClickable,
onClickItem,
- 'dataviews-view-grid__primary-field'
- );
+ className: 'dataviews-view-grid__primary-field',
+ } );
return (
{
field: NormalizedField< Item >;
item: Item;
isItemClickable: ( item: Item ) => boolean;
- onClickItem: ( item: Item ) => void;
+ onClickItem?: ( item: Item ) => void;
}
interface TableColumnCombinedProps< Item > {
@@ -52,7 +52,7 @@ interface TableColumnCombinedProps< Item > {
item: Item;
view: ViewTableType;
isItemClickable: ( item: Item ) => boolean;
- onClickItem: ( item: Item ) => void;
+ onClickItem?: ( item: Item ) => void;
}
interface TableColumnProps< Item > {
@@ -62,7 +62,7 @@ interface TableColumnProps< Item > {
column: string;
view: ViewTableType;
isItemClickable: ( item: Item ) => boolean;
- onClickItem: ( item: Item ) => void;
+ onClickItem?: ( item: Item ) => void;
}
interface TableRowProps< Item > {
@@ -77,7 +77,7 @@ interface TableRowProps< Item > {
getItemId: ( item: Item ) => string;
onChangeSelection: SetSelection;
isItemClickable: ( item: Item ) => boolean;
- onClickItem: ( item: Item ) => void;
+ onClickItem?: ( item: Item ) => void;
}
function TableColumn< Item >( {
@@ -118,12 +118,12 @@ function TableColumnField< Item >( {
const isItemClickableField = ( i: Item ) =>
isItemClickable( i ) && isPrimaryField;
- const clickableProps = getClickableItemProps(
+ const clickableProps = getClickableItemProps( {
item,
- isItemClickableField,
+ isItemClickable: isItemClickableField,
onClickItem,
- 'dataviews-view-table__cell-content'
- );
+ className: 'dataviews-view-table__cell-content',
+ } );
return (
(
- item: Item,
- isItemClickable: ( item: Item ) => boolean,
- onClickItem: ( item: Item ) => void,
- className: string
-) {
- if ( ! isItemClickable( item ) ) {
+export default function getClickableItemProps< Item >( {
+ item,
+ isItemClickable,
+ onClickItem,
+ className,
+}: {
+ item: Item;
+ isItemClickable: ( item: Item ) => boolean;
+ onClickItem?: ( item: Item ) => void;
+ className: string;
+} ) {
+ if ( ! isItemClickable( item ) || ! onClickItem ) {
return { className };
}
diff --git a/packages/dataviews/src/types.ts b/packages/dataviews/src/types.ts
index 861dc53404f91..0bce8b8cf2c08 100644
--- a/packages/dataviews/src/types.ts
+++ b/packages/dataviews/src/types.ts
@@ -510,7 +510,7 @@ export interface ViewBaseProps< Item > {
onChangeSelection: SetSelection;
selection: string[];
setOpenedFilter: ( fieldId: string ) => void;
- onClickItem: ( item: Item ) => void;
+ onClickItem?: ( item: Item ) => void;
isItemClickable: ( item: Item ) => boolean;
view: View;
}
From 340d617fa5cdab9b9402960de32c6496ef563d1d Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Mon, 2 Dec 2024 13:31:54 +0100
Subject: [PATCH 25/52] [mini] drag and drop: fix misplaced drop indicator
(#67434)
Co-authored-by: ellatrix
Co-authored-by: youknowriad
Co-authored-by: Mamaduka
Co-authored-by: ntsekouras
---
.../block-editor/src/components/block-popover/inbetween.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/packages/block-editor/src/components/block-popover/inbetween.js b/packages/block-editor/src/components/block-popover/inbetween.js
index 2ed9ee0bcb284..1d7c176673240 100644
--- a/packages/block-editor/src/components/block-popover/inbetween.js
+++ b/packages/block-editor/src/components/block-popover/inbetween.js
@@ -148,6 +148,10 @@ function BlockPopoverInbetween( {
? nextRect.left - previousRect.right
: 0;
}
+
+ // Avoid a negative width which happens when the next rect
+ // is on the next line.
+ width = Math.max( width, 0 );
}
return new window.DOMRect( left, top, width, height );
From c517e410017f4d65a7c6f03a31c5c2fa15cbbd65 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Greg=20Zi=C3=B3=C5=82kowski?=
Date: Mon, 2 Dec 2024 13:32:53 +0100
Subject: [PATCH 26/52] Extensibility: Make Block Bindings work with
`editor.BlockEdit` hook (#67370)
* Block Bindings: Move the place when the attributes get overriden
* Fix failing unit tests
* Wrap Edit with bindings logic only when the block supports it
* Extend the test plugin with `editor.BlockEdit` filter
* Add a new test covering the extensibility inside inspector controls
* Fix the issue with missing context established by sources
Co-authored-by: gziolo
Co-authored-by: SantosGuillamot
---
.../src/components/block-edit/edit.js | 12 +-
.../with-block-bindings-support.js} | 103 ++++--------------
.../block-list/use-block-props/index.js | 2 +-
.../src/components/rich-text/index.js | 2 +-
.../block-editor/src/hooks/block-bindings.js | 10 +-
packages/block-editor/src/hooks/index.js | 1 -
.../block-editor/src/utils/block-bindings.js | 37 +++++++
packages/e2e-tests/plugins/block-bindings.php | 6 +-
.../e2e-tests/plugins/block-bindings/index.js | 45 ++++++++
.../various/block-bindings/post-meta.spec.js | 41 +++++++
10 files changed, 165 insertions(+), 94 deletions(-)
rename packages/block-editor/src/{hooks/use-bindings-attributes.js => components/block-edit/with-block-bindings-support.js} (73%)
diff --git a/packages/block-editor/src/components/block-edit/edit.js b/packages/block-editor/src/components/block-edit/edit.js
index 83d0e3f406f82..6b1ddd86f4c76 100644
--- a/packages/block-editor/src/components/block-edit/edit.js
+++ b/packages/block-editor/src/components/block-edit/edit.js
@@ -18,6 +18,8 @@ import { useContext, useMemo } from '@wordpress/element';
* Internal dependencies
*/
import BlockContext from '../block-context';
+import { withBlockBindingsSupport } from './with-block-bindings-support';
+import { canBindBlock } from '../../utils/block-bindings';
/**
* Default value used for blocks which do not define their own context needs,
@@ -47,6 +49,8 @@ const Edit = ( props ) => {
const EditWithFilters = withFilters( 'editor.BlockEdit' )( Edit );
+const EditWithFiltersAndBindings = withBlockBindingsSupport( EditWithFilters );
+
const EditWithGeneratedProps = ( props ) => {
const { attributes = {}, name } = props;
const blockType = getBlockType( name );
@@ -67,8 +71,12 @@ const EditWithGeneratedProps = ( props ) => {
return null;
}
+ const EditComponent = canBindBlock( name )
+ ? EditWithFiltersAndBindings
+ : EditWithFilters;
+
if ( blockType.apiVersion > 1 ) {
- return ;
+ return ;
}
// Generate a class name for the block's editable form.
@@ -82,7 +90,7 @@ const EditWithGeneratedProps = ( props ) => {
);
return (
- ( props ) => {
const registry = useRegistry();
const blockContext = useContext( BlockContext );
@@ -108,9 +72,9 @@ export const withBlockBindingSupport = createHigherOrderComponent(
() =>
replacePatternOverrideDefaultBindings(
name,
- props.attributes.metadata?.bindings
+ props.attributes?.metadata?.bindings
),
- [ props.attributes.metadata?.bindings, name ]
+ [ props.attributes?.metadata?.bindings, name ]
);
// While this hook doesn't directly call any selectors, `useSelect` is
@@ -196,7 +160,7 @@ export const withBlockBindingSupport = createHigherOrderComponent(
const hasParentPattern = !! updatedContext[ 'pattern/overrides' ];
const hasPatternOverridesDefaultBinding =
- props.attributes.metadata?.bindings?.[ DEFAULT_ATTRIBUTE ]
+ props.attributes?.metadata?.bindings?.[ DEFAULT_ATTRIBUTE ]
?.source === 'core/pattern-overrides';
const _setAttributes = useCallback(
@@ -283,40 +247,13 @@ export const withBlockBindingSupport = createHigherOrderComponent(
);
return (
- <>
-
- >
+
);
},
'withBlockBindingSupport'
);
-
-/**
- * Filters a registered block's settings to enhance a block's `edit` component
- * to upgrade bound attributes.
- *
- * @param {WPBlockSettings} settings - Registered block settings.
- * @param {string} name - Block name.
- * @return {WPBlockSettings} Filtered block settings.
- */
-function shimAttributeSource( settings, name ) {
- if ( ! canBindBlock( name ) ) {
- return settings;
- }
-
- return {
- ...settings,
- edit: withBlockBindingSupport( settings.edit ),
- };
-}
-
-addFilter(
- 'blocks.registerBlockType',
- 'core/editor/custom-sources-backwards-compatibility/shim-attribute-source',
- shimAttributeSource
-);
diff --git a/packages/block-editor/src/components/block-list/use-block-props/index.js b/packages/block-editor/src/components/block-list/use-block-props/index.js
index 4696149dc3875..7e50b75e1b956 100644
--- a/packages/block-editor/src/components/block-list/use-block-props/index.js
+++ b/packages/block-editor/src/components/block-list/use-block-props/index.js
@@ -29,7 +29,7 @@ import { useBlockRefProvider } from './use-block-refs';
import { useIntersectionObserver } from './use-intersection-observer';
import { useScrollIntoView } from './use-scroll-into-view';
import { useFlashEditableBlocks } from '../../use-flash-editable-blocks';
-import { canBindBlock } from '../../../hooks/use-bindings-attributes';
+import { canBindBlock } from '../../../utils/block-bindings';
import { useFirefoxDraggableCompatibility } from './use-firefox-draggable-compatibility';
/**
diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js
index bc8eca6ea94d0..768ffbb0cdd2d 100644
--- a/packages/block-editor/src/components/rich-text/index.js
+++ b/packages/block-editor/src/components/rich-text/index.js
@@ -39,7 +39,7 @@ import FormatEdit from './format-edit';
import { getAllowedFormats } from './utils';
import { Content, valueToHTMLString } from './content';
import { withDeprecations } from './with-deprecations';
-import { canBindBlock } from '../../hooks/use-bindings-attributes';
+import { canBindBlock } from '../../utils/block-bindings';
import BlockContext from '../block-context';
export const keyboardShortcutContext = createContext();
diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js
index e10696cc1257d..cec80dffaeaa1 100644
--- a/packages/block-editor/src/hooks/block-bindings.js
+++ b/packages/block-editor/src/hooks/block-bindings.js
@@ -23,15 +23,15 @@ import { useViewportMatch } from '@wordpress/compose';
/**
* Internal dependencies
*/
-import {
- canBindAttribute,
- getBindableAttributes,
-} from '../hooks/use-bindings-attributes';
import { unlock } from '../lock-unlock';
import InspectorControls from '../components/inspector-controls';
import BlockContext from '../components/block-context';
import { useBlockEditContext } from '../components/block-edit';
-import { useBlockBindingsUtils } from '../utils/block-bindings';
+import {
+ canBindAttribute,
+ getBindableAttributes,
+ useBlockBindingsUtils,
+} from '../utils/block-bindings';
import { store as blockEditorStore } from '../store';
const { Menu } = unlock( componentsPrivateApis );
diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js
index 66ff60b691b66..7f9b29376ad1f 100644
--- a/packages/block-editor/src/hooks/index.js
+++ b/packages/block-editor/src/hooks/index.js
@@ -32,7 +32,6 @@ import './metadata';
import blockHooks from './block-hooks';
import blockBindingsPanel from './block-bindings';
import './block-renaming';
-import './use-bindings-attributes';
import './grid-visualizer';
createBlockEditFilter(
diff --git a/packages/block-editor/src/utils/block-bindings.js b/packages/block-editor/src/utils/block-bindings.js
index dcf80d985473b..82f0dff53531a 100644
--- a/packages/block-editor/src/utils/block-bindings.js
+++ b/packages/block-editor/src/utils/block-bindings.js
@@ -13,6 +13,43 @@ function isObjectEmpty( object ) {
return ! object || Object.keys( object ).length === 0;
}
+export const BLOCK_BINDINGS_ALLOWED_BLOCKS = {
+ 'core/paragraph': [ 'content' ],
+ 'core/heading': [ 'content' ],
+ 'core/image': [ 'id', 'url', 'title', 'alt' ],
+ 'core/button': [ 'url', 'text', 'linkTarget', 'rel' ],
+};
+
+/**
+ * Based on the given block name,
+ * check if it is possible to bind the block.
+ *
+ * @param {string} blockName - The block name.
+ * @return {boolean} Whether it is possible to bind the block to sources.
+ */
+export function canBindBlock( blockName ) {
+ return blockName in BLOCK_BINDINGS_ALLOWED_BLOCKS;
+}
+
+/**
+ * Based on the given block name and attribute name,
+ * check if it is possible to bind the block attribute.
+ *
+ * @param {string} blockName - The block name.
+ * @param {string} attributeName - The attribute name.
+ * @return {boolean} Whether it is possible to bind the block attribute.
+ */
+export function canBindAttribute( blockName, attributeName ) {
+ return (
+ canBindBlock( blockName ) &&
+ BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ].includes( attributeName )
+ );
+}
+
+export function getBindableAttributes( blockName ) {
+ return BLOCK_BINDINGS_ALLOWED_BLOCKS[ blockName ];
+}
+
/**
* Contains utils to update the block `bindings` metadata.
*
diff --git a/packages/e2e-tests/plugins/block-bindings.php b/packages/e2e-tests/plugins/block-bindings.php
index b86673c2c523d..1fd6d8468c77d 100644
--- a/packages/e2e-tests/plugins/block-bindings.php
+++ b/packages/e2e-tests/plugins/block-bindings.php
@@ -41,7 +41,11 @@ function gutenberg_test_block_bindings_registration() {
plugins_url( 'block-bindings/index.js', __FILE__ ),
array(
'wp-blocks',
- 'wp-private-apis',
+ 'wp-block-editor',
+ 'wp-components',
+ 'wp-compose',
+ 'wp-element',
+ 'wp-hooks',
),
filemtime( plugin_dir_path( __FILE__ ) . 'block-bindings/index.js' ),
true
diff --git a/packages/e2e-tests/plugins/block-bindings/index.js b/packages/e2e-tests/plugins/block-bindings/index.js
index 5c364257caed1..63c463e197fa8 100644
--- a/packages/e2e-tests/plugins/block-bindings/index.js
+++ b/packages/e2e-tests/plugins/block-bindings/index.js
@@ -1,4 +1,9 @@
const { registerBlockBindingsSource } = wp.blocks;
+const { InspectorControls } = wp.blockEditor;
+const { PanelBody, TextControl } = wp.components;
+const { createHigherOrderComponent } = wp.compose;
+const { createElement: el, Fragment } = wp.element;
+const { addFilter } = wp.hooks;
const { fieldsList } = window.testingBindings || {};
const getValues = ( { bindings } ) => {
@@ -46,3 +51,43 @@ registerBlockBindingsSource( {
getValues,
canUserEditValue: () => true,
} );
+
+const withBlockBindingsInspectorControl = createHigherOrderComponent(
+ ( BlockEdit ) => {
+ return ( props ) => {
+ if ( ! props.attributes?.metadata?.bindings?.content ) {
+ return el( BlockEdit, props );
+ }
+
+ return el(
+ Fragment,
+ {},
+ el( BlockEdit, props ),
+ el(
+ InspectorControls,
+ {},
+ el(
+ PanelBody,
+ { title: 'Bindings' },
+ el( TextControl, {
+ __next40pxDefaultSize: true,
+ __nextHasNoMarginBottom: true,
+ label: 'Content',
+ value: props.attributes.content,
+ onChange: ( content ) =>
+ props.setAttributes( {
+ content,
+ } ),
+ } )
+ )
+ )
+ );
+ };
+ }
+);
+
+addFilter(
+ 'editor.BlockEdit',
+ 'testing/bindings-inspector-control',
+ withBlockBindingsInspectorControl
+);
diff --git a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js
index 32334bfc777f2..318707e22f098 100644
--- a/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js
+++ b/test/e2e/specs/editor/various/block-bindings/post-meta.spec.js
@@ -524,6 +524,47 @@ test.describe( 'Post Meta source', () => {
previewPage.locator( '#connected-paragraph' )
).toHaveText( 'new value' );
} );
+
+ test( 'should be possible to edit the value of the connected custom fields in the inspector control registered by the plugin', async ( {
+ editor,
+ page,
+ } ) => {
+ await editor.insertBlock( {
+ name: 'core/paragraph',
+ attributes: {
+ anchor: 'connected-paragraph',
+ content: 'fallback content',
+ metadata: {
+ bindings: {
+ content: {
+ source: 'core/post-meta',
+ args: {
+ key: 'movie_field',
+ },
+ },
+ },
+ },
+ },
+ } );
+ const contentInput = page.getByRole( 'textbox', {
+ name: 'Content',
+ } );
+ await expect( contentInput ).toHaveValue(
+ 'Movie field default value'
+ );
+ await contentInput.fill( 'new value' );
+ // Check that the paragraph content attribute didn't change.
+ const [ paragraphBlockObject ] = await editor.getBlocks();
+ expect( paragraphBlockObject.attributes.content ).toBe(
+ 'fallback content'
+ );
+ // Check the value of the custom field is being updated by visiting the frontend.
+ const previewPage = await editor.openPreviewPage();
+ await expect(
+ previewPage.locator( '#connected-paragraph' )
+ ).toHaveText( 'new value' );
+ } );
+
test( 'should be possible to connect movie fields through the attributes panel', async ( {
editor,
page,
From d3f344fe6193d7cd86e03e7a00770032e7919ab4 Mon Sep 17 00:00:00 2001
From: Dave Smith
Date: Mon, 2 Dec 2024 13:17:45 +0000
Subject: [PATCH 27/52] Prefer exact matches in Link Search results sorting
(#67367)
* Weight towards exact matches
* Add additional test coverage
Co-authored-by: getdave
Co-authored-by: draganescu
Co-authored-by: talldan
Co-authored-by: jasmussen
Co-authored-by: kevin940726
Co-authored-by: ironprogrammer
Co-authored-by: annezazu
---
.../__experimental-fetch-link-suggestions.ts | 25 +++++++++++--
.../__experimental-fetch-link-suggestions.js | 37 +++++++++++++++++++
2 files changed, 58 insertions(+), 4 deletions(-)
diff --git a/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.ts b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.ts
index e1a166ee272db..2901219758903 100644
--- a/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.ts
+++ b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.ts
@@ -270,12 +270,29 @@ export function sortResults( results: SearchResult[], search: string ) {
for ( const result of results ) {
if ( result.title ) {
const titleTokens = tokenize( result.title );
- const matchingTokens = titleTokens.filter( ( titleToken ) =>
- searchTokens.some( ( searchToken ) =>
- titleToken.includes( searchToken )
+ const exactMatchingTokens = titleTokens.filter( ( titleToken ) =>
+ searchTokens.some(
+ ( searchToken ) => titleToken === searchToken
)
);
- scores[ result.id ] = matchingTokens.length / titleTokens.length;
+ const subMatchingTokens = titleTokens.filter( ( titleToken ) =>
+ searchTokens.some(
+ ( searchToken ) =>
+ titleToken !== searchToken &&
+ titleToken.includes( searchToken )
+ )
+ );
+
+ // The score is a combination of exact matches and sub-matches.
+ // More weight is given to exact matches, as they are more relevant (e.g. "cat" vs "caterpillar").
+ // Diving by the total number of tokens in the title normalizes the score and skews
+ // the results towards shorter titles.
+ const exactMatchScore =
+ ( exactMatchingTokens.length / titleTokens.length ) * 10;
+
+ const subMatchScore = subMatchingTokens.length / titleTokens.length;
+
+ scores[ result.id ] = exactMatchScore + subMatchScore;
} else {
scores[ result.id ] = 0;
}
diff --git a/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js b/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js
index 6878c74332c3d..ad0014ff86ecb 100644
--- a/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js
+++ b/packages/core-data/src/fetch/test/__experimental-fetch-link-suggestions.js
@@ -393,6 +393,43 @@ describe( 'sortResults', () => {
6,
] );
} );
+
+ it( 'orders results to prefer direct matches over sub matches', () => {
+ const results = [
+ {
+ id: 1,
+ title: 'News',
+ url: 'http://wordpress.local/news/',
+ type: 'page',
+ kind: 'post-type',
+ },
+ {
+ id: 2,
+ title: 'Newspaper',
+ url: 'http://wordpress.local/newspaper/',
+ type: 'page',
+ kind: 'post-type',
+ },
+ {
+ id: 3,
+ title: 'News Flash News',
+ url: 'http://wordpress.local/news-flash-news/',
+ type: 'page',
+ kind: 'post-type',
+ },
+ {
+ id: 4,
+ title: 'News',
+ url: 'http://wordpress.local/news-2/',
+ type: 'page',
+ kind: 'post-type',
+ },
+ ];
+ const order = sortResults( results, 'News' ).map(
+ ( result ) => result.id
+ );
+ expect( order ).toEqual( [ 1, 4, 3, 2 ] );
+ } );
} );
describe( 'tokenize', () => {
From cd26001761b5f93143671ce1505b25732c51692e Mon Sep 17 00:00:00 2001
From: Mitchell Austin
Date: Mon, 2 Dec 2024 05:51:31 -0800
Subject: [PATCH 28/52] =?UTF-8?q?Fix=20Meta=20boxes=20saving=20when=20they?=
=?UTF-8?q?=E2=80=99re=20not=20present=20(#67254)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Initialize meta boxes whether or not they’re visible
* Add hook for initialization of meta boxes
* Minimize hook for meta boxes initialization
* Name the export
Co-authored-by: stokesman
Co-authored-by: afercia
Co-authored-by: t-hamano
Co-authored-by: Mamaduka
Co-authored-by: enricobattocchi
---
.../edit-post/src/components/layout/index.js | 13 ++++---
.../src/components/meta-boxes/index.js | 37 ++-----------------
.../meta-boxes/use-meta-box-initialization.js | 32 ++++++++++++++++
3 files changed, 43 insertions(+), 39 deletions(-)
create mode 100644 packages/edit-post/src/components/meta-boxes/use-meta-box-initialization.js
diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js
index 5dcbfa2c82cea..b8061571ec66c 100644
--- a/packages/edit-post/src/components/layout/index.js
+++ b/packages/edit-post/src/components/layout/index.js
@@ -74,6 +74,7 @@ import useEditPostCommands from '../../commands/use-commands';
import { usePaddingAppender } from './use-padding-appender';
import { useShouldIframe } from './use-should-iframe';
import useNavigateToEntityRecord from '../../hooks/use-navigate-to-entity-record';
+import { useMetaBoxInitialization } from '../meta-boxes/use-meta-box-initialization';
const { getLayoutStyles } = unlock( blockEditorPrivateApis );
const { useCommands } = unlock( coreCommandsPrivateApis );
@@ -413,6 +414,8 @@ function Layout( {
const { isZoomOut } = unlock( select( blockEditorStore ) );
const { getEditorMode, getRenderingMode } = select( editorStore );
const isRenderingPostOnly = getRenderingMode() === 'post-only';
+ const isNotDesignPostType =
+ ! DESIGN_POST_TYPES.includes( currentPostType );
return {
mode: getEditorMode(),
@@ -423,9 +426,7 @@ function Layout( {
!! select( blockEditorStore ).getBlockSelectionStart(),
showIconLabels: get( 'core', 'showIconLabels' ),
isDistractionFree: get( 'core', 'distractionFree' ),
- showMetaBoxes:
- ! DESIGN_POST_TYPES.includes( currentPostType ) &&
- ! isZoomOut(),
+ showMetaBoxes: isNotDesignPostType && ! isZoomOut(),
isWelcomeGuideVisible: isFeatureActive( 'welcomeGuide' ),
templateId:
supportsTemplateMode &&
@@ -435,9 +436,7 @@ function Layout( {
? getTemplateId( currentPostType, currentPostId )
: null,
enablePaddingAppender:
- ! isZoomOut() &&
- isRenderingPostOnly &&
- ! DESIGN_POST_TYPES.includes( currentPostType ),
+ ! isZoomOut() && isRenderingPostOnly && isNotDesignPostType,
};
},
[
@@ -447,6 +446,8 @@ function Layout( {
settings.supportsTemplateMode,
]
);
+ useMetaBoxInitialization( hasActiveMetaboxes );
+
const [ paddingAppenderRef, paddingStyle ] = usePaddingAppender(
enablePaddingAppender
);
diff --git a/packages/edit-post/src/components/meta-boxes/index.js b/packages/edit-post/src/components/meta-boxes/index.js
index 14728c97cf6b6..fdc74a5df4ce9 100644
--- a/packages/edit-post/src/components/meta-boxes/index.js
+++ b/packages/edit-post/src/components/meta-boxes/index.js
@@ -1,9 +1,7 @@
/**
* WordPress dependencies
*/
-import { useSelect, useRegistry } from '@wordpress/data';
-import { useEffect } from '@wordpress/element';
-import { store as editorStore } from '@wordpress/editor';
+import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
@@ -13,38 +11,11 @@ import MetaBoxVisibility from './meta-box-visibility';
import { store as editPostStore } from '../../store';
export default function MetaBoxes( { location } ) {
- const registry = useRegistry();
- const { metaBoxes, areMetaBoxesInitialized, isEditorReady } = useSelect(
- ( select ) => {
- const { __unstableIsEditorReady } = select( editorStore );
- const {
- getMetaBoxesPerLocation,
- areMetaBoxesInitialized: _areMetaBoxesInitialized,
- } = select( editPostStore );
- return {
- metaBoxes: getMetaBoxesPerLocation( location ),
- areMetaBoxesInitialized: _areMetaBoxesInitialized(),
- isEditorReady: __unstableIsEditorReady(),
- };
- },
- [ location ]
+ const metaBoxes = useSelect(
+ ( select ) =>
+ select( editPostStore ).getMetaBoxesPerLocation[ location ]
);
- const hasMetaBoxes = !! metaBoxes?.length;
-
- // When editor is ready, initialize postboxes (wp core script) and metabox
- // saving. This initializes all meta box locations, not just this specific
- // one.
- useEffect( () => {
- if ( isEditorReady && hasMetaBoxes && ! areMetaBoxesInitialized ) {
- registry.dispatch( editPostStore ).initializeMetaBoxes();
- }
- }, [ isEditorReady, hasMetaBoxes, areMetaBoxesInitialized ] );
-
- if ( ! areMetaBoxesInitialized ) {
- return null;
- }
-
return (
<>
{ ( metaBoxes ?? [] ).map( ( { id } ) => (
diff --git a/packages/edit-post/src/components/meta-boxes/use-meta-box-initialization.js b/packages/edit-post/src/components/meta-boxes/use-meta-box-initialization.js
new file mode 100644
index 0000000000000..4309d85e3c22b
--- /dev/null
+++ b/packages/edit-post/src/components/meta-boxes/use-meta-box-initialization.js
@@ -0,0 +1,32 @@
+/**
+ * WordPress dependencies
+ */
+import { useDispatch, useSelect } from '@wordpress/data';
+import { store as editorStore } from '@wordpress/editor';
+import { useEffect } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { store as editPostStore } from '../../store';
+
+/**
+ * Initializes WordPress `postboxes` script and the logic for saving meta boxes.
+ *
+ * @param { boolean } enabled
+ */
+export const useMetaBoxInitialization = ( enabled ) => {
+ const isEnabledAndEditorReady = useSelect(
+ ( select ) =>
+ enabled && select( editorStore ).__unstableIsEditorReady(),
+ [ enabled ]
+ );
+ const { initializeMetaBoxes } = useDispatch( editPostStore );
+ // The effect has to rerun when the editor is ready because initializeMetaBoxes
+ // will noop until then.
+ useEffect( () => {
+ if ( isEnabledAndEditorReady ) {
+ initializeMetaBoxes();
+ }
+ }, [ isEnabledAndEditorReady, initializeMetaBoxes ] );
+};
From d0c372c881cb7d68bf56703df58f057614817c02 Mon Sep 17 00:00:00 2001
From: Mario Santos <34552881+SantosGuillamot@users.noreply.github.com>
Date: Mon, 2 Dec 2024 15:06:19 +0100
Subject: [PATCH 29/52] Fix write mode persisting after disabling the
experiment
Co-authored-by: SantosGuillamot
Co-authored-by: getdave
---
packages/block-editor/src/store/selectors.js | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js
index 75c43770f7e17..dc90f35173252 100644
--- a/packages/block-editor/src/store/selectors.js
+++ b/packages/block-editor/src/store/selectors.js
@@ -3041,9 +3041,7 @@ export const getBlockEditingMode = createRegistrySelector(
clientId = '';
}
- const isNavMode =
- select( preferencesStore )?.get( 'core', 'editorTool' ) ===
- 'navigation';
+ const isNavMode = isNavigationMode( state );
// If the editor is currently not in navigation mode, check if the clientId
// has an editing mode set in the regular derived map.
From 65fa4f3b0273afd7a7b578614a6e9ffd80d05de5 Mon Sep 17 00:00:00 2001
From: Hit Bhalodia <58802366+hbhalodia@users.noreply.github.com>
Date: Mon, 2 Dec 2024 20:11:50 +0530
Subject: [PATCH 30/52] NumberControl: Deprecate 36px default size (#66730)
* Add the deprecation for 36px default size to number control
* Add the changelog for the deprecation
* Update unit test and supress warning for 40px default size warning from child component
* Add the deperection changelog to unreleased section and updated the component to use __next40pxDefaultSize for NumberControl
* Refactor the test for NumberControl component to reduce changes
* Update box control files to use supress warning prop before default 40px prop
* Supress the console warning for deprecation message from child component
* Addressed the feedbacks on the PR and updated the component based on that
---------
Co-authored-by: hbhalodia
Co-authored-by: mirka <0mirka00@git.wordpress.org>
---
.../src/components/line-height-control/index.js | 1 +
packages/components/CHANGELOG.md | 1 +
packages/components/src/angle-picker-control/index.tsx | 2 +-
.../components/src/color-picker/input-with-slider.tsx | 2 +-
packages/components/src/number-control/README.md | 3 ++-
packages/components/src/number-control/index.tsx | 9 +++++++++
.../src/number-control/stories/index.story.tsx | 1 +
packages/components/src/number-control/test/index.tsx | 6 +++++-
packages/components/src/number-control/types.ts | 7 +++++++
packages/components/src/range-control/index.tsx | 1 +
packages/components/src/unit-control/index.tsx | 1 +
11 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/packages/block-editor/src/components/line-height-control/index.js b/packages/block-editor/src/components/line-height-control/index.js
index b2c99c03f8784..e6af602c2875a 100644
--- a/packages/block-editor/src/components/line-height-control/index.js
+++ b/packages/block-editor/src/components/line-height-control/index.js
@@ -93,6 +93,7 @@ const LineHeightControl = ( {
}
spinControls="none"
- size="__unstable-large"
/>
{
return (
{};
@@ -53,9 +54,17 @@ function UnforwardedNumberControl(
size = 'default',
suffix,
onChange = noop,
+ __shouldNotWarnDeprecated36pxSize,
...restProps
} = useDeprecated36pxDefaultSizeProp< NumberControlProps >( props );
+ maybeWarnDeprecated36pxSize( {
+ componentName: 'NumberControl',
+ size,
+ __next40pxDefaultSize: restProps.__next40pxDefaultSize,
+ __shouldNotWarnDeprecated36pxSize,
+ } );
+
if ( hideHTMLArrows ) {
deprecated( 'wp.components.NumberControl hideHTMLArrows prop ', {
alternative: 'spinControls="none"',
diff --git a/packages/components/src/number-control/stories/index.story.tsx b/packages/components/src/number-control/stories/index.story.tsx
index 3feb0d63eadc2..8710839fea6ea 100644
--- a/packages/components/src/number-control/stories/index.story.tsx
+++ b/packages/components/src/number-control/stories/index.story.tsx
@@ -62,4 +62,5 @@ const Template: StoryFn< typeof NumberControl > = ( {
export const Default = Template.bind( {} );
Default.args = {
label: 'Value',
+ __next40pxDefaultSize: true,
};
diff --git a/packages/components/src/number-control/test/index.tsx b/packages/components/src/number-control/test/index.tsx
index 3cf3368f1636b..bf97b520673ea 100644
--- a/packages/components/src/number-control/test/index.tsx
+++ b/packages/components/src/number-control/test/index.tsx
@@ -12,9 +12,13 @@ import { useState } from '@wordpress/element';
/**
* Internal dependencies
*/
-import NumberControl from '..';
+import _NumberControl from '..';
import type { NumberControlProps } from '../types';
+const NumberControl = (
+ props: React.ComponentProps< typeof _NumberControl >
+) => <_NumberControl __next40pxDefaultSize { ...props } />;
+
function StatefulNumberControl( props: NumberControlProps ) {
const [ value, setValue ] = useState( props.value );
const handleOnChange = ( v: string | undefined ) => setValue( v );
diff --git a/packages/components/src/number-control/types.ts b/packages/components/src/number-control/types.ts
index 8d198e777bd55..2a0fbf402d356 100644
--- a/packages/components/src/number-control/types.ts
+++ b/packages/components/src/number-control/types.ts
@@ -91,4 +91,11 @@ export type NumberControlProps = Omit<
* The value of the input.
*/
value?: number | string;
+ /**
+ * Do not throw a warning for the deprecated 36px default size.
+ * For internal components of other components that already throw the warning.
+ *
+ * @ignore
+ */
+ __shouldNotWarnDeprecated36pxSize?: boolean;
};
diff --git a/packages/components/src/range-control/index.tsx b/packages/components/src/range-control/index.tsx
index 916571c3ee0e0..89dd8248a1e61 100644
--- a/packages/components/src/range-control/index.tsx
+++ b/packages/components/src/range-control/index.tsx
@@ -350,6 +350,7 @@ function UnforwardedRangeControl(
step={ step }
// @ts-expect-error TODO: Investigate if the `null` value is necessary
value={ inputSliderValue }
+ __shouldNotWarnDeprecated36pxSize
/>
) }
{ allowReset && (
diff --git a/packages/components/src/unit-control/index.tsx b/packages/components/src/unit-control/index.tsx
index 9845c4eb04ef2..65e1e56cda3b3 100644
--- a/packages/components/src/unit-control/index.tsx
+++ b/packages/components/src/unit-control/index.tsx
@@ -224,6 +224,7 @@ function UnforwardedUnitControl(
return (
Date: Mon, 2 Dec 2024 16:07:57 +0100
Subject: [PATCH 31/52] useEditorTitle: fix wrong request without ID (#67475)
Co-authored-by: ellatrix
Co-authored-by: youknowriad
---
.../src/components/editor/use-editor-title.js | 4 ++
test/e2e/specs/site-editor/preload.spec.js | 41 +++++++++++++++++++
2 files changed, 45 insertions(+)
create mode 100644 test/e2e/specs/site-editor/preload.spec.js
diff --git a/packages/edit-site/src/components/editor/use-editor-title.js b/packages/edit-site/src/components/editor/use-editor-title.js
index 727b190117e02..6f0b36f8e3b8b 100644
--- a/packages/edit-site/src/components/editor/use-editor-title.js
+++ b/packages/edit-site/src/components/editor/use-editor-title.js
@@ -22,6 +22,10 @@ function useEditorTitle( postType, postId ) {
const { getEditedEntityRecord, hasFinishedResolution } =
select( coreStore );
+ if ( ! postId ) {
+ return { isLoaded: false };
+ }
+
const _record = getEditedEntityRecord(
'postType',
postType,
diff --git a/test/e2e/specs/site-editor/preload.spec.js b/test/e2e/specs/site-editor/preload.spec.js
new file mode 100644
index 0000000000000..1e93f783a8a91
--- /dev/null
+++ b/test/e2e/specs/site-editor/preload.spec.js
@@ -0,0 +1,41 @@
+/**
+ * WordPress dependencies
+ */
+const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
+
+test.describe( 'Preload', () => {
+ test.beforeAll( async ( { requestUtils } ) => {
+ await requestUtils.activateTheme( 'emptytheme' );
+ } );
+
+ test.afterAll( async ( { requestUtils } ) => {
+ await requestUtils.activateTheme( 'twentytwentyone' );
+ } );
+
+ test( 'Should make no requests before the iframe is loaded', async ( {
+ page,
+ admin,
+ } ) => {
+ // Do not use `visitSiteEditor` because it waits for the iframe to load.
+ await admin.visitAdminPage( 'site-editor.php' );
+
+ const requests = [];
+ let isLoaded = false;
+
+ page.on( 'request', ( request ) => {
+ if ( request.resourceType() === 'document' ) {
+ // The iframe also "requests" a blob document. This is the most
+ // reliable way to wait for the iframe to start loading.
+ // `waitForSelector` is always a bit delayed, and we don't want
+ // to detect requests after the iframe mounts.
+ isLoaded = true;
+ } else if ( ! isLoaded && request.resourceType() === 'fetch' ) {
+ requests.push( request.url() );
+ }
+ } );
+
+ await page.waitForFunction( ( _isLoaded ) => _isLoaded, [ isLoaded ] );
+
+ expect( requests ).toEqual( [] );
+ } );
+} );
From 6689c778e6bbb9774dd4487c7f1c29e1ffd207f5 Mon Sep 17 00:00:00 2001
From: Jarda Snajdr
Date: Mon, 2 Dec 2024 17:02:52 +0100
Subject: [PATCH 32/52] SlotFill: remove explicit rerender from portal version
(#67471)
* SlotFill: remove explicit rerender from portal version
* Add changelog entry
Co-authored-by: jsnajdr
Co-authored-by: tyxla
---
packages/components/CHANGELOG.md | 1 +
.../src/slot-fill/bubbles-virtually/fill.tsx | 18 +++++++-----------
.../bubbles-virtually/slot-fill-provider.tsx | 15 ++-------------
.../src/slot-fill/bubbles-virtually/slot.tsx | 11 ++++-------
packages/components/src/slot-fill/types.ts | 7 ++++---
5 files changed, 18 insertions(+), 34 deletions(-)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 748525d6d9c15..94dfd1b3c3811 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -22,6 +22,7 @@
- Upgraded `@ariakit/react` (v0.4.13) and `@ariakit/test` (v0.4.5) ([#65907](https://github.com/WordPress/gutenberg/pull/65907)).
- Upgraded `@ariakit/react` (v0.4.15) and `@ariakit/test` (v0.4.7) ([#67404](https://github.com/WordPress/gutenberg/pull/67404)).
- Exported the `WPCompleter` type as it was being used in block-editor/autocompleters ([#67410](https://github.com/WordPress/gutenberg/pull/67410)).
+- `SlotFill`: remove manual rerenders from the portal `Fill` component ([#67471](https://github.com/WordPress/gutenberg/pull/67471)).
### Bug Fixes
diff --git a/packages/components/src/slot-fill/bubbles-virtually/fill.tsx b/packages/components/src/slot-fill/bubbles-virtually/fill.tsx
index d5287adfab417..ef7bc94ff540b 100644
--- a/packages/components/src/slot-fill/bubbles-virtually/fill.tsx
+++ b/packages/components/src/slot-fill/bubbles-virtually/fill.tsx
@@ -4,7 +4,6 @@
import { useObservableValue } from '@wordpress/compose';
import {
useContext,
- useReducer,
useRef,
useEffect,
createPortal,
@@ -20,18 +19,15 @@ import type { FillComponentProps } from '../types';
export default function Fill( { name, children }: FillComponentProps ) {
const registry = useContext( SlotFillContext );
const slot = useObservableValue( registry.slots, name );
- const [ , rerender ] = useReducer( () => [], [] );
- const ref = useRef( { rerender } );
+ const instanceRef = useRef( {} );
+ // We register fills so we can keep track of their existence.
+ // Slots can use the `useSlotFills` hook to know if there're already fills
+ // registered so they can choose to render themselves or not.
useEffect( () => {
- // We register fills so we can keep track of their existence.
- // Some Slot implementations need to know if there're already fills
- // registered so they can choose to render themselves or not.
- const refValue = ref.current;
- registry.registerFill( name, refValue );
- return () => {
- registry.unregisterFill( name, refValue );
- };
+ const instance = instanceRef.current;
+ registry.registerFill( name, instance );
+ return () => registry.unregisterFill( name, instance );
}, [ registry, name ] );
if ( ! slot || ! slot.ref.current ) {
diff --git a/packages/components/src/slot-fill/bubbles-virtually/slot-fill-provider.tsx b/packages/components/src/slot-fill/bubbles-virtually/slot-fill-provider.tsx
index 1dc5ef35ceccf..cf692700eef79 100644
--- a/packages/components/src/slot-fill/bubbles-virtually/slot-fill-provider.tsx
+++ b/packages/components/src/slot-fill/bubbles-virtually/slot-fill-provider.tsx
@@ -23,13 +23,7 @@ function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
ref,
fillProps
) => {
- const slot = slots.get( name );
-
- slots.set( name, {
- ...slot,
- ref: ref || slot?.ref,
- fillProps: fillProps || slot?.fillProps || {},
- } );
+ slots.set( name, { ref, fillProps } );
};
const unregisterSlot: SlotFillBubblesVirtuallyContext[ 'unregisterSlot' ] =
@@ -66,12 +60,7 @@ function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
return;
}
- slot.fillProps = fillProps;
- const slotFills = fills.get( name );
- if ( slotFills ) {
- // Force update fills.
- slotFills.forEach( ( fill ) => fill.rerender() );
- }
+ slots.set( name, { ref, fillProps } );
};
const registerFill: SlotFillBubblesVirtuallyContext[ 'registerFill' ] = (
diff --git a/packages/components/src/slot-fill/bubbles-virtually/slot.tsx b/packages/components/src/slot-fill/bubbles-virtually/slot.tsx
index b8ead7fc7ea8b..e65c055c410a6 100644
--- a/packages/components/src/slot-fill/bubbles-virtually/slot.tsx
+++ b/packages/components/src/slot-fill/bubbles-virtually/slot.tsx
@@ -39,8 +39,7 @@ function Slot(
...restProps
} = props;
- const { registerSlot, unregisterSlot, ...registry } =
- useContext( SlotFillContext );
+ const registry = useContext( SlotFillContext );
const ref = useRef< HTMLElement >( null );
@@ -54,11 +53,9 @@ function Slot(
}, [ fillProps ] );
useLayoutEffect( () => {
- registerSlot( name, ref, fillPropsRef.current );
- return () => {
- unregisterSlot( name, ref );
- };
- }, [ registerSlot, unregisterSlot, name ] );
+ registry.registerSlot( name, ref, fillPropsRef.current );
+ return () => registry.unregisterSlot( name, ref );
+ }, [ registry, name ] );
useLayoutEffect( () => {
registry.updateSlot( name, ref, fillPropsRef.current );
diff --git a/packages/components/src/slot-fill/types.ts b/packages/components/src/slot-fill/types.ts
index 15f082cf3f755..6668057323edd 100644
--- a/packages/components/src/slot-fill/types.ts
+++ b/packages/components/src/slot-fill/types.ts
@@ -110,15 +110,16 @@ export type SlotFillProviderProps = {
export type SlotRef = RefObject< HTMLElement >;
export type Rerenderable = { rerender: () => void };
+export type FillInstance = {};
export type SlotFillBubblesVirtuallyContext = {
slots: ObservableMap< SlotKey, { ref: SlotRef; fillProps: FillProps } >;
- fills: ObservableMap< SlotKey, Rerenderable[] >;
+ fills: ObservableMap< SlotKey, FillInstance[] >;
registerSlot: ( name: SlotKey, ref: SlotRef, fillProps: FillProps ) => void;
unregisterSlot: ( name: SlotKey, ref: SlotRef ) => void;
updateSlot: ( name: SlotKey, ref: SlotRef, fillProps: FillProps ) => void;
- registerFill: ( name: SlotKey, ref: Rerenderable ) => void;
- unregisterFill: ( name: SlotKey, ref: Rerenderable ) => void;
+ registerFill: ( name: SlotKey, instance: FillInstance ) => void;
+ unregisterFill: ( name: SlotKey, instance: FillInstance ) => void;
/**
* This helps the provider know if it's using the default context value or not.
From fa10d2fbd3faea76835374d6c850864d103a98c9 Mon Sep 17 00:00:00 2001
From: Andrea Fercia
Date: Mon, 2 Dec 2024 17:07:55 +0100
Subject: [PATCH 33/52] Fix EntitiesSavedStates panel dialog props. (#67351)
* Fix EntitiesSavedStates panel dialog props.
* Remove renderDialog default value.
Co-authored-by: afercia
Co-authored-by: ntsekouras
Co-authored-by: Mayank-Tripathi32
Co-authored-by: jameskoster
---
.../edit-site/src/components/save-panel/index.js | 12 +++++++++---
packages/editor/README.md | 2 +-
.../src/components/entities-saved-states/index.js | 15 ++++++---------
.../src/components/post-publish-button/index.js | 2 ++
.../src/components/save-publish-panels/index.js | 6 +++++-
5 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/packages/edit-site/src/components/save-panel/index.js b/packages/edit-site/src/components/save-panel/index.js
index 81a0f99557df0..95ec9b9ffc8c4 100644
--- a/packages/edit-site/src/components/save-panel/index.js
+++ b/packages/edit-site/src/components/save-panel/index.js
@@ -31,7 +31,7 @@ const { EntitiesSavedStatesExtensible, NavigableRegion } =
unlock( privateApis );
const { useLocation } = unlock( routerPrivateApis );
-const EntitiesSavedStatesForPreview = ( { onClose } ) => {
+const EntitiesSavedStatesForPreview = ( { onClose, renderDialog } ) => {
const isDirtyProps = useEntitiesSavedStatesIsDirty();
let activateSaveLabel;
if ( isDirtyProps.isDirty ) {
@@ -75,14 +75,20 @@ const EntitiesSavedStatesForPreview = ( { onClose } ) => {
onSave,
saveEnabled: true,
saveLabel: activateSaveLabel,
+ renderDialog,
} }
/>
);
};
-const _EntitiesSavedStates = ( { onClose, renderDialog = undefined } ) => {
+const _EntitiesSavedStates = ( { onClose, renderDialog } ) => {
if ( isPreviewingTheme() ) {
- return ;
+ return (
+
+ );
}
return (
diff --git a/packages/editor/README.md b/packages/editor/README.md
index 8b48d773acb26..dd7b53f421a1d 100644
--- a/packages/editor/README.md
+++ b/packages/editor/README.md
@@ -401,7 +401,7 @@ _Parameters_
- _props_ `Object`: The component props.
- _props.close_ `Function`: The function to close the dialog.
-- _props.renderDialog_ `Function`: The function to render the dialog.
+- _props.renderDialog_ `boolean`: Whether to render the component with modal dialog behavior.
_Returns_
diff --git a/packages/editor/src/components/entities-saved-states/index.js b/packages/editor/src/components/entities-saved-states/index.js
index ea05bca522941..ad584b0df7557 100644
--- a/packages/editor/src/components/entities-saved-states/index.js
+++ b/packages/editor/src/components/entities-saved-states/index.js
@@ -31,14 +31,11 @@ function identity( values ) {
*
* @param {Object} props The component props.
* @param {Function} props.close The function to close the dialog.
- * @param {Function} props.renderDialog The function to render the dialog.
+ * @param {boolean} props.renderDialog Whether to render the component with modal dialog behavior.
*
* @return {React.ReactNode} The rendered component.
*/
-export default function EntitiesSavedStates( {
- close,
- renderDialog = undefined,
-} ) {
+export default function EntitiesSavedStates( { close, renderDialog } ) {
const isDirtyProps = useIsDirty();
return (
@@ -102,7 +103,10 @@ export default function SavePublishPanels( {
return (
<>
{ isEntitiesSavedStatesOpen && (
-
+
) }
{ ! isEntitiesSavedStatesOpen && unmountableContent }
From 656110814c85d346669dd4fa2c9d3de670d35cbb Mon Sep 17 00:00:00 2001
From: Dave Smith
Date: Mon, 2 Dec 2024 16:39:38 +0000
Subject: [PATCH 34/52] Correctly apply current-menu-ancestor class to in
Nav block #67169
Co-authored-by: getdave
Co-authored-by: MaggieCabrera
Co-authored-by: carolinan
Co-authored-by: mrwweb
Co-authored-by: juanfra
Co-authored-by: draganescu
Co-authored-by: bph
Co-authored-by: jordesign
Co-authored-by: webmandesign
---
packages/block-library/src/navigation-submenu/index.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/block-library/src/navigation-submenu/index.php b/packages/block-library/src/navigation-submenu/index.php
index 92b55e291606e..d61dbb2426c24 100644
--- a/packages/block-library/src/navigation-submenu/index.php
+++ b/packages/block-library/src/navigation-submenu/index.php
@@ -222,7 +222,7 @@ function render_block_core_navigation_submenu( $attributes, $content, $block ) {
if ( strpos( $inner_blocks_html, 'current-menu-item' ) ) {
$tag_processor = new WP_HTML_Tag_Processor( $html );
- while ( $tag_processor->next_tag( array( 'class_name' => 'wp-block-navigation-item__content' ) ) ) {
+ while ( $tag_processor->next_tag( array( 'class_name' => 'wp-block-navigation-item' ) ) ) {
$tag_processor->add_class( 'current-menu-ancestor' );
}
$html = $tag_processor->get_updated_html();
From 1d06b35940ef1d255b08f51cf242ed776d0077b0 Mon Sep 17 00:00:00 2001
From: Ella <4710635+ellatrix@users.noreply.github.com>
Date: Mon, 2 Dec 2024 17:55:55 +0100
Subject: [PATCH 35/52] Site Editor: Pages: Preload template lookup (#66654)
Co-authored-by: ellatrix
Co-authored-by: Mamaduka
Co-authored-by: jorgefilipecosta
---
backport-changelog/6.8/7695.md | 2 ++
lib/compat/wordpress-6.8/preload.php | 18 +++++++++++++-----
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/backport-changelog/6.8/7695.md b/backport-changelog/6.8/7695.md
index f45b2039e3027..d69a59f2d67d1 100644
--- a/backport-changelog/6.8/7695.md
+++ b/backport-changelog/6.8/7695.md
@@ -2,3 +2,5 @@ https://github.com/WordPress/wordpress-develop/pull/7695
* https://github.com/WordPress/gutenberg/pull/66631
* https://github.com/WordPress/gutenberg/pull/67465
+* https://github.com/WordPress/gutenberg/pull/66579
+* https://github.com/WordPress/gutenberg/pull/66654
diff --git a/lib/compat/wordpress-6.8/preload.php b/lib/compat/wordpress-6.8/preload.php
index 494e3ad32ec02..879e263f5a189 100644
--- a/lib/compat/wordpress-6.8/preload.php
+++ b/lib/compat/wordpress-6.8/preload.php
@@ -10,18 +10,26 @@
*/
function gutenberg_block_editor_preload_paths_6_8( $paths, $context ) {
if ( 'core/edit-site' === $context->name ) {
- $post_id = null;
+ $post = null;
if ( isset( $_GET['postId'] ) && is_numeric( $_GET['postId'] ) ) {
- $post_id = (int) $_GET['postId'];
+ $post = get_post( (int) $_GET['postId'] );
}
if ( isset( $_GET['p'] ) && preg_match( '/^\/page\/(\d+)$/', $_GET['p'], $matches ) ) {
- $post_id = (int) $matches[1];
+ $post = get_post( (int) $matches[1] );
}
- if ( $post_id ) {
- $route_for_post = rest_get_route_for_post( $post_id );
+ if ( $post ) {
+ $route_for_post = rest_get_route_for_post( $post );
if ( $route_for_post ) {
$paths[] = add_query_arg( 'context', 'edit', $route_for_post );
+ if ( 'page' === $post->post_type ) {
+ $paths[] = add_query_arg(
+ 'slug',
+ // @see https://github.com/WordPress/gutenberg/blob/489f6067c623926bce7151a76755bb68d8e22ea7/packages/edit-site/src/components/sync-state-with-url/use-init-edited-entity-from-url.js#L139-L140
+ 'page-' . $post->post_name,
+ '/wp/v2/templates/lookup'
+ );
+ }
}
}
From d8a457b56b6671fa25dedf2b40841a13bdabe6f1 Mon Sep 17 00:00:00 2001
From: Marin Atanasov <8436925+tyxla@users.noreply.github.com>
Date: Mon, 2 Dec 2024 19:02:59 +0200
Subject: [PATCH 36/52] Block Editor: Animate useScaleCanvas() only when
toggling zoomed out mode (#67481)
Co-authored-by: tyxla
Co-authored-by: ellatrix
---
.../src/components/iframe/use-scale-canvas.js | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/packages/block-editor/src/components/iframe/use-scale-canvas.js b/packages/block-editor/src/components/iframe/use-scale-canvas.js
index c72266e82e2b0..2d8cb217a3255 100644
--- a/packages/block-editor/src/components/iframe/use-scale-canvas.js
+++ b/packages/block-editor/src/components/iframe/use-scale-canvas.js
@@ -2,7 +2,11 @@
* WordPress dependencies
*/
import { useEffect, useRef, useCallback } from '@wordpress/element';
-import { useReducedMotion, useResizeObserver } from '@wordpress/compose';
+import {
+ usePrevious,
+ useReducedMotion,
+ useResizeObserver,
+} from '@wordpress/compose';
/**
* @typedef {Object} TransitionState
@@ -280,6 +284,7 @@ export function useScaleCanvas( {
transitionFromRef.current = transitionToRef.current;
}, [ iframeDocument ] );
+ const previousIsZoomedOut = usePrevious( isZoomedOut );
/**
* Runs when zoom out mode is toggled, and sets the startAnimation flag
* so the animation will start when the next useEffect runs. We _only_
@@ -287,7 +292,7 @@ export function useScaleCanvas( {
* changes due to the container resizing.
*/
useEffect( () => {
- if ( ! iframeDocument ) {
+ if ( ! iframeDocument || previousIsZoomedOut === isZoomedOut ) {
return;
}
@@ -300,7 +305,7 @@ export function useScaleCanvas( {
return () => {
iframeDocument.documentElement.classList.remove( 'is-zoomed-out' );
};
- }, [ iframeDocument, isZoomedOut ] );
+ }, [ iframeDocument, isZoomedOut, previousIsZoomedOut ] );
/**
* This handles:
From 232d14f33f57fcf6f0c07e52bdb5c52ba8f3dcae Mon Sep 17 00:00:00 2001
From: Lena Morita
Date: Tue, 3 Dec 2024 02:32:19 +0900
Subject: [PATCH 37/52] DropdownMenu: Increase option height to 40px (#67435)
* DropdownMenu: Increase option height to 40px
* Add changelog
Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: tyxla
---
packages/components/CHANGELOG.md | 3 +++
packages/components/src/dropdown-menu/index.tsx | 1 +
packages/components/src/dropdown-menu/style.scss | 2 +-
packages/components/src/menu-items-choice/style.scss | 1 +
4 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 94dfd1b3c3811..b482a4801c2ea 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -12,6 +12,9 @@
### Enhancements
- `BorderBoxControl`: Reduce gap value when unlinked ([#67049](https://github.com/WordPress/gutenberg/pull/67049)).
+- `DropdownMenu`: Increase option height to 40px ([#67435](https://github.com/WordPress/gutenberg/pull/67435)).
+- `MenuItem`: Increase height to 40px ([#67435](https://github.com/WordPress/gutenberg/pull/67435)).
+- `MenuItemsChoice`: Increase option height to 40px ([#67435](https://github.com/WordPress/gutenberg/pull/67435)).
### Experimental
diff --git a/packages/components/src/dropdown-menu/index.tsx b/packages/components/src/dropdown-menu/index.tsx
index 0e4501be4839c..de83695978c2d 100644
--- a/packages/components/src/dropdown-menu/index.tsx
+++ b/packages/components/src/dropdown-menu/index.tsx
@@ -164,6 +164,7 @@ function UnconnectedDropdownMenu( dropdownMenuProps: DropdownMenuProps ) {
{ controlSets?.flatMap( ( controlSet, indexOfSet ) =>
controlSet.map( ( control, indexOfControl ) => (