diff --git a/.eslintrc.js b/.eslintrc.js index 7c89b96e997f6..adb816d615f63 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -189,11 +189,6 @@ const restrictedImports = [ message: "edit-widgets is a WordPress top level package that shouldn't be imported into other packages", }, - { - name: '@wordpress/edit-navigation', - message: - "edit-navigation is a WordPress top level package that shouldn't be imported into other packages", - }, ]; module.exports = { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ebddac0375d4d..aa19138a958f1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -50,9 +50,6 @@ /packages/customize-widgets @noisysocks /packages/widgets @noisysocks -# Navigation -/packages/edit-navigation @draganescu @talldan @tellthemachines @adamziel @kevin940726 @getdave - # Full Site Editing /packages/edit-site diff --git a/bin/plugin/commands/test/__snapshots__/changelog.js.snap b/bin/plugin/commands/test/__snapshots__/changelog.js.snap index ccdbb8e712cd1..d0af344550c04 100644 --- a/bin/plugin/commands/test/__snapshots__/changelog.js.snap +++ b/bin/plugin/commands/test/__snapshots__/changelog.js.snap @@ -43,6 +43,7 @@ exports[`getChangelog verify that the changelog is properly formatted 1`] = ` - Fix block appender position in classic themes. ([33895](https://github.com/WordPress/gutenberg/pull/33895)) - Fix misspelling of \\"queries\\" in filter documentation. ([33799](https://github.com/WordPress/gutenberg/pull/33799)) - Fix positioning discrepancy with draggable chip. ([33893](https://github.com/WordPress/gutenberg/pull/33893)) +- Navigation Editor: Avoid React warning when creating a new menu. ([33843](https://github.com/WordPress/gutenberg/pull/33843)) #### Block Library - Fix justification for button block when selected. ([33739](https://github.com/WordPress/gutenberg/pull/33739)) @@ -71,10 +72,6 @@ exports[`getChangelog verify that the changelog is properly formatted 1`] = ` #### CSS & Styling - Fix navigation block placeholder preview markup. ([33963](https://github.com/WordPress/gutenberg/pull/33963)) -#### Navigation Screen -- Fix regressed menu selection dropdown placeholder value for Nav Editor menu locations UI. ([33748](https://github.com/WordPress/gutenberg/pull/33748)) -- Navigation Editor: Avoid React warning when creating a new menu. ([33843](https://github.com/WordPress/gutenberg/pull/33843)) - #### Site Editor - Fix the site editor breaking in firefox. ([33896](https://github.com/WordPress/gutenberg/pull/33896)) @@ -87,6 +84,9 @@ exports[`getChangelog verify that the changelog is properly formatted 1`] = ` #### Build Tooling - Readable JS assets Plugin: Fix webpack 5 support. ([33785](https://github.com/WordPress/gutenberg/pull/33785)) +#### Navigation Screen +- Fix regressed menu selection dropdown placeholder value for Nav Editor menu locations UI. ([33748](https://github.com/WordPress/gutenberg/pull/33748)) + #### Accessibility - Fix some JAWS bugs. ([33627](https://github.com/WordPress/gutenberg/pull/33627)) diff --git a/bin/plugin/commands/test/fixtures/pull-requests.json b/bin/plugin/commands/test/fixtures/pull-requests.json index bb9a66a6e8e94..34f3c06c5d854 100644 --- a/bin/plugin/commands/test/fixtures/pull-requests.json +++ b/bin/plugin/commands/test/fixtures/pull-requests.json @@ -5099,24 +5099,6 @@ "color": "d93f0b", "default": false, "description": "An existing feature is broken." - }, - { - "id": 2074073931, - "node_id": "MDU6TGFiZWwyMDc0MDczOTMx", - "url": "https://api.github.com/repos/WordPress/gutenberg/labels/[Feature]%20Navigation%20Screen", - "name": "[Feature] Navigation Screen", - "color": "fbca04", - "default": false, - "description": "A new block-based screen intended to replace nav-menus.php." - }, - { - "id": 2536082965, - "node_id": "MDU6TGFiZWwyNTM2MDgyOTY1", - "url": "https://api.github.com/repos/WordPress/gutenberg/labels/[Package]%20Edit%20Navigation", - "name": "[Package] Edit Navigation", - "color": "ed2572", - "default": false, - "description": "/packages/edit-navigation" } ], "state": "closed", @@ -8617,15 +8599,6 @@ "color": "fbca04", "default": false, "description": "A new block-based screen intended to replace nav-menus.php." - }, - { - "id": 2536082965, - "node_id": "MDU6TGFiZWwyNTM2MDgyOTY1", - "url": "https://api.github.com/repos/WordPress/gutenberg/labels/[Package]%20Edit%20Navigation", - "name": "[Package] Edit Navigation", - "color": "ed2572", - "default": false, - "description": "/packages/edit-navigation" } ], "state": "closed", diff --git a/lib/client-assets.php b/lib/client-assets.php index e23e1c4b89bd7..0f6e64c27c014 100644 --- a/lib/client-assets.php +++ b/lib/client-assets.php @@ -385,15 +385,6 @@ function gutenberg_register_packages_styles( $styles ) { ); $styles->add_data( 'wp-list-reusable-block', 'rtl', 'replace' ); - gutenberg_override_style( - $styles, - 'wp-edit-navigation', - gutenberg_url( 'build/edit-navigation/style.css' ), - array( 'wp-components', 'wp-block-editor', 'wp-editor', 'wp-edit-blocks' ), - $version - ); - $styles->add_data( 'wp-edit-navigation', 'rtl', 'replace' ); - gutenberg_override_style( $styles, 'wp-edit-site', diff --git a/lib/experimental/navigation-page.php b/lib/experimental/navigation-page.php deleted file mode 100644 index 77949d76aee53..0000000000000 --- a/lib/experimental/navigation-page.php +++ /dev/null @@ -1,173 +0,0 @@ - - - $results_per_page, - 'context' => 'edit', - '_locale' => 'user', - ) - ); -} - -/** - * This function returns an url for the /wp/v2/menu-items endpoint - * - * @since 11.8.0 - * - * @param int $menu_id Menu ID. - * @param int $results_per_page Results per page. - * @return string - */ -function gutenberg_navigation_get_menu_items_endpoint( $menu_id, $results_per_page = 100 ) { - return '/wp/v2/menu-items?' . build_query( - array( - 'context' => 'edit', - 'menus' => $menu_id, - 'per_page' => $results_per_page, - '_locale' => 'user', - ) - ); -} - -/** - * This function returns an url for the /wp/v2/types endpoint - * - * @since 11.8.0 - * - * @return string - */ -function gutenberg_navigation_get_types_endpoint() { - return '/wp/v2/types?' . build_query( - array( - 'context' => 'edit', - ) - ); -} - -/** - * Initialize the Gutenberg Navigation page. - * - * @param string $hook Page. - * @since 7.8.0 - */ -function gutenberg_navigation_init( $hook ) { - if ( 'gutenberg_page_gutenberg-navigation' !== $hook ) { - return; - } - - $menus = wp_get_nav_menus(); - $first_menu_id = ! empty( $menus ) ? $menus[0]->term_id : null; - - $preload_paths = array( - '/wp/v2/menu-locations', - array( '/wp/v2/pages', 'OPTIONS' ), - array( '/wp/v2/posts', 'OPTIONS' ), - gutenberg_navigation_get_types_endpoint(), - ); - - if ( $first_menu_id ) { - $preload_paths[] = gutenberg_navigation_get_menu_items_endpoint( $first_menu_id ); - } - - $custom_settings = array( - 'blockNavMenus' => false, - // We should uncomment the line below when the block-nav-menus feature becomes stable. - // @see https://github.com/WordPress/gutenberg/issues/34265. - /*'blockNavMenus' => get_theme_support( 'block-nav-menus' ),*/ - ); - - $context = new WP_Block_Editor_Context( array( 'name' => 'core/edit-navigation' ) ); - $settings = get_block_editor_settings( $custom_settings, $context ); - gutenberg_initialize_editor( - 'navigation_editor', - 'edit-navigation', - array( - 'initializer_name' => 'initialize', - 'editor_settings' => $settings, - 'preload_paths' => $preload_paths, - ) - ); - - gutenberg_navigation_editor_preload_menus(); - - wp_enqueue_script( 'wp-edit-navigation' ); - wp_enqueue_style( 'wp-edit-navigation' ); - wp_enqueue_script( 'wp-format-library' ); - wp_enqueue_style( 'wp-format-library' ); - do_action( 'enqueue_block_editor_assets' ); -} -add_action( 'admin_enqueue_scripts', 'gutenberg_navigation_init' ); - -/** - * Tells the script loader to load the scripts and styles of custom block on navigation editor screen. - * - * @param bool $is_block_editor_screen Current decision about loading block assets. - * @return bool Filtered decision about loading block assets. - */ -function gutenberg_navigation_editor_load_block_editor_scripts_and_styles( $is_block_editor_screen ) { - if ( is_callable( 'get_current_screen' ) && get_current_screen() && 'gutenberg_page_gutenberg-navigation' === get_current_screen()->base ) { - return true; - } - - return $is_block_editor_screen; -} - -add_filter( 'should_load_block_editor_scripts_and_styles', 'gutenberg_navigation_editor_load_block_editor_scripts_and_styles' ); - -/** - * This function calls createMenuPreloadingMiddleware middleware because - * we need to use custom preloading logic for menus. - * - * @return void - */ -function gutenberg_navigation_editor_preload_menus() { - $menus_data = array_reduce( - array( - gutenberg_navigation_get_menus_endpoint(), - ), - 'rest_preload_api_request', - array() - ); - - if ( ! $menus_data ) { - return; - } - - wp_add_inline_script( - 'wp-edit-navigation', - sprintf( - 'wp.apiFetch.use( wp.editNavigation.__unstableCreateMenuPreloadingMiddleware( %s ) );', - wp_json_encode( $menus_data ) - ), - 'after' - ); -} diff --git a/lib/load.php b/lib/load.php index 71e48264a73f3..9467312a7b955 100644 --- a/lib/load.php +++ b/lib/load.php @@ -94,7 +94,6 @@ function gutenberg_is_experiment_enabled( $name ) { } require __DIR__ . '/experimental/blocks.php'; require __DIR__ . '/experimental/navigation-theme-opt-in.php'; -require __DIR__ . '/experimental/navigation-page.php'; require __DIR__ . '/experimental/kses.php'; // Fonts API. diff --git a/package-lock.json b/package-lock.json index 75f24270366df..25ee3155936ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17705,35 +17705,6 @@ "uuid": "^8.3.0" } }, - "@wordpress/edit-navigation": { - "version": "file:packages/edit-navigation", - "requires": { - "@babel/runtime": "^7.16.0", - "@wordpress/api-fetch": "file:packages/api-fetch", - "@wordpress/block-editor": "file:packages/block-editor", - "@wordpress/block-library": "file:packages/block-library", - "@wordpress/blocks": "file:packages/blocks", - "@wordpress/components": "file:packages/components", - "@wordpress/compose": "file:packages/compose", - "@wordpress/core-data": "file:packages/core-data", - "@wordpress/data": "file:packages/data", - "@wordpress/dom": "file:packages/dom", - "@wordpress/element": "file:packages/element", - "@wordpress/hooks": "file:packages/hooks", - "@wordpress/html-entities": "file:packages/html-entities", - "@wordpress/i18n": "file:packages/i18n", - "@wordpress/icons": "file:packages/icons", - "@wordpress/interface": "file:packages/interface", - "@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts", - "@wordpress/keycodes": "file:packages/keycodes", - "@wordpress/notices": "file:packages/notices", - "@wordpress/plugins": "file:packages/plugins", - "@wordpress/preferences": "file:packages/preferences", - "@wordpress/url": "file:packages/url", - "classnames": "^2.3.1", - "lodash": "^4.17.21" - } - }, "@wordpress/edit-post": { "version": "file:packages/edit-post", "requires": { diff --git a/package.json b/package.json index b8aa12b89fb0e..137a0e3b4b8bc 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/dom": "file:packages/dom", "@wordpress/dom-ready": "file:packages/dom-ready", - "@wordpress/edit-navigation": "file:packages/edit-navigation", "@wordpress/edit-post": "file:packages/edit-post", "@wordpress/edit-site": "file:packages/edit-site", "@wordpress/edit-widgets": "file:packages/edit-widgets", diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 28f3a14ac1d95..0e3cf596a24c5 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -135,7 +135,6 @@ $z-layers: ( ".components-popover.interface-more-menu__content": 99998, ".components-popover.block-editor-rich-text__inline-format-toolbar": 99998, ".components-popover.block-editor-warning__dropdown": 99998, - ".components-popover.edit-navigation-menu-actions__switcher-dropdown": 99998, ".components-autocomplete__results": 1000000, @@ -170,7 +169,6 @@ $z-layers: ( ".components-circular-option-picker__option.is-pressed": 1, // Needs to be higher than .components-circular-option-picker__option.is-pressed. ".components-circular-option-picker__option.is-pressed + svg": 2, - ".edit-navigation-layout__overlay": 999, // Appear under the customizer heading UI, but over anything else. ".customize-widgets__topbar": 8, diff --git a/packages/block-library/src/navigation/menu-items-to-blocks.js b/packages/block-library/src/navigation/menu-items-to-blocks.js index 2ffc374978157..00bd4dbbe2a98 100644 --- a/packages/block-library/src/navigation/menu-items-to-blocks.js +++ b/packages/block-library/src/navigation/menu-items-to-blocks.js @@ -97,8 +97,6 @@ function mapMenuItemsToBlocks( menuItems, level = 0 ) { * For more documentation on the individual fields present on a menu item please see: * https://core.trac.wordpress.org/browser/tags/5.7.1/src/wp-includes/nav-menu.php#L789 * - * Changes made here should also be mirrored in packages/edit-navigation/src/store/utils.js. - * * @typedef WPNavMenuItem * * @property {Object} title stores the raw and rendered versions of the title/label for this menu item. diff --git a/packages/e2e-tests/specs/experiments/__snapshots__/navigation-editor.test.js.snap b/packages/e2e-tests/specs/experiments/__snapshots__/navigation-editor.test.js.snap deleted file mode 100644 index bee7b0da9d1be..0000000000000 --- a/packages/e2e-tests/specs/experiments/__snapshots__/navigation-editor.test.js.snap +++ /dev/null @@ -1,39 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Navigation editor allows creation of a menu when there are existing menu items 1`] = `""`; - -exports[`Navigation editor allows creation of a menu when there are no current menu items 1`] = ` -" - -" -`; - -exports[`Navigation editor displays the first created menu when at least one menu exists 1`] = ` -" - - - - - - - - - - - - - - - - - - - - - - - - - -" -`; diff --git a/packages/e2e-tests/specs/experiments/navigation-editor.test.js b/packages/e2e-tests/specs/experiments/navigation-editor.test.js deleted file mode 100644 index be8d9c9a2bdb3..0000000000000 --- a/packages/e2e-tests/specs/experiments/navigation-editor.test.js +++ /dev/null @@ -1,850 +0,0 @@ -/** - * External dependencies - */ -// eslint-disable-next-line no-restricted-imports -import { find, findAll } from 'puppeteer-testing-library'; - -/** - * WordPress dependencies - */ -import { - createJSONResponse, - createMenu, - deleteAllMenus, - pressKeyTimes, - pressKeyWithModifier, - setBrowserViewport, - setUpResponseMocking, - visitAdminPage, - __experimentalRest as rest, -} from '@wordpress/e2e-test-utils'; -import { addQueryArgs } from '@wordpress/url'; - -/** - * Internal dependencies - */ -import { useExperimentalFeatures } from './experimental-features'; -import menuItemsFixture from './fixtures/menu-items-request-fixture.json'; - -const TYPE_NAMES = { - post: 'post', - page: 'page', - post_tag: 'tag', - category: 'category', -}; - -const searchFixture = [ - { - id: 300, - title: 'Home', - url: 'https://example.com/home', - type: 'post', - subtype: 'page', - }, - { - id: 301, - title: 'About', - url: 'https://example.com/about', - type: 'post', - subtype: 'page', - }, - { - id: 302, - title: 'Boats', - url: 'https://example.com/?cat=123', - type: 'category', - }, - { - id: 303, - title: 'Faves', - url: 'https://example.com/?tag=456', - type: 'post_tag', - }, -]; - -// Matching against variations of the same URL encoded and non-encoded -// produces the most reliable mocking. -const REST_SEARCH_ROUTES = [ - '/wp/v2/search', - `rest_route=${ encodeURIComponent( '/wp/v2/search' ) }`, -]; - -const REST_PAGES_ROUTES = [ - '/wp/v2/pages', - `rest_route=${ encodeURIComponent( '/wp/v2/pages' ) }`, -]; - -/** - * Determines if a given URL matches any of a given collection of - * routes (expressed as substrings). - * - * @param {string} reqUrl the full URL to be tested for matches. - * @param {Array} routes array of strings to match against the URL. - */ -function matchUrlToRoute( reqUrl, routes ) { - return routes.some( ( route ) => reqUrl.includes( route ) ); -} - -function getEndpointMocks( matchingRoutes, responsesByMethod ) { - return [ 'GET', 'POST', 'DELETE', 'PUT' ].reduce( ( mocks, restMethod ) => { - if ( responsesByMethod[ restMethod ] ) { - return [ - ...mocks, - { - match: ( request ) => - matchUrlToRoute( request.url(), matchingRoutes ) && - request.method() === restMethod, - onRequestMatch: createJSONResponse( - responsesByMethod[ restMethod ] - ), - }, - ]; - } - - return mocks; - }, [] ); -} - -function getSearchMocks( responsesByMethod ) { - return getEndpointMocks( REST_SEARCH_ROUTES, responsesByMethod ); -} - -function getPagesMocks( responsesByMethod ) { - return getEndpointMocks( REST_PAGES_ROUTES, responsesByMethod ); -} - -async function visitNavigationEditor() { - const query = addQueryArgs( '', { - page: 'gutenberg-navigation', - } ); - await visitAdminPage( '/admin.php', query ); -} - -/** - * Get a list of the editor's current blocks for use in a snapshot. - * - * @return {string} Block HTML. - */ -async function getSerializedBlocks() { - const blocks = await page.evaluate( () => - wp.data.select( 'core/block-editor' ).getBlocks() - ); - const safeBlocks = replaceUnstableBlockAttributes( blocks ); - return page.evaluate( - ( blocksToSerialize ) => wp.blocks.serialize( blocksToSerialize ), - safeBlocks - ); -} - -/** - * Some block attributes contain values that are not stable from test run to - * test run and can't be compared with a snapshot. These are typically the - * ids of posts or pages that are created prior to each test run. Replace those - * values with their types before serializing to a snapshot. - * - * @param {Array} blocks The array of block data. - * - * @return {Array} Updated array of block data. - */ -function replaceUnstableBlockAttributes( blocks ) { - return blocks?.map( ( block ) => { - const id = block.attributes.id ? typeof block.attributes.id : undefined; - const url = block.attributes.url - ? typeof block.attributes.url - : undefined; - - return { - ...block, - attributes: { - ...block.attributes, - id, - url, - }, - innerBlocks: replaceUnstableBlockAttributes( block.innerBlocks ), - }; - } ); -} - -async function deleteAllLinkedResources() { - [ '/wp/v2/posts', '/wp/v2/pages' ].forEach( async ( path ) => { - const items = await rest( { path } ); - - for ( const item of items ) { - await rest( { - method: 'DELETE', - path: `${ path }/${ item.id }?force=true`, - } ); - } - } ); -} - -async function openMenuActionsDropdown() { - const menuActionsDropdown = await page.waitForXPath( - '//*[@role="region"][@aria-label="Navigation top bar"]//*[@class="edit-navigation-menu-actions"]//button[@aria-expanded="false"]' - ); - await menuActionsDropdown.click(); -} - -async function getMenuItem( menuItemName ) { - return await page - .waitForXPath( - `//*[@role="group"]//*[@role="menuitemradio"]/span[text()="${ menuItemName }"]` - ) - .catch( ( error ) => { - if ( error.name !== 'TimeoutError' ) { - throw error; - } else { - return null; - } - } ); -} - -describe.skip( 'Navigation editor', () => { - useExperimentalFeatures( [ '#gutenberg-navigation' ] ); - - beforeAll( async () => { - await deleteAllMenus(); - await deleteAllLinkedResources(); - } ); - - afterEach( async () => { - await deleteAllMenus(); - await deleteAllLinkedResources(); - await setUpResponseMocking( [] ); - } ); - - it( 'allows creation of a menu when there are no current menu items', async () => { - await visitNavigationEditor(); - await setUpResponseMocking( [ - ...getPagesMocks( { - GET: [ - { - type: 'page', - id: 1, - link: 'https://example.com/1', - title: { - rendered: 'My page', - }, - }, - ], - } ), - ] ); - - // Wait for the header to show that no menus are available. - await page.waitForXPath( '//h3[.="Create your first menu"]', { - visible: true, - } ); - - await page.keyboard.type( 'Main Menu' ); - const createMenuButton = await page.waitForXPath( - '//button[contains(., "Create menu")]' - ); - await createMenuButton.click(); - - // A snackbar will appear when menu creation has completed. - await page.waitForXPath( '//div[contains(., "Menu created")]' ); - - // Select the navigation block and create a block from existing pages. - const navigationBlock = await page.waitForSelector( - 'div[aria-label="Block: Navigation"]' - ); - await navigationBlock.click(); - - const addAllPagesButton = await page.waitForXPath( - '//button[contains(., "Add all pages")]' - ); - await addAllPagesButton.click(); - - // When the block is created the root element changes from a div (for the placeholder) - // to a nav (for the navigation itself). Wait for this to happen. - await page.waitForSelector( 'nav[aria-label="Block: Navigation"]' ); - - expect( await getSerializedBlocks() ).toMatchSnapshot(); - } ); - - it( 'allows creation of a menu when there are existing menu items', async () => { - await createMenu( { name: 'Test Menu 1' }, menuItemsFixture ); - await createMenu( { name: 'Test Menu 2' }, menuItemsFixture ); - await visitNavigationEditor(); - - // Wait for the header to show the menu name. - await page.waitForXPath( '//h2[contains(., "Test Menu 1")]', { - visible: true, - } ); - - const createMenuButton = await page.waitForXPath( - '//button[.="New menu"]' - ); - await createMenuButton.click(); - - const menuNameInputLabel = await page.waitForXPath( - '//form//label[.="Menu name"]' - ); - await menuNameInputLabel.click(); - - await page.keyboard.type( 'New menu' ); - await page.keyboard.press( 'Enter' ); - - // A snackbar will appear when menu creation has completed. - await page.waitForXPath( '//div[contains(., "Menu created")]' ); - - // An empty navigation block will appear. - await page.waitForSelector( 'div[aria-label="Block: Navigation"]' ); - - expect( await getSerializedBlocks() ).toMatchSnapshot(); - } ); - - it( 'displays the first created menu when at least one menu exists', async () => { - await createMenu( { name: 'Test Menu 1' }, menuItemsFixture ); - await createMenu( { name: 'Test Menu 2' }, menuItemsFixture ); - - await visitNavigationEditor(); - - // Wait for the header to show the menu name. - await page.waitForXPath( '//h2[contains(., "Test Menu 1")]', { - visible: true, - } ); - - // Wait for the block to be present. - await page.waitForSelector( 'nav[aria-label="Block: Navigation"]' ); - - expect( await getSerializedBlocks() ).toMatchSnapshot(); - } ); - - it( 'shows the trailing block appender within the navigation block when no blocks are selected', async () => { - // The test requires the presence of existing menus. - await createMenu( { name: 'Test Menu 1' }, menuItemsFixture ); - await visitNavigationEditor(); - - // Wait for at least one block to be present on the page. - await page.waitForSelector( '.wp-block' ); - - // And for this test to be valid, no blocks should be selected, which - // should be the case when the editor loads. - const selectedBlocks = await page.$$( '.wp-block.is-selected' ); - expect( selectedBlocks.length ).toBe( 0 ); - - // And when no blocks are selected, the trailing appender is present. - const blockListAppender = await page.waitForSelector( - '.block-list-appender button[aria-label="Add block"]' - ); - expect( blockListAppender ).toBeTruthy(); - } ); - - it( 'has a disabled undo button when an existing menu is loaded', async () => { - // The test requires the presence of existing menus. - await createMenu( { name: 'Test Menu 1' }, menuItemsFixture ); - await visitNavigationEditor(); - - // Wait for at least one block to be present on the page. - await page.waitForSelector( '.wp-block' ); - - // Check whether there's a disabled undo button. - const disabledUndoButton = await page.waitForSelector( - 'button[aria-label="Undo"][aria-disabled="true"]' - ); - expect( disabledUndoButton ).toBeTruthy(); - } ); - - it( 'shows a submenu when a link is selected and hides it when clicking the editor to deselect it', async () => { - await createMenu( { name: 'Test Menu 1' }, menuItemsFixture ); - await visitNavigationEditor(); - - // Select a submenu block with nested links in a submenu. - const parentLinkXPath = - '//div[@aria-label="Block: Submenu" and contains(.,"WordPress.org")]'; - const linkBlock = await page.waitForXPath( parentLinkXPath ); - await linkBlock.click(); - - // There should be a submenu link visible. - // - // Submenus are hidden using `visibility: hidden` and shown using - // `visibility: visible` so the visible/hidden options must be used - // when selecting the elements. - const submenuLinkXPath = `${ parentLinkXPath }//div[@aria-label="Block: Custom Link"]`; - const submenuLinkVisible = await page.waitForXPath( submenuLinkXPath, { - visible: true, - } ); - expect( submenuLinkVisible ).toBeDefined(); - - // Click in the top left corner of the canvas. - const canvas = await page.$( '.edit-navigation-layout__content-area' ); - const boundingBox = await canvas.boundingBox(); - await page.mouse.click( boundingBox.x + 5, boundingBox.y + 5 ); - - // There should be a submenu in the DOM, but it should be hidden. - const submenuLinkHidden = await page.waitForXPath( submenuLinkXPath, { - hidden: true, - } ); - expect( submenuLinkHidden ).toBeDefined(); - } ); - - it( 'displays suggestions when adding a link', async () => { - await createMenu( { name: 'Test Menu 1' } ); - await setUpResponseMocking( [ - ...getSearchMocks( { GET: searchFixture } ), - ] ); - - await visitNavigationEditor(); - - // Wait for the block to be present and start an empty block. - const navBlock = await page.waitForSelector( - 'div[aria-label="Block: Navigation"]' - ); - await navBlock.click(); - const startEmptyButton = await page.waitForXPath( - '//button[.="Start blank"]' - ); - await startEmptyButton.click(); - - const appender = await page.waitForSelector( - 'button[aria-label="Add block"]' - ); - await appender.click(); - - await page.waitForSelector( 'input[aria-label="URL"]' ); - - // The link suggestions should be searchable. - for ( let i = 0; i < searchFixture.length; i++ ) { - const { title, type, subtype, url } = searchFixture[ i ]; - const expectedURL = url.replace( 'https://', '' ); - const expectedType = TYPE_NAMES[ subtype || type ]; - - await page.keyboard.type( title ); - const suggestionTitle = await page.waitForXPath( - `//button[@role="option"]//span[.="${ title }"]` - ); - const suggestionType = await page.waitForXPath( - `//button[@role="option"]//span[.="${ expectedType }"]` - ); - const suggestionURL = await page.waitForXPath( - `//button[@role="option"]//span[.="${ expectedURL }"]` - ); - expect( suggestionTitle ).toBeTruthy(); - expect( suggestionType ).toBeTruthy(); - expect( suggestionURL ).toBeTruthy(); - await pressKeyWithModifier( 'primary', 'A' ); - } - } ); - - describe( 'Menu name editor', () => { - const initialMenuName = 'Main Menu'; - const nameEditorSelector = '.edit-navigation-name-editor__text-control'; - const inputSelector = `${ nameEditorSelector } input`; - - it( 'saves menu name changes', async () => { - await createMenu( { name: initialMenuName } ); - await visitNavigationEditor(); - - // Rename the menu and save it. - const newName = 'New menu'; - await page.waitForSelector( inputSelector ); - await page.click( inputSelector ); - await pressKeyTimes( 'Backspace', initialMenuName.length ); - await page.keyboard.type( newName ); - await page.click( '.edit-navigation-toolbar__save-button' ); - await page.waitForSelector( '.components-snackbar' ); - await page.reload(); - - // Expect the header to have the new name. - const headerSubtitle = await page.waitForSelector( - '.edit-navigation-menu-actions__subtitle' - ); - const headerSubtitleText = await headerSubtitle.evaluate( - ( element ) => element.innerText - ); - expect( headerSubtitleText ).toBe( newName ); - } ); - - // Flaky test, see https://github.com/WordPress/gutenberg/pull/34869#issuecomment-922711557. - it.skip( 'does not save a menu name upon clicking save button when name is empty', async () => { - await createMenu( { name: initialMenuName } ); - await visitNavigationEditor(); - - // Try saving a menu with an empty name. - await page.waitForSelector( inputSelector ); - await page.click( inputSelector ); - await pressKeyTimes( 'Backspace', initialMenuName.length ); - await page.click( '.edit-navigation-toolbar__save-button' ); - const snackbar = await page.waitForSelector( - '.components-snackbar', - { visible: true } - ); - const snackbarText = await snackbar.evaluate( - ( element ) => element.innerText - ); - expect( snackbarText ).toBe( - "Unable to save: 'A name is required for this term.'" - ); - expect( console ).toHaveErrored( - 'Failed to load resource: the server responded with a status of 500 (Internal Server Error)' - ); - await page.reload(); - - // Expect the header to have the old name. - const headerSubtitle = await page.waitForSelector( - '.edit-navigation-menu-actions__subtitle' - ); - const headerSubtitleText = await headerSubtitle.evaluate( - ( element ) => element.innerText - ); - expect( headerSubtitleText ).toBe( initialMenuName ); - } ); - } ); - - describe( 'Change detections', () => { - beforeEach( async () => { - await createMenu( { name: 'Main' } ); - await visitNavigationEditor(); - } ); - - async function assertIsDirty( isDirty ) { - let hadDialog = false; - - function handleOnDialog() { - hadDialog = true; - } - - try { - page.on( 'dialog', handleOnDialog ); - await page.reload(); - - // Ensure whether it was expected that dialog was encountered. - expect( hadDialog ).toBe( isDirty ); - } catch ( error ) { - throw error; - } finally { - page.removeListener( 'dialog', handleOnDialog ); - } - } - - // eslint-disable-next-line jest/no-disabled-tests - it.skip( 'should not prompt to confirm unsaved changes for the newly selected menu', async () => { - await assertIsDirty( false ); - } ); - - // eslint-disable-next-line jest/no-disabled-tests - it.skip( 'should prompt to confirm unsaved changes when menu name is edited', async () => { - await page.type( - '.edit-navigation-name-editor__text-control input', - ' Menu' - ); - - await assertIsDirty( true ); - } ); - } ); - - describe( 'Sidebar inserter', () => { - it( 'disables inserter toggle when Navigation block is in placeholder state', async () => { - await createMenu( { name: 'Main Menu' } ); - await visitNavigationEditor(); - - // Wait for the block to be present. - await expect( { - role: 'document', - name: 'Block: Navigation', - } ).toBeFound(); - - // Check for the placeholder state. - await expect( { - role: 'button', - name: 'Start blank', - } ).toBeFound(); - - // Expect the block inserter to be disabled. - await expect( { - name: 'Toggle block inserter', - disabled: true, - role: 'button', - } ).toBeFound(); - } ); - - it( 'enables inserter toggle when Navigation block is in editable state', async () => { - await createMenu( { name: 'Main Menu' }, menuItemsFixture ); - await visitNavigationEditor(); - - // Wait for the block to be present. - await expect( { - role: 'document', - name: 'Block: Navigation', - } ).toBeFound(); - - // Expect the block inserter to be found. - await expect( { - name: 'Toggle block inserter', - role: 'button', - } ).toBeFound(); - - // Work around bug where `find` with `disabled=false` doesn't return anything. - const isEnabled = await page.$eval( - '[aria-label="Toggle block inserter"]', - ( element ) => ! element.disabled - ); - - expect( isEnabled ).toBeTruthy(); - } ); - - it( 'toggles the inserter sidebar open and closed', async () => { - await createMenu( { name: 'Main Menu' }, menuItemsFixture ); - await visitNavigationEditor(); - - // Wait for the block to be present. - await expect( { - role: 'document', - name: 'Block: Navigation', - } ).toBeFound(); - - // Expect inserter sidebar to **not** be in the DOM. - await expect( { - role: 'region', - name: 'Block library', - } ).not.toBeFound(); - - const inserterToggle = await find( { - name: 'Toggle block inserter', - role: 'button', - } ); - - await inserterToggle.click(); - - // Expect the inserter sidebar to be present in the DOM. - await expect( { - role: 'region', - name: 'Block library', - } ).toBeFound(); - - // Expect block search input to be focused. - await expect( { - role: 'searchbox', - name: 'Search for blocks and patterns', - focused: true, - } ).toBeFound(); - } ); - - it( 'inserts items at end of Navigation block by default', async () => { - await setUpResponseMocking( [ - ...getSearchMocks( { GET: searchFixture } ), - ] ); - await createMenu( { name: 'Main Menu' }, menuItemsFixture ); - - await visitNavigationEditor(); - - // Wait for the block to be present. - await expect( { - role: 'document', - name: 'Block: Navigation', - } ).toBeFound(); - - const inserterToggle = await find( { - name: 'Toggle block inserter', - role: 'button', - } ); - - await inserterToggle.click(); - - // Expect the inserter sidebar to be present in the DOM. - await expect( { - role: 'region', - name: 'Block library', - } ).toBeFound(); - - // Add Custom Link item. - const customLinkOption = await find( { - name: 'Custom Link', - role: 'option', - } ); - - customLinkOption.click(); - - // Expect that inserter is auto-closed. - await expect( { - role: 'region', - name: 'Block library', - } ).not.toBeFound(); - - // Expect to be focused inside the Link UI search input. - await expect( { - role: 'combobox', - name: 'URL', - focused: true, - } ).toBeFound(); - - const [ itemToSelect ] = searchFixture; - - // Add Custom Link item. - const [ firstSearchSuggestion ] = await findAll( { - role: 'option', - name: `${ itemToSelect.title } ${ itemToSelect.subtype }`, - } ); - - await firstSearchSuggestion.click(); - - // Get the title/label of the last Nav item inside the Nav block. - const lastItemAttributes = await page.evaluate( () => { - const { getBlockOrder, getBlocks } = - wp.data.select( 'core/block-editor' ); - - const lockedNavigationBlock = getBlockOrder()[ 0 ]; - - const navItemBlocks = getBlocks( lockedNavigationBlock ); - - const { attributes } = - navItemBlocks[ navItemBlocks.length - 1 ]; - - return attributes; - } ); - - // Check the last item is the one we just inserted. - expect( lastItemAttributes.label ).toEqual( itemToSelect.title ); - expect( lastItemAttributes.isTopLevelLink ).toBeTruthy(); - } ); - } ); - - describe( 'Delete menu button', () => { - useExperimentalFeatures( [ '#gutenberg-navigation' ] ); - - beforeAll( async () => { - await deleteAllMenus(); - await deleteAllLinkedResources(); - } ); - - afterEach( async () => { - await deleteAllMenus(); - await deleteAllLinkedResources(); - } ); - - afterEach( async () => { - await setBrowserViewport( 'large' ); - } ); - it.each( [ 'large', 'small' ] )( - `should retain menu when confirmation is canceled and the viewport is %s`, - async ( viewport ) => { - const menuName = 'Menu delete test'; - await createMenu( { name: menuName }, menuItemsFixture ); - await visitNavigationEditor(); - await setBrowserViewport( viewport ); - // Wait for the header to show the menu name. - await page.waitForXPath( - `//*[@role="region"][@aria-label="Navigation top bar"]//h2[contains(text(), "${ menuName }")]` - ); - - if ( viewport === 'small' ) { - const openSettingsSidebar = await page.waitForXPath( - '//button[@aria-label="Settings"][@aria-expanded="false"]' - ); - await openSettingsSidebar.click(); - } - - const deleteMenuButton = await page.waitForXPath( - '//*[@role="region"][@aria-label="Navigation settings"]//button[text()="Delete menu"]' - ); - await deleteMenuButton.click(); - - const cancelButton = await page.waitForXPath( - '//*[@role="dialog"]//button[text()="Cancel"]' - ); - await cancelButton.click(); - - const menuActionsDropdown = await page.waitForXPath( - `//*[contains(@class,"edit-navigation-menu-actions")]//h2[text()="${ menuName }"]` - ); - const currentSelectedMenu = await page.evaluate( - ( el ) => el.textContent, - menuActionsDropdown - ); - - expect( currentSelectedMenu ).toBe( menuName ); - } - ); - it.each( [ 'large', 'small' ] )( - `should delete menu when confirmation is confirmed and there are no other menus and the viewport is %s`, - async ( viewport ) => { - const menuName = 'Menu delete test'; - await createMenu( { name: menuName }, menuItemsFixture ); - await visitNavigationEditor(); - await setBrowserViewport( viewport ); - // Wait for the header to show the menu name. - await page.waitForXPath( - `//*[@role="region"][@aria-label="Navigation top bar"]//h2[contains(text(), "${ menuName }")]` - ); - if ( viewport === 'small' ) { - const openSettingsSidebar = await page.waitForXPath( - '//button[@aria-label="Settings"][@aria-expanded="false"]' - ); - await openSettingsSidebar.click(); - } - - const deleteMenuButton = await page.waitForXPath( - '//*[@role="region"][@aria-label="Navigation settings"]//button[text()="Delete menu"]' - ); - await deleteMenuButton.click(); - - const confirmButton = await page.waitForXPath( - '//*[@role="dialog"]//button[text()="OK"]' - ); - await confirmButton.click(); - - await page.waitForXPath( - `//*[@role="button"][@aria-label="Dismiss this notice"]//*[text()='"${ menuName }" menu has been deleted']` - ); - - // If the "Create your first menu" prompt appears, we know there are no remaining menus, - // so our test menu must have been deleted successfully. - const createFirstMenuPrompt = await page.waitForXPath( - '//h3[.="Create your first menu"]', - { - visible: true, - } - ); - const noMenusRemaining = createFirstMenuPrompt ? true : false; - expect( noMenusRemaining ).toBe( true ); - } - ); - - it.each( [ 'large', 'small' ] )( - `should delete menu when confirmation is confirmed and there are other existing menus and the viewport is %s`, - async () => { - const menuName = 'Menu delete test'; - await createMenu( { name: menuName }, menuItemsFixture ); - await createMenu( - { name: `${ menuName } 2` }, - menuItemsFixture - ); - await visitNavigationEditor(); - // Wait for the header to show the menu name - await page.waitForXPath( - `//*[@role="region"][@aria-label="Navigation top bar"]//h2[contains(text(), "${ menuName }")]` - ); - - // Confirm both test menus are present - openMenuActionsDropdown(); - const firstTestMenuItem = await getMenuItem( menuName ); - const secondTestMenuItem = await getMenuItem( - `${ menuName } 2` - ); - - expect( firstTestMenuItem ).not.toBeNull(); - expect( secondTestMenuItem ).not.toBeNull(); - - // Delete the first test menu - const deleteMenuButton = await page.waitForXPath( - '//*[@role="region"][@aria-label="Navigation settings"]//button[text()="Delete menu"]' - ); - await deleteMenuButton.click(); - - const confirmButton = await page.waitForXPath( - '//*[@role="dialog"]//button[text()="OK"]' - ); - await confirmButton.click(); - - await page.waitForXPath( - `//*[@role="button"][@aria-label="Dismiss this notice"]//*[text()='"${ menuName }" menu has been deleted']` - ); - - openMenuActionsDropdown(); - const deletedTestMenuItem = await getMenuItem( menuName ); - expect( deletedTestMenuItem ).toBeNull(); - } - ); - } ); -} ); diff --git a/packages/edit-navigation/.npmrc b/packages/edit-navigation/.npmrc deleted file mode 100644 index 43c97e719a5a8..0000000000000 --- a/packages/edit-navigation/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/packages/edit-navigation/CHANGELOG.md b/packages/edit-navigation/CHANGELOG.md deleted file mode 100644 index 660506373402d..0000000000000 --- a/packages/edit-navigation/CHANGELOG.md +++ /dev/null @@ -1,15 +0,0 @@ - - -## Unreleased - -### Bug Fix - -- Removed unused `@wordpress/dom-ready`, `@wordpress/media-util`, `rememo` and `uuid` dependencies ([#38388](https://github.com/WordPress/gutenberg/pull/38388)). - -### Internal - -- Removed `getStablePath` function. Please use `normalizePath` from `@wordpress/url` package instead ([#35992](https://github.com/WordPress/gutenberg/pull/35992)). - -## 1.0.0 - -- Initial version of the package. diff --git a/packages/edit-navigation/README.md b/packages/edit-navigation/README.md deleted file mode 100644 index 0f0fd254bb966..0000000000000 --- a/packages/edit-navigation/README.md +++ /dev/null @@ -1,188 +0,0 @@ -# Edit navigation - -Edit Navigation page module for WordPress - a Gutenberg-based UI for editing navigation menus. - -> This package is meant to be used only with WordPress core. Feel free to use it in your own project but please keep in mind that it might never get fully documented. - -## Usage - -```js -/** - * WordPress dependencies - */ -import { initialize } from '@wordpress/edit-navigation'; - -/** - * Internal dependencies - */ -import blockEditorSettings from './block-editor-settings'; - -initialize( '#navigation-editor-root', blockEditorSettings ); -``` - -## Purpose - -By default, the Navigation Editor screen allows users to create and edit complex navigations using a block-based UI. The aim is to supersede [the current Menus screen](https://codex.wordpress.org/WordPress_Menu_User_Guide) by providing a superior experience whilst retaining backwards compatibility. - -The editing experience is provided as a block editor wrapper around the core functionality of the **Navigation _block_**. Features of the block are disabled/enhanced as necessary to provide an experience appropriate to editing a navigation outside of a Full Site Editing context. - -## Modes - -The Navigation Editor has two "modes" for _persistence_ ("saving" navigations) and _rendering_: - -1. **Classic (default)** - navigations are saved to the _existing_ (post type powered) Menus system and rendered using standard Walker classes. -2. **Block-based** (opt _in_) - navigations continue to be _saved_ using the existing post type system, but: - - the [navigation is _rendered_ using the `core/navigation` block](https://github.com/WordPress/gutenberg/blob/7fcd57c9a62c232899e287f6d96416477d810d5e/lib/navigation.php#L228) (as opposed to Walker) to provide access to the full power of blocks (with some tradeoffs in terms of backwards compatibility). - - non-link blocks (anything that is not `core/navigation-link`) are saved as _blocks_. - -### Classic Mode - -In this mode, navigations created in the Navigation Editor are stored using the _existing Menu post type_ (`nav_menu_item`) system. As this method matches that used in the _existing_ Menus screen, there is a smooth upgrade path to using new Navigation Editor screen to edit navigations. - -Moreover, when the navigation is rendered on the front of the site the system continues to use [the classic Navigation "Walker" class](https://developer.wordpress.org/reference/classes/walker_nav_menu/), thereby ensuring the HTML markup remains the same when using a classic Theme. - -### Block-based Mode - -**Important**: block-based mode has been temporarily **_disabled_** until it becomes stable. So, if a theme declares support for the `block-nav-menus` feature it will not affect the frontend. - -If desired, themes are able to opt into [_rendering_ complete block-based menus](https://github.com/WordPress/gutenberg/blob/7fcd57c9a62c232899e287f6d96416477d810d5e/lib/navigation.php#L228) using the Navigation Editor. This allows for arbitrarily complex navigation block structures to be used in an existing theme whilst still ensuring the navigation data is still _saved_ to the existing (post type powered) Menus system. - -Themes can opt into this behaviour by declaring: - -```php -add_theme_support( 'block-nav-menus' ); -``` - -This unlocks significant additional capabilities in the Navigation Editor. For example, by default, [the Navigation Editor screen only allows _link_ (`core/navigation-link`) blocks to be inserted into a navigation](https://github.com/WordPress/gutenberg/blob/7fcd57c9a62c232899e287f6d96416477d810d5e/packages/edit-navigation/src/filters/disable-inserting-non-navigation-blocks.js). When a theme opts into `block-nav-menus` however, users are able to add non-link blocks to a navigation using the Navigation Editor screen, including: - -- `core/navigation-link`. -- `core/social`. -- `core/search`. - -As these items are still saved to `nav_menu_items` this ensures if we ever revert to classic (Walker-based) rendering, these items will still be rendered (as blocks). - -## Backwards compatibility - -By design the underlying systems of the Nav Editor screen should be largely backwards compatible with the existing Menus screen. Therefore any navigations created or edited using the new Navigation Editor screen should continue to work in the existing classic Menus screen. - -Currently, the only exception to this would be any custom functionality added (by Plugins or otherwise) to the existing Menus screen would not be replicated in the new Navigation Editor screen. In this scenario there might be danger of some data loss. - -### Downgrading from block-based to classic Themes - -If the user switches to a theme that does not support block menus, or disables this functionality, ~non-link blocks are no longer rendered on the frontend~ [block-based links will still be rendered on the front end](https://github.com/WordPress/gutenberg/blob/7310097da5e16159b79e6e039a2cb3812cb9055e/lib/navigation.php#L104-L135). Care is also taken to ensure that users can still see their data on the existing Menus screen. - -## Block to Menu Item mapping - -The Navigation Editor needs to be able to map navigation items in two directions: - -1. `nav_menu_item`s to Blocks - when displaying an existing navigation. -2. Blocks to `nav_menu_item`s - when _saving_ an navigation being editing in the Navigation screen. - -The Navigation Editor has two dedicated methods for handling mapping between these two expressions of the data: - -- [`menuItemToBlockAttributes()`](https://github.com/WordPress/gutenberg/blob/7fcd57c9a62c232899e287f6d96416477d810d5e/packages/edit-navigation/src/store/utils.js#L261-L313). -- [`blockAttributestoMenuItem()`](https://github.com/WordPress/gutenberg/blob/7fcd57c9a62c232899e287f6d96416477d810d5e/packages/edit-navigation/src/store/utils.js#L184-L253) - -To understand these fully, one must appreciate that WordPress maps raw `nav_menu_item` posts to [Menu item _objects_](https://core.trac.wordpress.org/browser/tags/5.7.1/src/wp-includes/nav-menu.php#L786). These have various properties which map as follows: - -| Menu Item object property | Equivalent Block Attribute | Description | -| :------------------------ | :----------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: | -| `ID` | Not mapped. | The term_id if the menu item represents a taxonomy term. | -| `attr_title` | `title` | The title attribute of the link element for this menu item. | -| `classes` | `classNames` | The array of class attribute values for the link element of this menu item. | -| `db_id` | Not mapped. | The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist). | -| `description` | `description` | The description of this menu item. | -| `menu_item_parent` | Not mapped.[1](#menu_item_menu_item_parent) | The DB ID of the nav_menu_item that is this item's menu parent, if any. 0 otherwise. | -| `object` | `type` | The type of object originally represented, such as 'category', 'post', or 'attachment'. | -| `object_id` | `id` | The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories. | -| `post_parent` | Not mapped. | The DB ID of the original object's parent object, if any (0 otherwise). | -| `post_title` | Not mapped. | A "no title" label if menu item represents a post that lacks a title. | -| `target` | `opensInNewTab`[2](#menu_item_target) | The target attribute of the link element for this menu item. | -| `title` | `label` | The title of this menu item. | -| `type` | `kind` | The family of objects originally represented, such as 'post_type' or 'taxonomy'. | -| `type_label` | Not mapped. | The singular label used to describe this type of menu item. | -| `url` | `url` | The URL to which this menu item points. | -| `xfn` | `rel` | The XFN relationship expressed in the link of this menu item. | -| `\_invalid` | Not mapped. | Whether the menu item represents an object that no longer exists. | - -- [1] - the parent -> child relationship is expressed in block via the `innerBlocks` attribute and is therefore not required as a explicit block attribute. -- [2] - applies only if the value of the `target` field is `_blank`. - -### Inconsistencies - -#### Mapping - -For historical reasons, the following properties display some inconsistency in their mapping from Menu Item Object to Block attribute: - -- `type` -> `kind` - the family of objects is stored as `kind` on the block and so must be mapped accordingly. -- `object` -> `type` - the type of object is stored as `type` on the block and so must be mapped accordingly. -- `object_id` -> `id` - the block stores a reference to the original object's ID as the `id` _attribute_. This should not be confused with the block's `clientId` which is unrelated. -- `attr_title` -> `title` - the HTML `title` attribute is stored as `title` on the block and so must be mapped accordingly. - -#### Object Types - -- Menu Item objects which represent "Tags" are stored in WordPress as `post_tag` but the block expects their `type` attribute to be `tag` (omiting the `post_` suffix). This inconsistency is accounted for in [the mapping utilities methods](https://github.com/WordPress/gutenberg/blob/7fcd57c9a62c232899e287f6d96416477d810d5e/packages/edit-navigation/src/store/utils.js#L279-L281). - -## Hooks - -The `useNavigationEditor` and `useEntityBlockEditor` hooks are the central part of this package. They bridge the gap between the API and the block editor interface: - -```jsx -// Data from API: -const { - menus, - hasLoadedMenus, - selectedMenuId, - navigationPost, -} = useNavigationEditor(); - -// Working state: -const [ blocks, onInput, onChange ] = useEntityBlockEditor( - NAVIGATION_POST_KIND, - NAVIGATION_POST_POST_TYPE, - { - id: navigationPost?.id, - } -); - -const isBlockEditorReady = !! ( - menus?.length && - navigationPost && - selectedMenuId -); - -return ( - - { isBlockEditorReady && ( -
- - - -
- ) } -
-); -``` - -## Glossary - -- **(Navigation) link** - the basic `core/navigation-link` block which is the standard block used to add links within navigations. -- **Block-based link** - any navigation item that is _not_ a `core/navigation-link` block. These are persisted as blocks but still utilise the existing Menus post type system. -- **Navigation block** - the root `core/navigation` block which can be used both with the Navigation Editor and outside (eg: Post / Site Editor). -- **Navigation editor / screen** - the new screen provided by Gutenberg to allow the user to edit navigations using a block-based UI. -- **Menus screen** - the current/existing [interface/screen for managing Menus](https://codex.wordpress.org/WordPress_Menu_User_Guide) in WordPress WPAdmin. - -_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for such language features and APIs, you should include [the polyfill shipped in `@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill) in your code._ - -## Contributing to this package - -This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects. - -To find out more about contributing to this package or Gutenberg as a whole, please read the project's main [contributor guide](https://github.com/WordPress/gutenberg/tree/HEAD/CONTRIBUTING.md). - -

Code is Poetry.

diff --git a/packages/edit-navigation/docs/README.md b/packages/edit-navigation/docs/README.md deleted file mode 100644 index f55b91dc44a7b..0000000000000 --- a/packages/edit-navigation/docs/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Documentation - -This folder contains documentations for the Navigation Editor. - -1. [User Documentation](./user-documentation.md) diff --git a/packages/edit-navigation/docs/user-documentation.md b/packages/edit-navigation/docs/user-documentation.md deleted file mode 100644 index d12a1bb78cb2d..0000000000000 --- a/packages/edit-navigation/docs/user-documentation.md +++ /dev/null @@ -1,86 +0,0 @@ -Note: this documentation is a work in progress. - -# Block-based navigation editor - -The Block-based Navigation Editor brings the power of blocks to the Appearance > Navigation section in the WordPress Administration Screens allowing you use blocks to create menus. - -## How to use the editor - -The interface replicates the Post Editor experience, allowing you to use similar workflows like drag and drop. - -The editor provides access to any menus created using the previous Menus screen in WordPress. - -New menus can easily be created, options are provided to do so in a few different ways: -* from an existing page hierarchy -* by copying an existing menu -* by creating a blank menu - - -Rather than the paragraph being the go-to block, the Navigation Editor provides a link block for creating links to different types of items. Page, post, tag, category and custom links can all be created. Links can be nested to create sub-menus. - -### How to create a menu - -1. At the top of the editor select 'New menu'. -2. Type the name of your menu. -3. Choose a starting option of either 'Start blank', 'Add all pages', or 'Copy existing menu'. - -### How to insert a link - -If you'd like to link to a page, post, tag, or category: - -1. Click one of the `+` buttons. -2. Select the type of link you want to add. -3. Use the dialog to search for the item you want to link to. - -For a custom link: - -1. Click one of the `+` buttons. -2. Select the 'Custom Link' block. -3. Using the dialog that appears, type in a URL and press 'Enter'. - -### How to insert a submenu - -There are two ways to insert a submenu. - -The first option is to create a new submenu from scratch: - -1. Click one of the `+` buttons. -2. Select the 'Submenu' block. -3. (Optional) Add a URL for the submenu. -4. Use the nested `+` button in the submenu to add blocks in the submenu - -Alternatively, an existing link can be converted into a submenu: - -1. Select an existing link block. -2. From the toolbar, select the 'Add submenu' button. - -### How to rename a menu - -1. If the settings sidebar isn't visible, click the 'Settings' button in the top-right corner of the screen. -2. Select the 'Menu' tab. -3. Use the name field at the top of the sidebar to rename a menu. -4. Save any changes. - -### Assigning a menu to a theme location - -A theme location designates the part of your site that a menu is displayed. To assign, change or unassign a theme location. - -1. If the settings sidebar isn't visible, click the 'Settings' button in the top-right corner of the screen. -2. Select the 'Menu' tab. -3. Use the Theme Locations panel to select which locations the menu should be assigned to. -4. Save any changes. - -For an overview of which menus are assigned to which locations, use the 'Manage locations' button in the same part of the screen. - -### Deleting a menu - -1. Choose a menu to delete using the menu selector in the middle of the editor's header. -2. If the settings sidebar isn't visible, click the 'Settings' button in the top-right corner of the screen. -3. Select the 'Menu' tab. -4. Click the 'Delete menu' button. - -### How to opt-in or out of using the block-based navigation editor - -1. From WordPress admin, select the Gutenberg > Experiments option. -2. Uncheck the tickbox to disable the editor or check it to enable it. -3. Save changes. diff --git a/packages/edit-navigation/package.json b/packages/edit-navigation/package.json deleted file mode 100644 index ea504ea09bd06..0000000000000 --- a/packages/edit-navigation/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "@wordpress/edit-navigation", - "version": "1.9.22", - "private": true, - "description": "Module for the Navigation page in WordPress.", - "author": "The WordPress Contributors", - "license": "GPL-2.0-or-later", - "keywords": [ - "wordpress", - "gutenberg", - "editor", - "navigation" - ], - "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/packages/edit-navigation/README.md", - "repository": { - "type": "git", - "url": "https://github.com/WordPress/gutenberg.git", - "directory": "packages/edit-navigation" - }, - "bugs": { - "url": "https://github.com/WordPress/gutenberg/issues" - }, - "engines": { - "node": ">=12" - }, - "main": "build/index.js", - "module": "build-module/index.js", - "react-native": "src/index", - "dependencies": { - "@babel/runtime": "^7.16.0", - "@wordpress/api-fetch": "file:../api-fetch", - "@wordpress/block-editor": "file:../block-editor", - "@wordpress/block-library": "file:../block-library", - "@wordpress/blocks": "file:../blocks", - "@wordpress/components": "file:../components", - "@wordpress/compose": "file:../compose", - "@wordpress/core-data": "file:../core-data", - "@wordpress/data": "file:../data", - "@wordpress/dom": "file:../dom", - "@wordpress/element": "file:../element", - "@wordpress/hooks": "file:../hooks", - "@wordpress/html-entities": "file:../html-entities", - "@wordpress/i18n": "file:../i18n", - "@wordpress/icons": "file:../icons", - "@wordpress/interface": "file:../interface", - "@wordpress/keyboard-shortcuts": "file:../keyboard-shortcuts", - "@wordpress/keycodes": "file:../keycodes", - "@wordpress/notices": "file:../notices", - "@wordpress/plugins": "file:../plugins", - "@wordpress/preferences": "file:../preferences", - "@wordpress/url": "file:../url", - "classnames": "^2.3.1", - "lodash": "^4.17.21" - }, - "publishConfig": { - "access": "public" - } -} diff --git a/packages/edit-navigation/src/components/add-menu/index.js b/packages/edit-navigation/src/components/add-menu/index.js deleted file mode 100644 index 936aed5d9b3b5..0000000000000 --- a/packages/edit-navigation/src/components/add-menu/index.js +++ /dev/null @@ -1,114 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { useState, useEffect } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { Button, TextControl, withNotices } from '@wordpress/components'; -import { useFocusOnMount } from '@wordpress/compose'; -import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; -import { store as noticesStore } from '@wordpress/notices'; -import { store as coreStore } from '@wordpress/core-data'; - -/** - * Internal dependencies - */ -import { MENU_POST_TYPE, MENU_KIND } from '../../constants'; - -function AddMenu( { - className, - onCreate, - titleText, - helpText, - focusInputOnMount = false, - noticeUI, - noticeOperations, -} ) { - const inputRef = useFocusOnMount( focusInputOnMount ); - const [ menuName, setMenuName ] = useState( '' ); - const [ isCreatingMenu, setIsCreatingMenu ] = useState( false ); - const { createInfoNotice } = useDispatch( noticesStore ); - const { saveMenu } = useDispatch( coreStore ); - - const { createErrorNotice, removeAllNotices } = noticeOperations; - - const lastSaveError = useSelect( ( select ) => { - return select( coreStore ).getLastEntitySaveError( - MENU_KIND, - MENU_POST_TYPE - ); - }, [] ); - - useEffect( () => { - if ( lastSaveError ) { - createErrorNotice( stripHTML( lastSaveError?.message ) ); - } - }, [ lastSaveError ] ); - - const createMenu = async ( event ) => { - event.preventDefault(); - - if ( ! menuName.length || isCreatingMenu ) { - return; - } - - setIsCreatingMenu( true ); - - // Remove any existing notices. - removeAllNotices(); - - const menu = await saveMenu( { name: menuName } ); - - setIsCreatingMenu( false ); - - if ( menu ) { - createInfoNotice( __( 'Menu created' ), { - type: 'snackbar', - isDismissible: true, - } ); - if ( onCreate ) { - onCreate( menu.id ); - } - } - }; - - return ( -
- { noticeUI } - { titleText && ( -

- { titleText } -

- ) } - - - - - ); -} - -export default withNotices( AddMenu ); diff --git a/packages/edit-navigation/src/components/add-menu/style.scss b/packages/edit-navigation/src/components/add-menu/style.scss deleted file mode 100644 index 835fb912545a0..0000000000000 --- a/packages/edit-navigation/src/components/add-menu/style.scss +++ /dev/null @@ -1,27 +0,0 @@ -.edit-navigation-add-menu { - display: flex; - flex-direction: column; - - // Notices. - .components-with-notices-ui { - margin-bottom: $grid-unit-20; - - // Notice is too big with default styles. - .components-notice { - margin: 0; - padding: $grid-unit-10 $grid-unit-15; - } - - .components-notice__content { - margin: 0; - } - } -} - -.edit-navigation-add-menu__title { - margin-top: 0; -} - -.edit-navigation-add-menu__create-menu-button { - align-self: flex-end; -} diff --git a/packages/edit-navigation/src/components/block-placeholder/index.js b/packages/edit-navigation/src/components/block-placeholder/index.js deleted file mode 100644 index 48c2171dd4916..0000000000000 --- a/packages/edit-navigation/src/components/block-placeholder/index.js +++ /dev/null @@ -1,187 +0,0 @@ -/** - * WordPress dependencies - */ -import { createBlock } from '@wordpress/blocks'; -import { - Placeholder, - Button, - DropdownMenu, - MenuGroup, - MenuItem, - Spinner, -} from '@wordpress/components'; -import { - forwardRef, - useCallback, - useState, - useEffect, -} from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { chevronDown } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import { useMenuEntityProp, useSelectedMenuId } from '../../hooks'; -import useNavigationEntities from './use-navigation-entities'; -import { menuItemsToBlocks } from '../../store/transform'; - -/** - * Convert pages to blocks. - * - * @param {Object[]} pages An array of pages. - * - * @return {WPBlock[]} An array of blocks. - */ -function convertPagesToBlocks( pages ) { - if ( ! pages?.length ) { - return null; - } - - return pages.map( ( { title, type, link: url, id } ) => - createBlock( 'core/navigation-link', { - type, - id, - url, - label: ! title.rendered ? __( '(no title)' ) : title.rendered, - opensInNewTab: false, - } ) - ); -} - -const TOGGLE_PROPS = { variant: 'tertiary' }; -const POPOVER_PROPS = { position: 'bottom center' }; - -function BlockPlaceholder( { onCreate }, ref ) { - const [ selectedMenu, setSelectedMenu ] = useState(); - const [ isCreatingFromMenu, setIsCreatingFromMenu ] = useState( false ); - - const [ selectedMenuId ] = useSelectedMenuId(); - const [ menuName ] = useMenuEntityProp( 'name', selectedMenuId ); - - const { - isResolvingPages, - menus, - isResolvingMenus, - menuItems, - hasResolvedMenuItems, - pages, - hasPages, - hasMenus, - } = useNavigationEntities( selectedMenu ); - - const isLoading = isResolvingPages || isResolvingMenus; - - const createFromMenu = useCallback( () => { - const { innerBlocks: blocks } = menuItemsToBlocks( menuItems ); - const selectNavigationBlock = true; - onCreate( blocks, selectNavigationBlock ); - }, [ menuItems, menuItemsToBlocks, onCreate ] ); - - const onCreateFromMenu = () => { - // If we have menu items, create the block right away. - if ( hasResolvedMenuItems ) { - createFromMenu(); - return; - } - - // Otherwise, create the block when resolution finishes. - setIsCreatingFromMenu( true ); - }; - - const onCreateEmptyMenu = () => { - onCreate( [] ); - }; - - const onCreateAllPages = () => { - const blocks = convertPagesToBlocks( pages ); - const selectNavigationBlock = true; - onCreate( blocks, selectNavigationBlock ); - }; - - useEffect( () => { - // If the user selected a menu but we had to wait for menu items to - // finish resolving, then create the block once resolution finishes. - if ( isCreatingFromMenu && hasResolvedMenuItems ) { - createFromMenu(); - setIsCreatingFromMenu( false ); - } - }, [ isCreatingFromMenu, hasResolvedMenuItems ] ); - - const selectableMenus = menus?.filter( - ( menu ) => menu.id !== selectedMenuId - ); - - const hasSelectableMenus = !! selectableMenus?.length; - - return ( - -
- { isLoading && ( -
- -
- ) } - { ! isLoading && ( -
- - { hasPages ? ( - - ) : undefined } - { hasSelectableMenus ? ( - - { ( { onClose } ) => ( - - { selectableMenus.map( ( menu ) => { - return ( - { - setSelectedMenu( - menu.id - ); - onCreateFromMenu(); - } } - onClose={ onClose } - key={ menu.id } - > - { menu.name } - - ); - } ) } - - ) } - - ) : undefined } -
- ) } -
-
- ); -} - -export default forwardRef( BlockPlaceholder ); diff --git a/packages/edit-navigation/src/components/block-placeholder/menu-items-to-blocks.js b/packages/edit-navigation/src/components/block-placeholder/menu-items-to-blocks.js deleted file mode 100644 index e16d1e38cba58..0000000000000 --- a/packages/edit-navigation/src/components/block-placeholder/menu-items-to-blocks.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * WordPress dependencies - */ -import { createBlock } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import { menuItemToBlockAttributes } from '../../store/transform'; - -/** - * Convert a flat menu item structure to a nested blocks structure. - * - * @param {Object[]} menuItems An array of menu items. - * - * @return {WPBlock[]} An array of blocks. - */ -export default function menuItemsToBlocks( menuItems ) { - if ( ! menuItems ) { - return null; - } - - const menuTree = createDataTree( menuItems ); - return mapMenuItemsToBlocks( menuTree ); -} - -/** @typedef {import('../..store/utils').WPNavMenuItem} WPNavMenuItem */ - -/** - * A recursive function that maps menu item nodes to blocks. - * - * @param {WPNavMenuItem[]} menuItems An array of WPNavMenuItem items. - * @return {Object} Object containing innerBlocks and mapping. - */ -function mapMenuItemsToBlocks( menuItems ) { - let mapping = {}; - - // The menuItem should be in menu_order sort order. - const sortedItems = [ ...menuItems ].sort( - ( a, b ) => a.menu_order - b.menu_order - ); - - const innerBlocks = sortedItems.map( ( menuItem ) => { - const attributes = menuItemToBlockAttributes( menuItem ); - - // If there are children recurse to build those nested blocks. - const { - innerBlocks: nestedBlocks = [], // alias to avoid shadowing - mapping: nestedMapping = {}, // alias to avoid shadowing - } = menuItem.children?.length - ? mapMenuItemsToBlocks( menuItem.children ) - : {}; - - // Update parent mapping with nested mapping. - mapping = { - ...mapping, - ...nestedMapping, - }; - - // Create block with nested "innerBlocks". - const block = createBlock( - 'core/navigation-link', - attributes, - nestedBlocks - ); - - // Create mapping for menuItem -> block. - mapping[ menuItem.id ] = block.clientId; - - return block; - } ); - - return { - innerBlocks, - mapping, - }; -} - -/** - * Creates a nested, hierarchical tree representation from unstructured data that - * has an inherent relationship defined between individual items. - * - * For example, by default, each element in the dataset should have an `id` and - * `parent` property where the `parent` property indicates a relationship between - * the current item and another item with a matching `id` properties. - * - * This is useful for building linked lists of data from flat data structures. - * - * @param {Array} dataset linked data to be rearranged into a hierarchical tree based on relational fields. - * @param {string} id the property which uniquely identifies each entry within the array. - * @param {*} relation the property which identifies how the current item is related to other items in the data (if at all). - * @return {Array} a nested array of parent/child relationships - */ -function createDataTree( dataset, id = 'id', relation = 'parent' ) { - const hashTable = Object.create( null ); - const dataTree = []; - - for ( const data of dataset ) { - hashTable[ data[ id ] ] = { - ...data, - children: [], - }; - if ( data[ relation ] ) { - hashTable[ data[ relation ] ] = hashTable[ data[ relation ] ] || {}; - hashTable[ data[ relation ] ].children = - hashTable[ data[ relation ] ].children || []; - hashTable[ data[ relation ] ].children.push( - hashTable[ data[ id ] ] - ); - } else { - dataTree.push( hashTable[ data[ id ] ] ); - } - } - - return dataTree; -} diff --git a/packages/edit-navigation/src/components/block-placeholder/style.scss b/packages/edit-navigation/src/components/block-placeholder/style.scss deleted file mode 100644 index bd9b6eab7c9a9..0000000000000 --- a/packages/edit-navigation/src/components/block-placeholder/style.scss +++ /dev/null @@ -1,54 +0,0 @@ -.edit-navigation-block-placeholder { - // The navigation editor already has a border around content. - // Hide the placeholder's border. Requires extra specificity. - &.edit-navigation-block-placeholder { - box-shadow: none; - background: transparent; - - @include break-medium() { - margin: -$grid-unit-20 0; - } - } - - // Show placeholder instructions when it's a medium size. - &.is-medium .components-placeholder__instructions { - display: block; - } - - // Display buttons in a column when placeholder is small. - .edit-navigation-block-placeholder__actions { - display: flex; - flex-direction: column; - align-items: flex-start; - - .components-button { - margin-bottom: $grid-unit-05; - margin-right: 0; - - // Avoid bottom margin on the dropdown since it makes the - // menu anchor itself too far away from the button. - &.components-dropdown-menu__toggle { - margin-bottom: 0; - - svg { - // Make the spacing inside the left of the button match the - // spacing inside the right of the button. - margin-left: -6px; - } - } - } - } - - @include break-medium() { - .edit-navigation-block-placeholder__actions { - flex-direction: row; - } - - // Change the default button margin. Again use extra specificity. - &.edit-navigation-block-placeholder.is-medium .components-button { - margin-bottom: 0; - margin-right: $grid-unit-15; - } - } - -} diff --git a/packages/edit-navigation/src/components/block-placeholder/use-navigation-entities.js b/packages/edit-navigation/src/components/block-placeholder/use-navigation-entities.js deleted file mode 100644 index 4271877b8d0db..0000000000000 --- a/packages/edit-navigation/src/components/block-placeholder/use-navigation-entities.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; -import { store as coreStore, useEntityRecords } from '@wordpress/core-data'; - -/** - * @typedef {Object} NavigationEntitiesData - * @property {Array|undefined} pages - a collection of WP Post entity objects of post type "Page". - * @property {boolean} isResolvingPages - indicates whether the request to fetch pages is currently resolving. - * @property {boolean} hasResolvedPages - indicates whether the request to fetch pages has finished resolving. - * @property {Array|undefined} menus - a collection of Menu entity objects. - * @property {boolean} isResolvingMenus - indicates whether the request to fetch menus is currently resolving. - * @property {boolean} hasResolvedMenus - indicates whether the request to fetch menus has finished resolving. - * @property {Array|undefined} menusItems - a collection of Menu Item entity objects for the current menuId. - * @property {boolean} hasResolvedMenuItems - indicates whether the request to fetch menuItems has finished resolving. - * @property {boolean} hasPages - indicates whether there is currently any data for pages. - * @property {boolean} hasMenus - indicates whether there is currently any data for menus. - */ - -/** - * Manages fetching and resolution state for all entities required - * for the Navigation block. - * - * @param {number} menuId the menu for which to retrieve menuItem data. - * @return { NavigationEntitiesData } the entity data. - */ -export default function useNavigationEntities( menuId ) { - const { - records: menus, - isResolving: isResolvingMenus, - hasResolved: hasResolvedMenus, - } = useEntityRecords( 'root', 'menu', [ { per_page: -1 } ] ); - - const { - records: pages, - isResolving: isResolvingPages, - hasResolved: hasResolvedPages, - } = useEntityRecords( 'postType', 'page', { - parent: 0, - order: 'asc', - orderby: 'id', - per_page: -1, - } ); - - return { - pages, - isResolvingPages, - hasResolvedPages, - hasPages: !! ( hasResolvedPages && pages?.length ), - - menus, - isResolvingMenus, - hasResolvedMenus, - hasMenus: !! ( hasResolvedMenus && menus?.length ), - ...useMenuItemEntities( menuId ), - }; -} - -function useMenuItemEntities( menuId ) { - const { menuItems, hasResolvedMenuItems = false } = useSelect( - ( select ) => { - if ( ! menuId ) { - return {}; - } - - const { getMenuItems, hasFinishedResolution } = select( coreStore ); - const query = { - menus: menuId, - per_page: -1, - }; - return { - menuItems: getMenuItems( query ), - hasResolvedMenuItems: hasFinishedResolution( 'getMenuItems', [ - query, - ] ), - }; - }, - [ menuId ] - ); - - return { - menuItems, - hasResolvedMenuItems, - }; -} diff --git a/packages/edit-navigation/src/components/editor/index.js b/packages/edit-navigation/src/components/editor/index.js deleted file mode 100644 index 2596e368532b3..0000000000000 --- a/packages/edit-navigation/src/components/editor/index.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * WordPress dependencies - */ -import { BlockList, ObserveTyping, WritingFlow } from '@wordpress/block-editor'; -import { Spinner } from '@wordpress/components'; - -export default function Editor( { isPending } ) { - return ( -
- { isPending ? ( - - ) : ( -
- - - - - -
- ) } -
- ); -} diff --git a/packages/edit-navigation/src/components/editor/style.scss b/packages/edit-navigation/src/components/editor/style.scss deleted file mode 100644 index 7c1c631a43661..0000000000000 --- a/packages/edit-navigation/src/components/editor/style.scss +++ /dev/null @@ -1,185 +0,0 @@ -.edit-navigation-editor { - background: $white; - border: $border-width solid $gray-900; - border-radius: $radius-block-ui; - max-width: $navigation-editor-width; - margin: $grid-unit-40 auto 0 auto; - - @include break-medium() { - // Provide space for the floating block toolbar. - margin-top: $grid-unit-50 * 2; - } - - .editor-styles-wrapper { - padding: 0; - } - - .components-spinner { - display: block; - margin: $grid-unit-15 auto; - } - - // Adapt the layout of the Navigation and Link blocks. - // to work better in the context of the Navigation Screen. - .wp-block-navigation { - margin: 0; - font-size: 15px; - padding: $grid-unit-15; - - // This is the default font that is going to be used in the content of the areas (blocks). - font-family: $default-font; - - // Customize/zero out the gap. - .wp-block-navigation__container { - // This unsets flex. - display: block; - } - - // Increase specificity. - .wp-block-navigation-item { - display: block; - margin: $grid-unit-10 0; - - // Show submenus on click. - > .wp-block-navigation__submenu-container { - // This unsets some styles inherited from the block, meant to only show submenus on click, not hover, when inside the editor. - opacity: 1; - visibility: visible; - display: none; - right: auto; - box-sizing: border-box; - width: auto; - height: auto; - overflow: initial; - min-width: initial; - } - - // Fix focus outlines. - &.is-selected > .wp-block-navigation-item__content, - &.is-selected:hover > .wp-block-navigation-item__content { - box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); - } - - &.block-editor-block-list__block:not([contenteditable]):focus::after { - display: none; - } - - .wp-block-navigation-item__content.wp-block-navigation-item__content.wp-block-navigation-item__content { - padding: 0.5em 1em; - margin-right: 0; - border-radius: $radius-block-ui; - - &:hover { - box-shadow: 0 0 0 $border-width $gray-300; - } - } - - .wp-block-navigation-item__label, - .wp-block-navigation-link__placeholder-text { - padding: $grid-unit-05; - padding-left: $grid-unit-10; - line-height: 1; - } - - .wp-block-navigation-item__label { - // Without this Links with submenus display a pointer. - cursor: text; - } - } - - // Basic Page List support. - ul.wp-block-page-list { - // Make it inert. - background: $gray-100; - border-radius: $radius-block-ui; - pointer-events: none; - margin-right: 0; - - .wp-block-navigation-item { - color: $gray-700; - margin-bottom: 6px; - border-radius: $radius-block-ui; - padding: $grid-unit-05; - padding-left: $grid-unit-10; - } - } - - // Submenu icon indicator. - .wp-block-navigation__submenu-icon { - position: absolute; - top: 15px; - left: 0; - padding: 0; - pointer-events: none; - - svg { - // Point rightwards. - transform: rotate(-90deg); - transition: transform 0.2s ease; - @include reduce-motion("transition"); - } - } - - // Point downwards when open. - .wp-block-navigation-submenu.is-selected > .wp-block-navigation-item__content > .wp-block-navigation__submenu-icon svg, - .wp-block-navigation-submenu.has-child-selected > .wp-block-navigation-item__content > .wp-block-navigation__submenu-icon svg { - transform: rotate(0deg); - } - - // Override inherited values to optimize menu items for the screen context. - .wp-block-navigation-submenu.has-child, - .wp-block-navigation-item.has-child { - cursor: default; - border-radius: $radius-block-ui; - } - - // Override for deeply nested submenus. - .has-child .wp-block-navigation__container .wp-block-navigation__submenu-container { - left: auto; - } - - // When editing a submenu with children, highlight the parent - // and adjust the spacing and submenu icon. - .wp-block-navigation-submenu.is-editing { - > .wp-block-navigation__submenu-container { - opacity: 1; - visibility: visible; - position: relative; - background: transparent; - top: auto; - left: auto; - padding-left: $grid-unit-20 + $grid-unit-05; - min-width: auto; - width: 100%; - border: none; - display: block; - - &::before { - display: none; - } - } - } - - // Appender styles - .block-list-appender { - // Make appender rows the same height as items and center the button vertically. - display: flex; - flex-direction: column; - justify-content: center; - height: $grid-unit-50; - margin: $grid-unit-10 auto $grid-unit-10 0; - } - - .block-editor-button-block-appender.block-list-appender__toggle { - margin-left: $grid-unit-30; - } - } - - // Override behavior that hides the navigation block's appender when it's deselected. - .block-editor-block-list__block:not(.is-selected):not(.has-child-selected):not(.block-editor-block-list__layout) { - .block-editor-block-list__layout > .block-list-appender .block-list-appender__toggle { - opacity: unset; - transform: unset; - } - } -} diff --git a/packages/edit-navigation/src/components/error-boundary/index.js b/packages/edit-navigation/src/components/error-boundary/index.js deleted file mode 100644 index bea03d909c380..0000000000000 --- a/packages/edit-navigation/src/components/error-boundary/index.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * WordPress dependencies - */ -import { Component } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { Button } from '@wordpress/components'; -import { Warning } from '@wordpress/block-editor'; - -class ErrorBoundary extends Component { - constructor() { - super( ...arguments ); - - this.reboot = this.reboot.bind( this ); - - this.state = { - error: null, - }; - } - - componentDidCatch( error ) { - this.setState( { error } ); - } - - reboot() { - if ( this.props.onError ) { - this.props.onError(); - } - } - - render() { - const { error } = this.state; - if ( ! error ) { - return this.props.children; - } - - return ( - - { __( 'Attempt Recovery' ) } - , - ] } - > - { __( - 'The navigation editor has encountered an unexpected error.' - ) } - - ); - } -} - -export default ErrorBoundary; diff --git a/packages/edit-navigation/src/components/error-boundary/style.scss b/packages/edit-navigation/src/components/error-boundary/style.scss deleted file mode 100644 index 4f8b095dc6e89..0000000000000 --- a/packages/edit-navigation/src/components/error-boundary/style.scss +++ /dev/null @@ -1,7 +0,0 @@ -.navigation-editor-error-boundary { - margin: auto; - max-width: 780px; - padding: 20px; - margin-top: 60px; - box-shadow: $shadow-modal; -} diff --git a/packages/edit-navigation/src/components/header/index.js b/packages/edit-navigation/src/components/header/index.js deleted file mode 100644 index 6dbaaa8f90c12..0000000000000 --- a/packages/edit-navigation/src/components/header/index.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { NavigableToolbar } from '@wordpress/block-editor'; -import { useViewportMatch } from '@wordpress/compose'; -import { PinnedItems } from '@wordpress/interface'; - -/** - * Internal dependencies - */ -import MenuActions from './menu-actions'; -import NewButton from './new-button'; -import SaveButton from './save-button'; -import UndoButton from './undo-button'; -import RedoButton from './redo-button'; -import InserterToggle from './inserter-toggle'; -import MoreMenu from './more-menu'; - -export default function Header( { - isMenuSelected, - menus, - isPending, - navigationPost, -} ) { - const isMediumViewport = useViewportMatch( 'medium' ); - - if ( ! isMenuSelected ) { - return ( -
-
-

- { __( 'Navigation' ) } -

-
-
- ); - } - - return ( -
-
- { isMediumViewport && ( -

- { __( 'Navigation' ) } -

- ) } - - - - { isMediumViewport && ( - <> - - - - ) } - -
- - - -
- { isMediumViewport && } - - - -
-
- ); -} diff --git a/packages/edit-navigation/src/components/header/inserter-toggle.js b/packages/edit-navigation/src/components/header/inserter-toggle.js deleted file mode 100644 index 5c0bdaffa7905..0000000000000 --- a/packages/edit-navigation/src/components/header/inserter-toggle.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * WordPress dependencies - */ -import { store as blockEditorStore } from '@wordpress/block-editor'; -import { Button, ToolbarItem } from '@wordpress/components'; -import { _x } from '@wordpress/i18n'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { plus } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import { useNavigationEditorRootBlock } from '../../hooks'; -import { store as editNavigationStore } from '../../store'; - -function InserterToggle() { - const { navBlockClientId } = useNavigationEditorRootBlock(); - - const { isInserterOpened, hasInserterItems } = useSelect( - ( select ) => { - return { - hasInserterItems: - select( blockEditorStore ).hasInserterItems( - navBlockClientId - ), - isInserterOpened: - select( editNavigationStore ).isInserterOpened(), - }; - }, - [ navBlockClientId ] - ); - - const { setIsInserterOpened } = useDispatch( editNavigationStore ); - - return ( - { - event.preventDefault(); - } } - onClick={ () => setIsInserterOpened( ! isInserterOpened ) } - icon={ plus } - /* translators: button label text should, if possible, be under 16 - characters. */ - label={ _x( - 'Toggle block inserter', - 'Generic label for block inserter button' - ) } - disabled={ ! hasInserterItems } - /> - ); -} - -export default InserterToggle; diff --git a/packages/edit-navigation/src/components/header/menu-actions.js b/packages/edit-navigation/src/components/header/menu-actions.js deleted file mode 100644 index be9871a775a60..0000000000000 --- a/packages/edit-navigation/src/components/header/menu-actions.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { - DropdownMenu, - __experimentalText as Text, -} from '@wordpress/components'; -import { chevronDown } from '@wordpress/icons'; -import { useMemo, useState } from '@wordpress/element'; -import { decodeEntities } from '@wordpress/html-entities'; - -/** - * Internal dependencies - */ -import MenuSwitcher from '../menu-switcher'; -import { useMenuEntityProp, useSelectedMenuId } from '../../hooks'; - -export default function MenuActions( { menus, isLoading } ) { - const [ selectedMenuId, setSelectedMenuId ] = useSelectedMenuId(); - const [ menuName ] = useMenuEntityProp( 'name', selectedMenuId ); - - // Use internal state instead of a ref to make sure that the component - // re-renders when the popover's anchor updates. - const [ popoverAnchor, setPopoverAnchor ] = useState( null ); - - // Memoize popoverProps to avoid returning a new object every time. - const popoverProps = useMemo( - () => ( { - className: 'edit-navigation-menu-actions__switcher-dropdown', - position: 'bottom center', - // Use the title ref as the popover's anchor so that the dropdown is - // centered over the whole title area rather than just on part of it. - anchor: popoverAnchor, - } ), - [ popoverAnchor ] - ); - - if ( isLoading ) { - return ( -
- { __( 'Loading…' ) } -
- ); - } - - return ( -
-
- - { decodeEntities( menuName ) } - - - - { ( { onClose } ) => ( - { - setSelectedMenuId( menuId ); - onClose(); - } } - /> - ) } - -
-
- ); -} diff --git a/packages/edit-navigation/src/components/header/more-menu.js b/packages/edit-navigation/src/components/header/more-menu.js deleted file mode 100644 index a75e60c1766e6..0000000000000 --- a/packages/edit-navigation/src/components/header/more-menu.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * WordPress dependencies - */ -import { MenuGroup, MenuItem, VisuallyHidden } from '@wordpress/components'; -import { external } from '@wordpress/icons'; -import { MoreMenuDropdown } from '@wordpress/interface'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import ToolsMoreMenuGroup from './tools-more-menu-group'; - -export default function MoreMenu() { - return ( - - { ( onClose ) => ( - - - { __( 'Help' ) } - - { - /* translators: accessibility text */ - __( '(opens in a new tab)' ) - } - - - - - ) } - - ); -} diff --git a/packages/edit-navigation/src/components/header/new-button.js b/packages/edit-navigation/src/components/header/new-button.js deleted file mode 100644 index 6993ddca1d2f7..0000000000000 --- a/packages/edit-navigation/src/components/header/new-button.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { Button, Modal } from '@wordpress/components'; -import { useState } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import AddMenu from '../add-menu'; -import { useSelectedMenuId } from '../../hooks'; - -export default function NewButton() { - const [ isModalOpen, setIsModalOpen ] = useState( false ); - const [ , setSelectedMenuId ] = useSelectedMenuId(); - - return ( - <> - - { isModalOpen && ( - setIsModalOpen( false ) } - > - { - setIsModalOpen( false ); - setSelectedMenuId( menuId ); - } } - /> - - ) } - - ); -} diff --git a/packages/edit-navigation/src/components/header/redo-button.js b/packages/edit-navigation/src/components/header/redo-button.js deleted file mode 100644 index c4f4e6df37b91..0000000000000 --- a/packages/edit-navigation/src/components/header/redo-button.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * WordPress dependencies - */ -import { __, isRTL } from '@wordpress/i18n'; -import { ToolbarButton } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { redo as redoIcon, undo as undoIcon } from '@wordpress/icons'; -import { displayShortcut, isAppleOS } from '@wordpress/keycodes'; -import { store as coreStore } from '@wordpress/core-data'; - -export default function RedoButton() { - const shortcut = isAppleOS() - ? displayShortcut.primaryShift( 'z' ) - : displayShortcut.primary( 'y' ); - - const hasRedo = useSelect( - ( select ) => select( coreStore ).hasRedo(), - [] - ); - const { redo } = useDispatch( coreStore ); - return ( - - ); -} diff --git a/packages/edit-navigation/src/components/header/save-button.js b/packages/edit-navigation/src/components/header/save-button.js deleted file mode 100644 index ed255851a22ef..0000000000000 --- a/packages/edit-navigation/src/components/header/save-button.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * WordPress dependencies - */ -import { useDispatch, useSelect } from '@wordpress/data'; -import { Button } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { store as coreStore } from '@wordpress/core-data'; - -/** - * Internal dependencies - */ -import { store as editNavigationStore } from '../../store'; - -export default function SaveButton( { navigationPost } ) { - const { isDirty } = useSelect( ( select ) => { - const { __experimentalGetDirtyEntityRecords } = select( coreStore ); - const dirtyEntityRecords = __experimentalGetDirtyEntityRecords(); - - return { - isDirty: dirtyEntityRecords.length > 0, - }; - }, [] ); - - const { saveNavigationPost } = useDispatch( editNavigationStore ); - - const disabled = ! isDirty || ! navigationPost; - - return ( - - ); -} diff --git a/packages/edit-navigation/src/components/header/style.scss b/packages/edit-navigation/src/components/header/style.scss deleted file mode 100644 index a01c3dda0de2e..0000000000000 --- a/packages/edit-navigation/src/components/header/style.scss +++ /dev/null @@ -1,164 +0,0 @@ -.edit-navigation-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: $grid-unit-15 $grid-unit-30 $grid-unit-15 20px; -} - -.edit-navigation-header__toolbar-wrapper { - display: flex; - align-items: center; - justify-content: center; -} - -.edit-navigation-header__title { - font-size: 20px; - padding: 0; - margin: 0 20px 0 0; -} - -.edit-navigation-header__toolbar { - align-items: center; - border: none; - - // The Toolbar component adds different styles to buttons, so we reset them - // here to the original button styles - // Specificity bump needed to offset https://github.com/WordPress/gutenberg/blob/8ea29cb04412c80c9adf7c1db0e816d6a0ac1232/packages/components/src/toolbar/style.scss#L76 - > .components-button.has-icon.has-icon.has-icon, - > .components-dropdown > .components-button.has-icon.has-icon { - height: $button-size; - min-width: $button-size; - padding: 6px; - - &.is-pressed { - background: $gray-900; - } - - &:focus:not(:disabled) { - box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 $border-width $white; - outline: 1px solid transparent; - } - - &::before { - display: none; - } - } - - > .edit-navigation-header-inserter-toggle.has-icon.has-icon.has-icon { - margin-right: $grid-unit-10; - // Special dimensions for this button. - min-width: 32px; - width: 32px; - height: 32px; - padding: 0; - } -} - -.edit-navigation-menu-actions { - display: flex; - flex-direction: column; - justify-content: center; - - .edit-navigation-menu-actions__subtitle-wrapper { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - } - - .components-dropdown { - margin-left: $grid-unit-05; - } - - .edit-navigation-menu-actions__switcher-toggle { - padding: 0; - min-width: 0; - } -} - -.edit-navigation-menu-actions__switcher-dropdown { - // Appear below the modal overlay. - z-index: z-index(".components-popover.edit-navigation-menu-actions__switcher-dropdown"); - - // Resetting MenuItemGroup padding so button can take full space. - .components-menu-group.has-hidden-separator { - padding: 0; - } - - .edit-navigation-menu-switcher__new-button.components-button { - justify-content: center; - background: $gray-900; - color: $white; - height: ($button-size + $grid-unit-10); - border-radius: 0; - - &:hover { - color: $white; - } - - &:active { - color: $gray-400; - } - - &:focus:not(:disabled) { - box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 3px $white; - } - - // This is needed to center the button text. - .components-menu-item__item { - min-width: 0; - margin: 0; - } - } -} - -.edit-navigation-header__actions { - display: flex; - - > .components-dropdown, - > .components-button, - > .interface-pinned-items .components-button { - &:not(:last-child) { - margin-right: $grid-unit-15; - } - } -} - -// Hide notices. -.gutenberg_page_gutenberg-navigation { - .notice, - #wpfooter { - display: none; - } -} - - -// INSERTER TOGGLE -.edit-navigation-header-inserter-toggle { - - svg { - transition: transform cubic-bezier(0.165, 0.84, 0.44, 1) 0.2s; - @include reduce-motion("transition"); - } - - // Make the button appear like a "X" close button. - &.is-pressed { - svg { - transform: rotate(45deg); - } - } -} - -// INSERTER PANEL -.edit-navigation-layout__inserter-panel-header { - padding-top: $grid-unit-10; - padding-right: $grid-unit-10; - display: flex; - justify-content: flex-end; - - // Hide close button within panel on larger screens as this - // action is provided by the inserter toggle or ESC key. - @include break-medium() { - display: none; - } -} diff --git a/packages/edit-navigation/src/components/header/tools-more-menu-group.js b/packages/edit-navigation/src/components/header/tools-more-menu-group.js deleted file mode 100644 index bfcbb39d18459..0000000000000 --- a/packages/edit-navigation/src/components/header/tools-more-menu-group.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * WordPress dependencies - */ -import { createSlotFill } from '@wordpress/components'; - -const { Fill: ToolsMoreMenuGroup, Slot } = createSlotFill( - 'EditNavigationToolsMoreMenuGroup' -); - -ToolsMoreMenuGroup.Slot = ( { fillProps } ) => ( - { ( fills ) => fills } -); - -export default ToolsMoreMenuGroup; diff --git a/packages/edit-navigation/src/components/header/undo-button.js b/packages/edit-navigation/src/components/header/undo-button.js deleted file mode 100644 index 827ed1a415d74..0000000000000 --- a/packages/edit-navigation/src/components/header/undo-button.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * WordPress dependencies - */ -import { __, isRTL } from '@wordpress/i18n'; -import { ToolbarButton } from '@wordpress/components'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { undo as undoIcon, redo as redoIcon } from '@wordpress/icons'; -import { displayShortcut } from '@wordpress/keycodes'; -import { store as coreStore } from '@wordpress/core-data'; - -export default function UndoButton() { - const hasUndo = useSelect( - ( select ) => select( coreStore ).hasUndo(), - [] - ); - const { undo } = useDispatch( coreStore ); - return ( - - ); -} diff --git a/packages/edit-navigation/src/components/inserter-sidebar/index.js b/packages/edit-navigation/src/components/inserter-sidebar/index.js deleted file mode 100644 index 7c0bbe9e87409..0000000000000 --- a/packages/edit-navigation/src/components/inserter-sidebar/index.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * WordPress dependencies - */ -import { Button } from '@wordpress/components'; -import { close } from '@wordpress/icons'; -import { - __experimentalLibrary as Library, - store as blockEditorStore, -} from '@wordpress/block-editor'; -import { - useViewportMatch, - __experimentalUseDialog as useDialog, -} from '@wordpress/compose'; -import { useDispatch, useSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { store as editNavigationStore } from '../../store'; -import { useNavigationEditorRootBlock } from '../../hooks'; - -const SHOW_PREVIEWS = false; - -function InserterSidebar() { - const isMobileViewport = useViewportMatch( 'medium', '<' ); - - const { navBlockClientId, lastNavBlockItemIndex } = - useNavigationEditorRootBlock(); - - const { hasInserterItems, selectedBlockClientId } = useSelect( - ( select ) => { - return { - hasInserterItems: - select( blockEditorStore ).hasInserterItems( - navBlockClientId - ), - selectedBlockClientId: - select( blockEditorStore ).getSelectedBlock()?.clientId, - }; - }, - [ navBlockClientId ] - ); - - const { setIsInserterOpened } = useDispatch( editNavigationStore ); - - const [ inserterDialogRef, inserterDialogProps ] = useDialog( { - onClose: () => setIsInserterOpened( false ), - } ); - - // Only concerned with whether there are items to display. If not then - // we shouldn't render. - if ( ! hasInserterItems ) { - return null; - } - - const shouldInsertInNavBlock = - ! selectedBlockClientId || navBlockClientId === selectedBlockClientId; - - return ( -
-
-
-
- -
-
- ); -} - -export default InserterSidebar; diff --git a/packages/edit-navigation/src/components/layout/index.js b/packages/edit-navigation/src/components/layout/index.js deleted file mode 100644 index 18884ce25fe21..0000000000000 --- a/packages/edit-navigation/src/components/layout/index.js +++ /dev/null @@ -1,208 +0,0 @@ -/** - * WordPress dependencies - */ -import { - BlockEditorKeyboardShortcuts, - BlockEditorProvider, - BlockTools, - __unstableUseBlockSelectionClearer as useBlockSelectionClearer, -} from '@wordpress/block-editor'; -import { useEntityBlockEditor } from '@wordpress/core-data'; -import { Popover, SlotFillProvider, Spinner } from '@wordpress/components'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { useEffect, useMemo, useState } from '@wordpress/element'; -import { - InterfaceSkeleton, - ComplementaryArea, - store as interfaceStore, -} from '@wordpress/interface'; -import { __ } from '@wordpress/i18n'; -import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; -import { PluginArea } from '@wordpress/plugins'; - -/** - * Internal dependencies - */ -import UnselectedMenuState from './unselected-menu-state'; -import { - IsMenuNameControlFocusedContext, - useNavigationEditor, - useMenuNotifications, -} from '../../hooks'; -import ErrorBoundary from '../error-boundary'; -import NavigationEditorShortcuts from './shortcuts'; -import Sidebar from '../sidebar'; -import Header from '../header'; -import Notices from '../notices'; -import Editor from '../editor'; -import InserterSidebar from '../inserter-sidebar'; -import UnsavedChangesWarning from './unsaved-changes-warning'; -import { store as editNavigationStore } from '../../store'; -import { - NAVIGATION_POST_KIND, - NAVIGATION_POST_POST_TYPE, -} from '../../constants'; - -const interfaceLabels = { - /* translators: accessibility text for the navigation screen top bar landmark region. */ - header: __( 'Navigation top bar' ), - /* translators: accessibility text for the navigation screen content landmark region. */ - body: __( 'Navigation menu blocks' ), - /* translators: accessibility text for the navigation screen settings landmark region. */ - sidebar: __( 'Navigation settings' ), - secondarySidebar: __( 'Block library' ), -}; - -export default function Layout( { blockEditorSettings } ) { - const contentAreaRef = useBlockSelectionClearer(); - const [ isMenuNameControlFocused, setIsMenuNameControlFocused ] = - useState( false ); - const { saveNavigationPost } = useDispatch( editNavigationStore ); - const savePost = () => saveNavigationPost( navigationPost ); - - const { - menus, - hasLoadedMenus, - hasFinishedInitialLoad, - selectedMenuId, - navigationPost, - isMenuBeingDeleted, - selectMenu, - deleteMenu, - isMenuSelected, - } = useNavigationEditor(); - - const [ blocks, onInput, onChange ] = useEntityBlockEditor( - NAVIGATION_POST_KIND, - NAVIGATION_POST_POST_TYPE, - { - id: navigationPost?.id, - } - ); - - const { hasSidebarEnabled, isInserterOpened } = useSelect( - ( select ) => ( { - hasSidebarEnabled: !! select( - interfaceStore - ).getActiveComplementaryArea( 'core/edit-navigation' ), - isInserterOpened: select( editNavigationStore ).isInserterOpened(), - } ), - [] - ); - - useEffect( () => { - if ( ! selectedMenuId && menus?.length ) { - selectMenu( menus[ 0 ].id ); - } - }, [ selectedMenuId, menus ] ); - - useMenuNotifications( selectedMenuId ); - - const hasMenus = !! menus?.length; - - const isBlockEditorReady = !! ( - hasMenus && - navigationPost && - isMenuSelected - ); - - return ( - - -