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( '' ); +
{ + event.preventDefault(); + onCreate( title, syncType ); } } - overlayClassName={ className } > - { - event.preventDefault(); - onCreate( title, syncType ); - } } - > - - + + + { + setSyncType( + syncType === PATTERN_SYNC_TYPES.full + ? PATTERN_SYNC_TYPES.unsynced + : PATTERN_SYNC_TYPES.full + ); + } } + /> + + + > + { __( 'Cancel' ) } + - - - -
-
+ + + + ); } 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();