diff --git a/changelog.txt b/changelog.txt
index de2e8d243ff740..ba43ddc731d01b 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,5 +1,22 @@
== Changelog ==
+= 17.3.2 =
+
+## Changelog
+
+### Bug Fixes
+
+- Site editor: fix image upload bug ([57040](https://github.com/WordPress/gutenberg/pull/57040))
+
+## Contributors
+
+The following contributors merged PRs in this release:
+
+@glendaviesnz
+
+
+
+
= 17.4.1 =
## Changelog
diff --git a/docs/getting-started/tutorial.md b/docs/getting-started/tutorial.md
index f95ecc886571ce..11dde6506472f7 100644
--- a/docs/getting-started/tutorial.md
+++ b/docs/getting-started/tutorial.md
@@ -327,7 +327,7 @@ Save the file and confirm that the block appears correctly in the Editor and on
### Cleaning up
-When you use the `create-block` package to scaffold a block, it might include files that you don't need. In the case of this tutorial, the block doesn't use stylesheets or font end JavaScipt. Clean up the plugin's `src/` folder with the following actions.
+When you use the `create-block` package to scaffold a block, it might include files that you don't need. In the case of this tutorial, the block doesn't use stylesheets or front end JavaScript. Clean up the plugin's `src/` folder with the following actions.
1. In the `edit.js` file, remove the lines that import `editor.scss`
2. In the `index.js` file, remove the lines that import `style.scss`
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index db02364b707901..6215fbb74caf2f 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -639,7 +639,7 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) {
* for features like the enhanced pagination of the Query block.
*/
$container_class = gutenberg_incremental_id_per_prefix(
- 'wp-container-' . sanitize_title( $block['blockName'] ) . '-layout-'
+ 'wp-container-' . sanitize_title( $block['blockName'] ) . '-is-layout-'
);
// Set the correct layout type for blocks using legacy content width.
@@ -893,7 +893,7 @@ function gutenberg_restore_group_inner_container( $block_content, $block ) {
if ( $classes ) {
$classes = explode( ' ', $classes );
foreach ( $classes as $class_name ) {
- if ( str_contains( $class_name, 'layout' ) ) {
+ if ( str_contains( $class_name, 'is-layout-' ) ) {
array_push( $layout_classes, $class_name );
$processor->remove_class( $class_name );
}
diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js
index 54824558cb7036..a83d07398d54a9 100644
--- a/packages/block-editor/src/hooks/layout.js
+++ b/packages/block-editor/src/hooks/layout.js
@@ -351,7 +351,7 @@ function BlockWithLayoutStyles( { block: BlockListBlock, props } ) {
const layoutClasses = useLayoutClasses( attributes, name );
const { kebabCase } = unlock( componentsPrivateApis );
- const selectorPrefix = `wp-container-${ kebabCase( name ) }-layout-`;
+ const selectorPrefix = `wp-container-${ kebabCase( name ) }-is-layout-`;
// Higher specificity to override defaults from theme.json.
const selector = `.${ selectorPrefix }${ id }.${ selectorPrefix }${ id }`;
const [ blockGapSupport ] = useSettings( 'spacing.blockGap' );
diff --git a/packages/dataviews/src/item-actions.js b/packages/dataviews/src/item-actions.js
index 473615214da7b2..974c5c5d0d5c3f 100644
--- a/packages/dataviews/src/item-actions.js
+++ b/packages/dataviews/src/item-actions.js
@@ -21,6 +21,7 @@ const {
DropdownMenuGroupV2: DropdownMenuGroup,
DropdownMenuItemV2: DropdownMenuItem,
DropdownMenuItemLabelV2: DropdownMenuItemLabel,
+ kebabCase,
} = unlock( componentsPrivateApis );
function ButtonTrigger( { action, onClick } ) {
@@ -58,12 +59,17 @@ function ActionWithModal( { action, item, ActionTrigger } ) {
{ isModalOpen && (
{
setIsModalOpen( false );
} }
- overlayClassName="dataviews-action-modal"
+ overlayClassName={ `dataviews-action-modal dataviews-action-modal__${ kebabCase(
+ action.id
+ ) }` }
>
) }
diff --git a/packages/edit-post/src/hooks/commands/use-common-commands.js b/packages/edit-post/src/hooks/commands/use-common-commands.js
index c0300a292e29fd..003f84349dfa90 100644
--- a/packages/edit-post/src/hooks/commands/use-common-commands.js
+++ b/packages/edit-post/src/hooks/commands/use-common-commands.js
@@ -192,16 +192,16 @@ export default function useCommonCommands() {
useCommand( {
name: 'core/toggle-publish-sidebar',
label: isPublishSidebarEnabled
- ? __( 'Disable pre-publish checklist' )
- : __( 'Enable pre-publish checklist' ),
+ ? __( 'Disable pre-publish checks' )
+ : __( 'Enable pre-publish checks' ),
icon: formatListBullets,
callback: ( { close } ) => {
close();
toggle( 'core/edit-post', 'isPublishSidebarEnabled' );
createInfoNotice(
isPublishSidebarEnabled
- ? __( 'Pre-publish checklist off.' )
- : __( 'Pre-publish checklist on.' ),
+ ? __( 'Pre-publish checks disabled.' )
+ : __( 'Pre-publish checks enabled.' ),
{
id: 'core/edit-post/publish-sidebar/notice',
type: 'snackbar',
diff --git a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js b/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js
index a7e050660e9541..e49238495b5f3c 100644
--- a/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js
+++ b/packages/edit-site/src/components/page-patterns/dataviews-pattern-actions.js
@@ -6,6 +6,7 @@ import { paramCase as kebabCase } from 'change-case';
/**
* WordPress dependencies
*/
+import { getQueryArgs } from '@wordpress/url';
import { downloadBlob } from '@wordpress/blob';
import { __, sprintf } from '@wordpress/i18n';
import {
@@ -21,12 +22,23 @@ import { useState } from '@wordpress/element';
import { store as noticesStore } from '@wordpress/notices';
import { decodeEntities } from '@wordpress/html-entities';
import { store as reusableBlocksStore } from '@wordpress/reusable-blocks';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
/**
* Internal dependencies
*/
+import { unlock } from '../../lock-unlock';
import { store as editSiteStore } from '../../store';
-import { PATTERN_TYPES, TEMPLATE_PART_POST_TYPE } from '../../utils/constants';
+import {
+ PATTERN_TYPES,
+ TEMPLATE_PART_POST_TYPE,
+ PATTERN_DEFAULT_CATEGORY,
+} from '../../utils/constants';
+
+const { useHistory } = unlock( routerPrivateApis );
+const { CreatePatternModalContents, useDuplicatePatternProps } =
+ unlock( patternsPrivateApis );
export const exportJSONaction = {
id: 'export-pattern',
@@ -235,3 +247,37 @@ export const resetAction = {
);
},
};
+
+export const duplicatePatternAction = {
+ id: 'duplicate-pattern',
+ label: __( 'Duplicate' ),
+ isEligible: ( item ) => item.type !== TEMPLATE_PART_POST_TYPE,
+ modalHeader: __( 'Duplicate pattern' ),
+ RenderModal: ( { item, closeModal } ) => {
+ const { categoryId = PATTERN_DEFAULT_CATEGORY } = getQueryArgs(
+ window.location.href
+ );
+ const isThemePattern = item.type === PATTERN_TYPES.theme;
+ const history = useHistory();
+ function onPatternSuccess( { pattern } ) {
+ history.push( {
+ categoryType: PATTERN_TYPES.theme,
+ categoryId,
+ postType: PATTERN_TYPES.user,
+ postId: pattern.id,
+ } );
+ closeModal();
+ }
+ const duplicatedProps = useDuplicatePatternProps( {
+ pattern: isThemePattern ? item : item.patternPost,
+ onSuccess: onPatternSuccess,
+ } );
+ return (
+
+ );
+ },
+};
diff --git a/packages/edit-site/src/components/page-patterns/dataviews-patterns.js b/packages/edit-site/src/components/page-patterns/dataviews-patterns.js
index ed3555f418d95f..f7e86b8e2e23b1 100644
--- a/packages/edit-site/src/components/page-patterns/dataviews-patterns.js
+++ b/packages/edit-site/src/components/page-patterns/dataviews-patterns.js
@@ -54,6 +54,7 @@ import {
renameAction,
resetAction,
deleteAction,
+ duplicatePatternAction,
} from './dataviews-pattern-actions';
import usePatternSettings from './use-pattern-settings';
import { unlock } from '../../lock-unlock';
@@ -314,7 +315,13 @@ export default function DataviewsPatterns() {
}, [ patterns, view, fields ] );
const actions = useMemo(
- () => [ renameAction, exportJSONaction, resetAction, deleteAction ],
+ () => [
+ renameAction,
+ duplicatePatternAction,
+ exportJSONaction,
+ resetAction,
+ deleteAction,
+ ],
[]
);
const onChangeView = useCallback(
diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss
index 013dc836e19ad3..cce14e8067122d 100644
--- a/packages/edit-site/src/components/page-patterns/style.scss
+++ b/packages/edit-site/src/components/page-patterns/style.scss
@@ -255,3 +255,31 @@
z-index: 2;
}
}
+
+// TODO: this is duplicated from `patterns-menu-items__convert-modal` styles,
+// except for the `z-index`. Need to check if this is still needed.
+.dataviews-action-modal__duplicate-pattern {
+ // Fix the modal width to prevent added categories from stretching the modal.
+ [role="dialog"] > [role="document"] {
+ width: 350px;
+ }
+
+ .patterns-menu-items__convert-modal-categories {
+ position: relative;
+ }
+
+ .components-form-token-field__suggestions-list:not(:empty) {
+ position: absolute;
+ border: $border-width solid var(--wp-admin-theme-color);
+ border-bottom-left-radius: $radius-block-ui;
+ border-bottom-right-radius: $radius-block-ui;
+ box-shadow: 0 0 0.5px 0.5px var(--wp-admin-theme-color);
+ box-sizing: border-box;
+ z-index: 1;
+ background-color: $white;
+ width: calc(100% + 2px); // Account for the border width of the token field.
+ left: -1px;
+ min-width: initial;
+ max-height: $grid-unit-60 * 2; // Adjust to not cover the save button, showing three items.
+ }
+}
diff --git a/packages/interface/src/components/preferences-modal/README.md b/packages/interface/src/components/preferences-modal/README.md
index f873ccf297ec12..4327a59a7905ae 100644
--- a/packages/interface/src/components/preferences-modal/README.md
+++ b/packages/interface/src/components/preferences-modal/README.md
@@ -28,7 +28,7 @@ function MyEditorPreferencesModal() {
'Review settings, such as visibility and tags.'
) }
label={ __(
- 'Enable pre-publish flow'
+ 'Enable pre-publish checks'
) }
/>
diff --git a/packages/patterns/src/components/create-pattern-modal.js b/packages/patterns/src/components/create-pattern-modal.js
index b12e4c9d21bedf..e69390b9997521 100644
--- a/packages/patterns/src/components/create-pattern-modal.js
+++ b/packages/patterns/src/components/create-pattern-modal.js
@@ -28,11 +28,25 @@ import CategorySelector, { CATEGORY_SLUG } from './category-selector';
import { unlock } from '../lock-unlock';
export default function CreatePatternModal( {
+ className = 'patterns-menu-items__convert-modal',
+ modalTitle = __( 'Create pattern' ),
+ ...restProps
+} ) {
+ return (
+
+
+
+ );
+}
+
+export function CreatePatternModalContents( {
confirmLabel = __( 'Create' ),
defaultCategories = [],
- className = 'patterns-menu-items__convert-modal',
content,
- modalTitle = __( 'Create pattern' ),
onClose,
onError,
onSuccess,
@@ -148,78 +162,68 @@ export default function CreatePatternModal( {
return error.data.term_id;
}
}
-
return (
- {
- onClose();
- setTitle( '' );
+
-
+
+
+
+
);
}
diff --git a/packages/patterns/src/components/duplicate-pattern-modal.js b/packages/patterns/src/components/duplicate-pattern-modal.js
index a62e7306dc90e1..6fa25a16f8594c 100644
--- a/packages/patterns/src/components/duplicate-pattern-modal.js
+++ b/packages/patterns/src/components/duplicate-pattern-modal.js
@@ -29,11 +29,7 @@ function getTermLabels( pattern, categories ) {
.map( ( category ) => category.label );
}
-export default function DuplicatePatternModal( {
- pattern,
- onClose,
- onSuccess,
-} ) {
+export function useDuplicatePatternProps( { pattern, onSuccess } ) {
const { createSuccessNotice } = useDispatch( noticesStore );
const categories = useSelect( ( select ) => {
const { getUserPatternCategories, getBlockPatternCategories } =
@@ -44,12 +40,10 @@ export default function DuplicatePatternModal( {
user: getUserPatternCategories(),
};
} );
-
if ( ! pattern ) {
return null;
}
-
- const duplicatedProps = {
+ return {
content: pattern.content,
defaultCategories: getTermLabels( pattern, categories ),
defaultSyncType:
@@ -63,31 +57,39 @@ export default function DuplicatePatternModal( {
? pattern.title
: pattern.title.raw
),
- };
+ onSuccess: ( { pattern: newPattern } ) => {
+ createSuccessNotice(
+ sprintf(
+ // translators: %s: The new pattern's title e.g. 'Call to action (copy)'.
+ __( '"%s" duplicated.' ),
+ newPattern.title.raw
+ ),
+ {
+ type: 'snackbar',
+ id: 'patterns-create',
+ }
+ );
- function handleOnSuccess( { pattern: newPattern } ) {
- createSuccessNotice(
- sprintf(
- // translators: %s: The new pattern's title e.g. 'Call to action (copy)'.
- __( '"%s" duplicated.' ),
- newPattern.title.raw
- ),
- {
- type: 'snackbar',
- id: 'patterns-create',
- }
- );
+ onSuccess?.( { pattern: newPattern } );
+ },
+ };
+}
- onSuccess?.( { pattern: newPattern } );
+export default function DuplicatePatternModal( {
+ pattern,
+ onClose,
+ onSuccess,
+} ) {
+ const duplicatedProps = useDuplicatePatternProps( { pattern, onSuccess } );
+ if ( ! pattern ) {
+ return null;
}
-
return (
);
diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js
index b357efb1bc107a..046e20dd300039 100644
--- a/packages/patterns/src/private-apis.js
+++ b/packages/patterns/src/private-apis.js
@@ -2,8 +2,14 @@
* Internal dependencies
*/
import { lock } from './lock-unlock';
-import CreatePatternModal from './components/create-pattern-modal';
-import DuplicatePatternModal from './components/duplicate-pattern-modal';
+import {
+ default as CreatePatternModal,
+ CreatePatternModalContents,
+} from './components/create-pattern-modal';
+import {
+ default as DuplicatePatternModal,
+ useDuplicatePatternProps,
+} from './components/duplicate-pattern-modal';
import RenamePatternModal from './components/rename-pattern-modal';
import PatternsMenuItems from './components';
import RenamePatternCategoryModal from './components/rename-pattern-category-modal';
@@ -20,7 +26,9 @@ import {
export const privateApis = {};
lock( privateApis, {
CreatePatternModal,
+ CreatePatternModalContents,
DuplicatePatternModal,
+ useDuplicatePatternProps,
RenamePatternModal,
PatternsMenuItems,
RenamePatternCategoryModal,
diff --git a/test/e2e/specs/editor/various/pref-modal.spec.js b/test/e2e/specs/editor/various/pref-modal.spec.js
index f99c7d32a22a94..08c3cd322586a1 100644
--- a/test/e2e/specs/editor/various/pref-modal.spec.js
+++ b/test/e2e/specs/editor/various/pref-modal.spec.js
@@ -9,7 +9,7 @@ test.describe( 'Preferences modal', () => {
} );
test.describe( 'Preferences modal adaps to viewport', () => {
- test( 'Enable pre-publish flow is visible on desktop ', async ( {
+ test( 'Enable pre-publish checks is visible on desktop ', async ( {
page,
} ) => {
await page.click(
@@ -18,14 +18,14 @@ test.describe( 'Preferences modal', () => {
await page.click( 'role=menuitem[name="Preferences"i]' );
const prePublishToggle = page.locator(
- 'role=checkbox[name="Enable pre-publish flow"i]'
+ 'role=checkbox[name="Enable pre-publish checks"i]'
);
await expect( prePublishToggle ).toBeVisible();
} );
} );
test.describe( 'Preferences modal adaps to viewport', () => {
- test( 'Enable pre-publish flow is not visible on mobile ', async ( {
+ test( 'Enable pre-publish checks is not visible on mobile ', async ( {
page,
} ) => {
await page.setViewportSize( { width: 500, height: 800 } );
@@ -44,7 +44,7 @@ test.describe( 'Preferences modal', () => {
);
const prePublishToggle = page.locator(
- 'role=checkbox[name="Enable pre-publish flow"i]'
+ 'role=checkbox[name="Enable pre-publish checks"i]'
);
await expect( generalButton ).toBeVisible();