diff --git a/packages/format-library/src/link/index.js b/packages/format-library/src/link/index.js index ab287daff19a73..61b6f974cdce3a 100644 --- a/packages/format-library/src/link/index.js +++ b/packages/format-library/src/link/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { useState } from '@wordpress/element'; +import { useState, useLayoutEffect } from '@wordpress/element'; import { getTextContent, applyFormat, @@ -18,7 +18,7 @@ import { RichTextShortcut, } from '@wordpress/block-editor'; import { decodeEntities } from '@wordpress/html-entities'; -import { link as linkIcon, linkOff } from '@wordpress/icons'; +import { link as linkIcon } from '@wordpress/icons'; import { speak } from '@wordpress/a11y'; /** @@ -39,18 +39,47 @@ function Edit( { contentRef, } ) { const [ addingLink, setAddingLink ] = useState( false ); + // We only need to store the button element that opened the popover. We can ignore the other states, as they will be handled by the onFocus prop to return to the rich text field. + const [ openedBy, setOpenedBy ] = useState( null ); - function addLink() { + useLayoutEffect( () => { + const editableContentElement = contentRef.current; + if ( ! editableContentElement ) { + return; + } + + function handleClick( event ) { + // There is a situation whereby there is an existing link in the rich text + // and the user clicks on the leftmost edge of that link and fails to activate + // the link format, but the click event still fires on the `` element. + // This causes the `addingLink` state to be set to `true` and the link UI + // to be rendered in "creating" mode. We need to check isActive to see if + // we have an active link format. + if ( event.target.tagName !== 'A' || ! isActive ) { + return; + } + + setAddingLink( true ); + } + + editableContentElement.addEventListener( 'click', handleClick ); + + return () => { + editableContentElement.removeEventListener( 'click', handleClick ); + }; + }, [ contentRef, isActive ] ); + + function addLink( target ) { const text = getTextContent( slice( value ) ); - if ( text && isURL( text ) && isValidHref( text ) ) { + if ( ! isActive && text && isURL( text ) && isValidHref( text ) ) { onChange( applyFormat( value, { type: name, attributes: { url: text }, } ) ); - } else if ( text && isEmail( text ) ) { + } else if ( ! isActive && text && isEmail( text ) ) { onChange( applyFormat( value, { type: name, @@ -58,15 +87,48 @@ function Edit( { } ) ); } else { + if ( target ) { + setOpenedBy( target ); + } setAddingLink( true ); } } - function stopAddingLink( returnFocus = true ) { + /** + * Runs when the popover is closed via escape keypress, unlinking the selected text, + * but _not_ on a click outside the popover. onFocusOutside handles that. + */ + function stopAddingLink() { + // Don't let the click handler on the toolbar button trigger again. + + // There are two places for us to return focus to on Escape keypress: + // 1. The rich text field. + // 2. The toolbar button. + + // The toolbar button is the only one we need to handle returning focus to. + // Otherwise, we rely on the passed in onFocus to return focus to the rich text field. + + // Close the popover setAddingLink( false ); - if ( returnFocus ) { + // Return focus to the toolbar button or the rich text field + if ( openedBy?.tagName === 'BUTTON' ) { + openedBy.focus(); + } else { onFocus(); } + // Remove the openedBy state + setOpenedBy( null ); + } + + // Test for this: + // 1. Click on the link button + // 2. Click the Options button in the top right of header + // 3. Focus should be in the dropdown of the Options button + // 4. Press Escape + // 5. Focus should be on the Options button + function onFocusOutside() { + setAddingLink( false ); + setOpenedBy( null ); } function onRemoveFormat() { @@ -82,36 +144,23 @@ function Edit( { character="k" onUse={ onRemoveFormat } /> - { isActive && ( - - ) } - { ! isActive && ( - - ) } - { ( addingLink || isActive ) && ( + { + addLink( event.currentTarget ); + } } + isActive={ isActive || addingLink } + shortcutType="primary" + shortcutCharacter="k" + aria-haspopup="true" + aria-expanded={ addingLink } + /> + { addingLink && ( cachedRect; - // Focus should only be moved into the Popover when the Link is being created or edited. - // When the Link is in "preview" mode focus should remain on the rich text because at - // this point the Link dialog is informational only and thus the user should be able to - // continue editing the rich text. - // Ref used because the focusOnMount prop shouldn't evolve during render of a Popover - // otherwise it causes a render of the content. - const focusOnMount = useRef( addingLink ? 'firstElement' : false ); - async function handleCreate( pageTitle ) { const page = await createPageEntity( { title: pageTitle, @@ -252,9 +243,8 @@ function InlineLinkUI( { return ( stopAddingLink( false ) } + onFocusOutside={ onFocusOutside } placement="bottom" offset={ 10 } shift @@ -263,7 +253,6 @@ function InlineLinkUI( { value={ linkValue } onChange={ onChangeLink } onRemove={ removeLink } - forceIsEditingLink={ addingLink } hasRichPreviews createSuggestion={ createPageEntity && handleCreate } withCreateSuggestion={ userCanCreatePages } diff --git a/test/e2e/specs/editor/blocks/links.spec.js b/test/e2e/specs/editor/blocks/links.spec.js index d1de2df1da7ff7..2e6681c2d0701f 100644 --- a/test/e2e/specs/editor/blocks/links.spec.js +++ b/test/e2e/specs/editor/blocks/links.spec.js @@ -225,10 +225,10 @@ test.describe( 'Links', () => { pageUtils, LinkUtils, } ) => { - await LinkUtils.createAndReselectLink(); + await LinkUtils.createLink(); // Click on the Edit button. - await page.getByRole( 'button', { name: 'Edit' } ).click(); + await page.getByRole( 'button', { name: 'Edit', exact: true } ).click(); // Change the URL. // getByPlaceholder required in order to handle Link Control component @@ -251,7 +251,7 @@ test.describe( 'Links', () => { } ); test( `can remove existing links`, async ( { editor, LinkUtils } ) => { - await LinkUtils.createAndReselectLink(); + await LinkUtils.createLink(); const linkPopover = LinkUtils.getLinkPopover(); @@ -332,10 +332,12 @@ test.describe( 'Links', () => { LinkUtils, pageUtils, } ) => { - await LinkUtils.createAndReselectLink(); + await LinkUtils.createLink(); + await pageUtils.pressKeys( 'Escape' ); // Make a collapsed selection inside the link. await pageUtils.pressKeys( 'ArrowLeft' ); await pageUtils.pressKeys( 'ArrowRight' ); + await pageUtils.pressKeys( 'primary+k' ); const linkPopover = LinkUtils.getLinkPopover(); await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); @@ -384,7 +386,7 @@ test.describe( 'Links', () => { await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); // Insert a link. - await editor.clickBlockToolbarButton( 'Link' ); + await pageUtils.pressKeys( 'primary+k' ); const urlInput = page.getByRole( 'combobox', { name: 'Link', @@ -424,6 +426,31 @@ test.describe( 'Links', () => { }, }, ] ); + + // Test pressing escape from the toolbar button should return focus to the toolbar button + // Insert a link. + await editor.clickBlockToolbarButton( 'Link' ); + + // Expect the "Link" combobox to be visible and focused + await expect( urlInput ).toBeVisible(); + await expect( urlInput ).toBeFocused(); + + await page.keyboard.press( 'Escape' ); + await expect( LinkUtils.getLinkPopover() ).toBeHidden(); + + // Focus should return to the Link Toolbar Button that opened the popover + await expect( + page.getByLabel( 'Link', { exact: true } ) + ).toBeFocused(); + + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { + content: 'This is Gutenberg and more!', + }, + }, + ] ); } ); test( `can be created and modified using only the keyboard`, async ( { @@ -441,10 +468,10 @@ test.describe( 'Links', () => { await page.keyboard.type( 'This is Gutenberg' ); await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); await pageUtils.pressKeys( 'primary+K' ); + const linkPopover = LinkUtils.getLinkPopover(); await page.keyboard.type( URL ); await pageUtils.pressKeys( 'Enter' ); - const linkPopover = LinkUtils.getLinkPopover(); await expect( linkPopover ).toBeVisible(); // Close the link control to return the caret to the canvas await pageUtils.pressKeys( 'Escape' ); @@ -455,13 +482,15 @@ test.describe( 'Links', () => { await expect( linkPopover ).toBeHidden(); // Move the caret back into the link text and the link popover - // should be displayed. + // should not be displayed. await pageUtils.pressKeys( 'ArrowLeft' ); - await expect( linkPopover ).toBeVisible(); + await expect( linkPopover ).toBeHidden(); // Switch the Link UI into "Edit" mode via keyboard shortcut // and check that the input has the correct value. await pageUtils.pressKeys( 'primary+K' ); + await pageUtils.pressKeys( 'Tab' ); + await pageUtils.pressKeys( 'Enter' ); await expect( linkPopover.getByRole( 'combobox', { @@ -477,16 +506,12 @@ test.describe( 'Links', () => { // Move back into the RichText. await pageUtils.pressKeys( 'Escape' ); - - // ...but the Link Popover should still be active because we are within the link. - await expect( linkPopover ).toBeVisible(); + // Link Popover should be hidden even though it's within the linked text. + await expect( linkPopover ).toBeHidden(); // Move outside of the link entirely. await pageUtils.pressKeys( 'ArrowRight' ); - // Link Popover should now disappear because we are no longer within the link. - await expect( linkPopover ).toBeHidden(); - // Append some text to the paragraph to assert that focus has been returned // to the correct location within the RichText. await page.keyboard.type( ' and more!' ); @@ -543,6 +568,11 @@ test.describe( 'Links', () => { // Press Cmd+K to insert a link. await pageUtils.pressKeys( 'primary+K' ); + const linkPopover = LinkUtils.getLinkPopover(); + + // Expect link popover to be visible + await expect( linkPopover ).toBeVisible(); + // Type a URL. await page.keyboard.type( 'https://wordpress.org/gutenberg' ); @@ -555,34 +585,31 @@ test.describe( 'Links', () => { }, ] ); + // Submit the link. await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'ArrowLeft' ); - await page.keyboard.press( 'ArrowLeft' ); + // Expect the Link UI to still be visible + await expect( linkPopover ).toBeVisible(); - // Edit link. - await page.getByRole( 'button', { name: 'Edit' } ).click(); + // Tab to "Edit" button and enter edit mode again. + await pageUtils.pressKeys( 'Tab' ); + await pageUtils.pressKeys( 'Enter' ); // Open settings. - await page - .getByRole( 'region', { - name: 'Editor content', - } ) + await linkPopover .getByRole( 'button', { name: 'Advanced', } ) .click(); // Navigate to and toggle the "Open in new tab" checkbox. - const checkbox = page.getByLabel( 'Open in new tab' ); + const checkbox = linkPopover.getByLabel( 'Open in new tab' ); await checkbox.click(); // Toggle should still have focus and be checked. await expect( checkbox ).toBeChecked(); await expect( checkbox ).toBeFocused(); - const linkPopover = LinkUtils.getLinkPopover(); - // Tab back to the Submit and apply the link. await linkPopover.getByRole( 'button', { name: 'Save' } ).click(); @@ -609,18 +636,22 @@ test.describe( 'Links', () => { name: 'core/paragraph', } ); await page.keyboard.type( 'This is WordPress' ); + // Select "WordPress". await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); await pageUtils.pressKeys( 'primary+k' ); - await page.keyboard.type( 'w.org' ); - await page.keyboard.press( 'Enter' ); // Close the link control to return the caret to the canvas const linkPopover = LinkUtils.getLinkPopover(); + + await page.keyboard.type( 'w.org' ); + + // Submit the link + await page.keyboard.press( 'Enter' ); + + // Close the Link Popover. await pageUtils.pressKeys( 'Escape' ); - // Deselect the link text by moving the caret to the end of the line - // and the link popover should not be displayed. - await pageUtils.pressKeys( 'End' ); + await expect( linkPopover ).toBeHidden(); await expect.poll( editor.getBlocks ).toMatchObject( [ @@ -632,33 +663,28 @@ test.describe( 'Links', () => { }, ] ); - // Move caret back into the link. - await page.keyboard.press( 'ArrowLeft' ); - await page.keyboard.press( 'ArrowLeft' ); - - // Edit link. + // Edit the link again. await pageUtils.pressKeys( 'primary+k' ); - // getByPlaceholder required in order to handle Link Control component + // Expect Link UI to be visible again + await expect( linkPopover ).toBeVisible(); + + // Click on the `Edit` button. + await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); + + // Change the URL. + // Note: getByPlaceholder required in order to handle Link Control component // managing focus onto other inputs within the control. - await page.getByPlaceholder( 'Search or type url' ).fill( '' ); + await linkPopover.getByPlaceholder( 'Search or type url' ).fill( '' ); await page.keyboard.type( 'wordpress.org' ); - // Update the link. + // Save the link. await linkPopover.getByRole( 'button', { name: 'Save' } ).click(); - // Navigate back to the link editing state inputs to verify appears as changed. - await page.keyboard.press( 'Tab' ); - await page.keyboard.press( 'Enter' ); - - expect( - await page - .getByRole( 'combobox', { - name: 'Link', - } ) - .inputValue() - ).toContain( 'wordpress.org' ); + // Link UI should be closed. + await expect( linkPopover ).toBeHidden(); + // The link should have been updated. await expect.poll( editor.getBlocks ).toMatchObject( [ { name: 'core/paragraph', @@ -674,6 +700,7 @@ test.describe( 'Links', () => { page, editor, pageUtils, + LinkUtils, } ) => { // Create a block with some text. await editor.insertBlock( { @@ -691,29 +718,25 @@ test.describe( 'Links', () => { await page.keyboard.press( 'Escape' ); // Move to edge of text "Gutenberg". - await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); // If you just use Alt here it won't work on windows. await pageUtils.pressKeys( 'ArrowLeft' ); - + await pageUtils.pressKeys( 'ArrowLeft' ); // Select "Gutenberg". - await pageUtils.pressKeys( 'shiftAlt+ArrowRight' ); + await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); // If you just use Alt here it won't work on windows. // Create a link. await pageUtils.pressKeys( 'primary+k' ); + await page.keyboard.type( 'https://wordpress.org/plugins/gutenberg/' ); await page.keyboard.press( 'Enter' ); - await page.keyboard.press( 'Escape' ); - await pageUtils.pressKeys( 'End' ); - // Move back into the link. - await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); - await pageUtils.pressKeys( 'primary+k' ); + + // Press the "Edit" button + const linkPopover = LinkUtils.getLinkPopover(); + await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); // Toggle the Advanced settings to be open. // This should set the editor preference to persist this // UI state. - await page - .getByRole( 'region', { - name: 'Editor content', - } ) + await linkPopover .getByRole( 'button', { name: 'Advanced', } ) @@ -722,25 +745,25 @@ test.describe( 'Links', () => { // Move focus out of Link UI and into Paragraph block. await pageUtils.pressKeys( 'Escape' ); - // Move caret back into the "WordPress" link to trigger - // the Link UI for that link. - await pageUtils.pressKeys( 'Alt+ArrowRight' ); - await pageUtils.pressKeys( 'ArrowRight' ); - await pageUtils.pressKeys( 'ArrowRight' ); + // Click on the "WordPress" link + await editor.canvas + .getByRole( 'link', { + name: 'WordPress', + } ) + .click(); - // Switch Link UI to "edit" mode. - await page.getByRole( 'button', { name: 'Edit' } ).click(); + // press the "edit" button + await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); // Check that the Advanced settings are still expanded/open // and I can see the open in new tab checkbox. This verifies // that the editor preference was persisted. - await expect( page.getByLabel( 'Open in new tab' ) ).toBeVisible(); + await expect( + linkPopover.getByLabel( 'Open in new tab' ) + ).toBeVisible(); // Toggle the Advanced settings back to being closed. - await page - .getByRole( 'region', { - name: 'Editor content', - } ) + await linkPopover .getByRole( 'button', { name: 'Advanced', } ) @@ -751,18 +774,24 @@ test.describe( 'Links', () => { // Move caret back into the "Gutenberg" link and open // the Link UI for that link. - await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); - await pageUtils.pressKeys( 'primary+k' ); + await editor.canvas + .getByRole( 'link', { + name: 'Gutenberg', + } ) + .click(); + + // Switch to "Edit" mode. + await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); // Check that the Advanced settings are still closed. // This verifies that the editor preference was persisted. - await expect( page.getByLabel( 'Open in new tab' ) ).toBeHidden(); + await expect( + linkPopover.getByLabel( 'Open in new tab' ) + ).toBeHidden(); } ); test( 'can toggle link settings and save', async ( { - page, editor, - pageUtils, LinkUtils, } ) => { await editor.insertBlock( { @@ -773,31 +802,35 @@ test.describe( 'Links', () => { }, } ); - // Move caret into the link. - await pageUtils.pressKeys( 'ArrowRight' ); + // Click on "Gutenberg" link in the canvas + await editor.canvas + .getByRole( 'link', { + name: 'Gutenberg', + } ) + .click(); - // Switch Link UI to "edit" mode. - await page.getByRole( 'button', { name: 'Edit' } ).click(); + // Get the Link Popover using the LinkUtils helper + const linkPopover = LinkUtils.getLinkPopover(); + + // Switch to Edit the link + await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); // Open Advanced Settings - await page - .getByRole( 'region', { - name: 'Editor content', - } ) + await linkPopover .getByRole( 'button', { name: 'Advanced', } ) .click(); // expect settings for `Open in new tab` and `No follow` - await expect( page.getByLabel( 'Open in new tab' ) ).not.toBeChecked(); - await expect( page.getByLabel( 'nofollow' ) ).not.toBeChecked(); + await expect( + linkPopover.getByLabel( 'Open in new tab' ) + ).not.toBeChecked(); + await expect( linkPopover.getByLabel( 'nofollow' ) ).not.toBeChecked(); // Toggle both of the settings - await page.getByLabel( 'Open in new tab' ).click(); - await page.getByLabel( 'nofollow' ).click(); - - const linkPopover = LinkUtils.getLinkPopover(); + await linkPopover.getByLabel( 'Open in new tab' ).click(); + await linkPopover.getByLabel( 'nofollow' ).click(); // Save the link await linkPopover.getByRole( 'button', { name: 'Save' } ).click(); @@ -812,17 +845,20 @@ test.describe( 'Links', () => { }, ] ); - // Move caret back into the link. - await page.keyboard.press( 'ArrowRight' ); - await page.keyboard.press( 'ArrowRight' ); + // Click on "Gutenberg" link in the canvas again + await editor.canvas + .getByRole( 'link', { + name: 'Gutenberg', + } ) + .click(); // Edit the link - await page.getByRole( 'button', { name: 'Edit' } ).click(); + await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); // Toggle both the settings to be off. // Note: no need to toggle settings again because the open setting should be persisted. - await page.getByLabel( 'Open in new tab' ).click(); - await page.getByLabel( 'nofollow' ).click(); + await linkPopover.getByLabel( 'Open in new tab' ).click(); + await linkPopover.getByLabel( 'nofollow' ).click(); // Save the link await linkPopover.getByRole( 'button', { name: 'Save' } ).click(); @@ -845,20 +881,16 @@ test.describe( 'Links', () => { editor, LinkUtils, } ) => { - await LinkUtils.createAndReselectLink(); + await LinkUtils.createLink(); const originalLinkText = 'Gutenberg'; const changedLinkText = ' link text that was modified via the Link UI to include spaces '; - // Make a collapsed selection inside the link. This is used - // as a stress test to ensure we can find the link text from a - // collapsed RichTextValue that contains a link format. - await pageUtils.pressKeys( 'ArrowLeft' ); - await pageUtils.pressKeys( 'ArrowRight' ); + // Get the LinkPopover using the LinkUtils + const linkPopover = LinkUtils.getLinkPopover(); - await editor.showBlockToolbar(); - await page.getByRole( 'button', { name: 'Edit' } ).click(); + await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); const textInput = page.getByLabel( 'Text', { exact: true } ); @@ -919,13 +951,15 @@ test.describe( 'Links', () => { pageUtils, LinkUtils, } ) => { - await LinkUtils.createAndReselectLink(); + await LinkUtils.createLink(); + await pageUtils.pressKeys( 'Escape' ); // Make a collapsed selection inside the link. This is used // as a stress test to ensure we can find the link text from a // collapsed RichTextValue that contains a link format. await pageUtils.pressKeys( 'ArrowLeft' ); await pageUtils.pressKeys( 'ArrowRight' ); + await pageUtils.pressKeys( 'primary+k' ); const linkPopover = LinkUtils.getLinkPopover(); @@ -982,186 +1016,37 @@ test.describe( 'Links', () => { }, ] ); } ); - - test( 'should display (capture the) text from the currently active link even if there is a rich text selection', async ( { - editor, - pageUtils, - LinkUtils, - } ) => { - const originalLinkText = 'Gutenberg'; - - await LinkUtils.createAndReselectLink(); - - // Make a collapsed selection inside the link in order - // to activate the Link UI. - await pageUtils.pressKeys( 'ArrowLeft' ); - await pageUtils.pressKeys( 'ArrowRight' ); - - const linkPopover = LinkUtils.getLinkPopover(); - - await linkPopover.getByRole( 'button', { name: 'Edit' } ).click(); - - // Place cursor within the underling RichText link (not the Link UI). - await editor.canvas - .getByRole( 'document', { - name: 'Block: Paragraph', - } ) - .getByRole( 'link', { - name: 'Gutenberg', - } ) - .click(); - - // Make a selection within the RichText. - await pageUtils.pressKeys( 'shift+ArrowRight', { - times: 3, - } ); - - // Making a selection within the link text whilst the Link UI - // is open should not alter the value in the Link UI's "Text" - // field. It should remain as the full text of the currently - // focused link format. - await expect( - linkPopover.getByLabel( 'Text', { exact: true } ) - ).toHaveValue( originalLinkText ); - } ); } ); test.describe( 'Disabling Link UI active state', () => { - test( 'should not show the Link UI when selection extends beyond link boundary', async ( { + test( 'should correctly move focus when link control closes on click outside', async ( { page, pageUtils, - editor, LinkUtils, } ) => { - const linkedText = `Gutenberg`; - const textBeyondLinkedText = ` and more text.`; - - // Create a block with some text. - await editor.insertBlock( { - name: 'core/paragraph', - } ); - await page.keyboard.type( - `This is ${ linkedText }${ textBeyondLinkedText }` - ); - - // Move cursor next to end of `linkedText`. - await pageUtils.pressKeys( 'ArrowLeft', { - times: textBeyondLinkedText.length, - } ); - - // Select the linkedText. - await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); - - // Click on the Link button. - await editor.clickBlockToolbarButton( 'Link' ); - - // Type a URL. - await page.keyboard.type( 'https://wordpress.org/gutenberg' ); - - // Update the link. - await pageUtils.pressKeys( 'Enter' ); - await pageUtils.pressKeys( 'Escape' ); - await pageUtils.pressKeys( 'ArrowRight' ); - - // Reactivate the link. - await pageUtils.pressKeys( 'ArrowLeft' ); - await pageUtils.pressKeys( 'ArrowLeft' ); + await LinkUtils.createLink(); const linkPopover = LinkUtils.getLinkPopover(); await expect( linkPopover ).toBeVisible(); - // Make selection starting within the link and moving beyond boundary to the left. - await pageUtils.pressKeys( 'shiftAlt+ArrowLeft', { - times: linkedText.length, - } ); + const optionsButton = page + .getByRole( 'region', { name: 'Editor top bar' } ) + .getByRole( 'button', { name: 'Options' } ); - // The Link UI should have disappeared (i.e. be inactive). - await expect( linkPopover ).toBeHidden(); + await optionsButton.click(); - // Cancel selection and move back within the Link. - await pageUtils.pressKeys( 'ArrowRight' ); - - // We should see the Link UI displayed again. - await expect( linkPopover ).toBeVisible(); - - // Make selection starting within the link and moving beyond boundary to the right. - await pageUtils.pressKeys( 'shift+ArrowRight', { - times: 3, - } ); - - // The Link UI should have disappeared (i.e. be inactive). await expect( linkPopover ).toBeHidden(); - } ); - - test( 'should not show the Link UI when selection extends into another link', async ( { - page, - pageUtils, - editor, - LinkUtils, - } ) => { - const linkedTextOne = `Gutenberg`; - const linkedTextTwo = `Block Editor`; - const linkOneURL = 'https://wordpress.org'; - const linkTwoURL = 'https://wordpress.org/gutenberg'; - - // Create a block with some text. - await editor.insertBlock( { - name: 'core/paragraph', - } ); - await page.keyboard.type( - `This is the ${ linkedTextOne }${ linkedTextTwo }` - ); - - // Select the linkedTextTwo. - await pageUtils.pressKeys( 'shift+ArrowLeft', { - times: linkedTextTwo.length, - } ); - - // Click on the Link button. - await editor.clickBlockToolbarButton( 'Link' ); - - // Type a URL. - await page.keyboard.type( linkTwoURL ); - - // Update the link. - await pageUtils.pressKeys( 'Enter' ); - await pageUtils.pressKeys( 'Escape' ); - - // Move cursor next to the **end** of `linkTextOne` - await pageUtils.pressKeys( 'ArrowLeft' ); - - // Select `linkTextOne` - await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); - - // Click on the Link button. - await editor.clickBlockToolbarButton( 'Link' ); - - // Type a URL. - await page.keyboard.type( linkOneURL ); - - // Update the link. - await pageUtils.pressKeys( 'Enter' ); + // Expect focus on Top toolbar button within dropdown + await expect( + page.getByRole( 'menuitemcheckbox', { + name: 'Top toolbar Access all block and document tools in a single place', + } ) + ).toBeFocused(); + // Press Escape await pageUtils.pressKeys( 'Escape' ); - await pageUtils.pressKeys( 'ArrowRight' ); - - // Move cursor within `linkTextOne` - await pageUtils.pressKeys( 'ArrowLeft', { - times: 3, - } ); - - const linkPopover = LinkUtils.getLinkPopover(); - - // Link UI should activate for `linkTextOne` - await expect( linkPopover ).toBeVisible(); - - // Expand selection so that it overlaps with `linkTextTwo` - await pageUtils.pressKeys( 'Shift+ArrowRight', { - times: 6, - } ); - - // Link UI should be inactive. - await expect( linkPopover ).toBeHidden(); + // Expect focus on Top toolbar Options button + await expect( optionsButton ).toBeFocused(); } ); // Based on issue reported in https://github.com/WordPress/gutenberg/issues/41771/. @@ -1246,7 +1131,7 @@ class LinkUtils { }, isFixed ); } - async createAndReselectLink() { + async createLink() { // Create a block with some text. await this.editor.insertBlock( { name: 'core/paragraph', @@ -1257,18 +1142,19 @@ class LinkUtils { await this.pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); // Click on the Link button. - await this.page.getByRole( 'button', { name: 'Link' } ).click(); + await this.pageUtils.pressKeys( 'primary+k' ); + + // get the link popover + const linkPopover = this.getLinkPopover(); + + // Expect link popover to be visible + await expect( linkPopover ).toBeVisible(); // Type a URL. await this.page.keyboard.type( 'https://wordpress.org/gutenberg' ); - // Click on the Submit button. + // Submit the link. await this.pageUtils.pressKeys( 'Enter' ); - await this.pageUtils.pressKeys( 'Escape' ); - await this.pageUtils.pressKeys( 'End' ); - - // Reselect the link. - await this.pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); } /**