diff --git a/lib/client-assets.php b/lib/client-assets.php index 5115cb990e684..351e8ed37f316 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -325,7 +325,7 @@ function gutenberg_register_packages_styles( $styles ) { $styles, 'wp-editor', gutenberg_url( 'build/editor/style.css' ), - array( 'wp-components', 'wp-block-editor', 'wp-nux' ), + array( 'wp-components', 'wp-block-editor', 'wp-nux', 'wp-reusable-blocks' ), filemtime( gutenberg_dir_path() . 'build/editor/style.css' ) ); $styles->add_data( 'wp-editor', 'rtl', 'replace' ); @@ -375,6 +375,7 @@ function gutenberg_register_packages_styles( $styles ) { 'wp-components', 'wp-editor', 'wp-block-library', + 'wp-reusable-blocks', // Always include visual styles so the editor never appears broken. 'wp-block-library-theme', ), @@ -431,7 +432,7 @@ function gutenberg_register_packages_styles( $styles ) { $styles, 'wp-edit-widgets', gutenberg_url( 'build/edit-widgets/style.css' ), - array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks' ), + array( 'wp-components', 'wp-block-editor', 'wp-edit-blocks', 'wp-reusable-blocks' ), filemtime( gutenberg_dir_path() . 'build/edit-widgets/style.css' ) ); $styles->add_data( 'wp-edit-widgets', 'rtl', 'replace' ); @@ -453,6 +454,15 @@ function gutenberg_register_packages_styles( $styles ) { filemtime( gutenberg_dir_path() . 'build/customize-widgets/style.css' ) ); $styles->add_data( 'wp-customize-widgets', 'rtl', 'replace' ); + + gutenberg_override_style( + $styles, + 'wp-reusable-blocks', + gutenberg_url( 'build/reusable-blocks/style.css' ), + array( 'wp-components' ), + filemtime( gutenberg_dir_path() . 'build/reusable-blocks/style.css' ) + ); + $styles->add_data( 'wp-reusable-block', 'rtl', 'replace' ); } add_action( 'wp_default_styles', 'gutenberg_register_packages_styles' ); diff --git a/lib/compat.php b/lib/compat.php index 008298df27096..a941e27245abc 100644 --- a/lib/compat.php +++ b/lib/compat.php @@ -243,27 +243,27 @@ function gutenberg_preload_edit_post( $preload_paths ) { */ function gutenberg_override_reusable_block_post_type_labels() { return array( - 'name' => _x( 'Reusable Blocks', 'post type general name', 'gutenberg' ), - 'singular_name' => _x( 'Reusable Block', 'post type singular name', 'gutenberg' ), - 'menu_name' => _x( 'Reusable Blocks', 'admin menu', 'gutenberg' ), - 'name_admin_bar' => _x( 'Reusable Block', 'add new on admin bar', 'gutenberg' ), - 'add_new' => _x( 'Add New', 'Reusable Block', 'gutenberg' ), - 'add_new_item' => __( 'Add New Reusable Block', 'gutenberg' ), - 'new_item' => __( 'New Reusable Block', 'gutenberg' ), - 'edit_item' => __( 'Edit Reusable Block', 'gutenberg' ), - 'view_item' => __( 'View Reusable Block', 'gutenberg' ), - 'all_items' => __( 'All Reusable Blocks', 'gutenberg' ), - 'search_items' => __( 'Search Reusable Blocks', 'gutenberg' ), + 'name' => _x( 'Reusable blocks', 'post type general name', 'gutenberg' ), + 'singular_name' => _x( 'Reusable block', 'post type singular name', 'gutenberg' ), + 'menu_name' => _x( 'Reusable blocks', 'admin menu', 'gutenberg' ), + 'name_admin_bar' => _x( 'Reusable block', 'add new on admin bar', 'gutenberg' ), + 'add_new' => _x( 'Add New', 'Reusable block', 'gutenberg' ), + 'add_new_item' => __( 'Add new Reusable block', 'gutenberg' ), + 'new_item' => __( 'New Reusable block', 'gutenberg' ), + 'edit_item' => __( 'Edit Reusable block', 'gutenberg' ), + 'view_item' => __( 'View Reusable block', 'gutenberg' ), + 'all_items' => __( 'All Reusable blocks', 'gutenberg' ), + 'search_items' => __( 'Search Reusable blocks', 'gutenberg' ), 'not_found' => __( 'No reusable blocks found.', 'gutenberg' ), 'not_found_in_trash' => __( 'No reusable blocks found in Trash.', 'gutenberg' ), 'filter_items_list' => __( 'Filter reusable blocks list', 'gutenberg' ), - 'items_list_navigation' => __( 'Reusable Blocks list navigation', 'gutenberg' ), - 'items_list' => __( 'Reusable Blocks list', 'gutenberg' ), - 'item_published' => __( 'Reusable Block published.', 'gutenberg' ), - 'item_published_privately' => __( 'Reusable Block published privately.', 'gutenberg' ), - 'item_reverted_to_draft' => __( 'Reusable Block reverted to draft.', 'gutenberg' ), - 'item_scheduled' => __( 'Reusable Block scheduled.', 'gutenberg' ), - 'item_updated' => __( 'Reusable Block updated.', 'gutenberg' ), + 'items_list_navigation' => __( 'Reusable blocks list navigation', 'gutenberg' ), + 'items_list' => __( 'Reusable blocks list', 'gutenberg' ), + 'item_published' => __( 'Reusable block published.', 'gutenberg' ), + 'item_published_privately' => __( 'Reusable block published privately.', 'gutenberg' ), + 'item_reverted_to_draft' => __( 'Reusable block reverted to draft.', 'gutenberg' ), + 'item_scheduled' => __( 'Reusable block scheduled.', 'gutenberg' ), + 'item_updated' => __( 'Reusable block updated.', 'gutenberg' ), ); } add_filter( 'post_type_labels_wp_block', 'gutenberg_override_reusable_block_post_type_labels', 10, 0 ); diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 324f898a8edda..1784a3941c8a4 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -120,6 +120,9 @@ $z-layers: ( // #adminmenuwrap { z-index: 9990 } ".components-popover": 1000000, + // Should be above the popover (dropdown) + ".reusable-blocks-menu-items__convert-modal": 1000001, + // ...Except for popovers immediately beneath wp-admin menu on large breakpoints ".components-popover.block-editor-inserter__popover": 99999, ".components-popover.table-of-contents__popover": 99998, diff --git a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js index 0a0877b4a629b..3afb07cd9a8bb 100644 --- a/packages/block-editor/src/components/inserter/reusable-blocks-tab.js +++ b/packages/block-editor/src/components/inserter/reusable-blocks-tab.js @@ -65,7 +65,7 @@ export function ReusableBlocksTab( { rootClientId, onInsert, onHover } ) { post_type: 'wp_block', } ) } > - { __( 'Manage all reusable blocks' ) } + { __( 'Manage Reusable blocks' ) } diff --git a/packages/block-library/src/block/index.js b/packages/block-library/src/block/index.js index ac570e09b2be3..0c6353eb1ebed 100644 --- a/packages/block-library/src/block/index.js +++ b/packages/block-library/src/block/index.js @@ -14,7 +14,7 @@ const { name } = metadata; export { metadata, name }; export const settings = { - title: _x( 'Reusable Block', 'block title' ), + title: _x( 'Reusable block', 'block title' ), description: __( 'Create and save content to reuse across your site. Update the block, and the changes apply everywhere it’s used.' ), diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js index 2e2805ad0d744..9aafc372cc5b4 100644 --- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js @@ -17,6 +17,8 @@ import { } from '@wordpress/e2e-test-utils'; const reusableBlockNameInputSelector = + '.reusable-blocks-menu-items__convert-modal .components-text-control__input'; +const reusableBlockInspectorNameInputSelector = '.block-editor-block-inspector .components-text-control__input'; const saveAll = async () => { @@ -49,10 +51,16 @@ const createReusableBlock = async ( content, title ) => { await clickBlockToolbarButton( 'Options' ); await clickMenuItem( 'Add to Reusable blocks' ); + const nameInput = await page.waitForSelector( + reusableBlockNameInputSelector + ); + await nameInput.click(); + await page.keyboard.type( title ); + await page.keyboard.press( 'Enter' ); // Wait for creation to finish await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Block created."]' + '//*[contains(@class, "components-snackbar")]/*[text()="Reusable block created."]' ); // Check that we have a reusable block on the page @@ -60,20 +68,6 @@ const createReusableBlock = async ( content, title ) => { '.block-editor-block-list__block[data-type="core/block"]' ); expect( block ).not.toBeNull(); - - await openDocumentSettingsSidebar(); - const nameInput = await page.waitForSelector( - reusableBlockNameInputSelector - ); - if ( title ) { - await nameInput.click(); - - // Select all of the text in the title field. - await pressKeyWithModifier( 'primary', 'a' ); - - // Give the reusable block a title - await page.keyboard.type( title ); - } }; describe( 'Reusable blocks', () => { @@ -85,19 +79,8 @@ describe( 'Reusable blocks', () => { await createNewPost(); } ); - it( 'can be created with no title', async () => { - await createReusableBlock( 'Hello there!' ); - await openDocumentSettingsSidebar(); - const title = await page.$eval( - reusableBlockNameInputSelector, - ( element ) => element.value - ); - expect( title ).toBe( 'Untitled Reusable Block' ); - } ); - it( 'can be created, inserted, edited and converted to a regular block.', async () => { await createReusableBlock( 'Hello there!', 'Greeting block' ); - await saveAll(); await clearAllBlocks(); // Insert the reusable block we created above @@ -106,7 +89,7 @@ describe( 'Reusable blocks', () => { // Change the block's title await openDocumentSettingsSidebar(); const nameInput = await page.waitForSelector( - reusableBlockNameInputSelector + reusableBlockInspectorNameInputSelector ); await nameInput.click(); await pressKeyWithModifier( 'primary', 'a' ); @@ -157,9 +140,6 @@ describe( 'Reusable blocks', () => { it( 'can be inserted after refresh', async () => { await createReusableBlock( 'Awesome Paragraph', 'Awesome block' ); - // Save the reusable block - await saveAll(); - // Step 2. Create new post. await createNewPost(); @@ -169,7 +149,7 @@ describe( 'Reusable blocks', () => { // Check the title. await openDocumentSettingsSidebar(); const title = await page.$eval( - reusableBlockNameInputSelector, + reusableBlockInspectorNameInputSelector, ( element ) => element.value ); expect( title ).toBe( 'Awesome block' ); @@ -192,22 +172,18 @@ describe( 'Reusable blocks', () => { await clickBlockToolbarButton( 'Options' ); await clickMenuItem( 'Add to Reusable blocks' ); - // Wait for creation to finish - await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Block created."]' - ); - - // Set title. - await openDocumentSettingsSidebar(); + // Set title const nameInput = await page.waitForSelector( reusableBlockNameInputSelector ); await nameInput.click(); - await pressKeyWithModifier( 'primary', 'a' ); await page.keyboard.type( 'Multi-selection reusable block' ); + await page.keyboard.press( 'Enter' ); - // Save the reusable block - await saveAll(); + // Wait for creation to finish + await page.waitForXPath( + '//*[contains(@class, "components-snackbar")]/*[text()="Reusable block created."]' + ); await clearAllBlocks(); @@ -226,7 +202,6 @@ describe( 'Reusable blocks', () => { 'Awesome Paragraph', 'Random reusable block' ); - await saveAll(); await clearAllBlocks(); await insertReusableBlock( 'Random reusable block' ); @@ -260,7 +235,7 @@ describe( 'Reusable blocks', () => { // Save the reusable block await page.click( publishButtonSelector ); await page.waitForXPath( - '//*[contains(@class, "components-snackbar")]/*[text()="Reusable Block updated."]' + '//*[contains(@class, "components-snackbar")]/*[text()="Reusable block updated."]' ); await createNewPost(); @@ -293,7 +268,6 @@ describe( 'Reusable blocks', () => { 'Awesome Paragraph', 'Duplicated reusable block' ); - await saveAll(); await clearAllBlocks(); await insertReusableBlock( 'Duplicated reusable block' ); await insertReusableBlock( 'Duplicated reusable block' ); diff --git a/packages/edit-post/src/plugins/index.js b/packages/edit-post/src/plugins/index.js index 87962ff46588a..2c9acb703d41d 100644 --- a/packages/edit-post/src/plugins/index.js +++ b/packages/edit-post/src/plugins/index.js @@ -30,7 +30,7 @@ registerPlugin( 'edit-post', { post_type: 'wp_block', } ) } > - { __( 'Manage all reusable blocks' ) } + { __( 'Manage Reusable blocks' ) } { const { canUser } = select( 'core' ); @@ -76,10 +85,10 @@ export default function ReusableBlockConvertButton( { noticesStore ); const onConvert = useCallback( - async function () { + async function ( reusableBlockTitle ) { try { - await convertBlocksToReusable( clientIds ); - createSuccessNotice( __( 'Block created.' ), { + await convertBlocksToReusable( clientIds, reusableBlockTitle ); + createSuccessNotice( __( 'Reusable block created.' ), { type: 'snackbar', } ); } catch ( error ) { @@ -98,15 +107,61 @@ export default function ReusableBlockConvertButton( { return ( { ( { onClose } ) => ( - { - onConvert(); - onClose(); - } } - > - { __( 'Add to Reusable blocks' ) } - + <> + { + setIsModalOpen( true ); + } } + > + { __( 'Add to Reusable blocks' ) } + + { isModalOpen && ( + { + setIsModalOpen( false ); + setTitle( '' ); + } } + overlayClassName="reusable-blocks-menu-items__convert-modal" + > +
{ + event.preventDefault(); + onConvert( title ); + setIsModalOpen( false ); + setTitle( '' ); + onClose(); + } } + > + + + + + + + + + + +
+ ) } + ) }
); diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/style.scss b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/style.scss new file mode 100644 index 0000000000000..b3c922076faf7 --- /dev/null +++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/style.scss @@ -0,0 +1,3 @@ +.reusable-blocks-menu-items__convert-modal { + z-index: z-index(".reusable-blocks-menu-items__convert-modal"); +} diff --git a/packages/reusable-blocks/src/store/actions.js b/packages/reusable-blocks/src/store/actions.js index 964caa8ef2b2a..b9c12490f9630 100644 --- a/packages/reusable-blocks/src/store/actions.js +++ b/packages/reusable-blocks/src/store/actions.js @@ -20,9 +20,10 @@ export function* __experimentalConvertBlockToStatic( clientId ) { * Returns a generator converting one or more static blocks into a reusable block. * * @param {string[]} clientIds The client IDs of the block to detach. + * @param {string} title Reusable block title. */ -export function* __experimentalConvertBlocksToReusable( clientIds ) { - yield convertBlocksToReusable( clientIds ); +export function* __experimentalConvertBlocksToReusable( clientIds, title ) { + yield convertBlocksToReusable( clientIds, title ); } /** diff --git a/packages/reusable-blocks/src/store/controls.js b/packages/reusable-blocks/src/store/controls.js index 308cb68934338..f3bfad7631423 100644 --- a/packages/reusable-blocks/src/store/controls.js +++ b/packages/reusable-blocks/src/store/controls.js @@ -31,13 +31,15 @@ export function convertBlockToStatic( clientId ) { /** * Convert a static block to a reusable block effect handler * - * @param {Array} clientIds Block IDs. + * @param {Array} clientIds Block IDs. + * @param {string} title Reusable block title. * @return {Object} control descriptor. */ -export function convertBlocksToReusable( clientIds ) { +export function convertBlocksToReusable( clientIds, title ) { return { type: 'CONVERT_BLOCKS_TO_REUSABLE', clientIds, + title, }; } @@ -77,9 +79,9 @@ const controls = { CONVERT_BLOCKS_TO_REUSABLE: createRegistryControl( ( registry ) => - async function ( { clientIds } ) { + async function ( { clientIds, title } ) { const reusableBlock = { - title: __( 'Untitled Reusable Block' ), + title: title || __( 'Untitled Reusable block' ), content: serialize( registry .select( 'core/block-editor' ) diff --git a/packages/reusable-blocks/src/store/test/controls.js b/packages/reusable-blocks/src/store/test/controls.js index a3387263630f5..4f51b737af901 100644 --- a/packages/reusable-blocks/src/store/test/controls.js +++ b/packages/reusable-blocks/src/store/test/controls.js @@ -30,7 +30,7 @@ describe( 'reusable blocks effects', () => { } ); registerBlockType( 'core/block', { - title: 'Reusable Block', + title: 'Reusable block', category: 'text', save: () => null, attributes: { @@ -74,7 +74,7 @@ describe( 'reusable blocks effects', () => { expect.objectContaining( { content: '', status: 'publish', - title: 'Untitled Reusable Block', + title: 'Untitled Reusable block', } ) ); expect( replaceBlocks ).toHaveBeenCalledWith( diff --git a/packages/reusable-blocks/src/style.scss b/packages/reusable-blocks/src/style.scss new file mode 100644 index 0000000000000..b55e8d8aa95d3 --- /dev/null +++ b/packages/reusable-blocks/src/style.scss @@ -0,0 +1 @@ +@import "./components/reusable-blocks-menu-items/style.scss"