diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index 4803b9a0efebc4..41455c4ecf9704 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -292,7 +292,8 @@ function LinkControl( { const shownUnlinkControl = onRemove && value && ! isEditingLink && ! isCreatingPage; - const showSettings = !! settings?.length; + const showSettings = !! settings?.length && isEditingLink && hasLinkValue; + const showActions = isEditingLink && hasLinkValue; // Only show text control once a URL value has been committed // and it isn't just empty whitespace. @@ -322,6 +323,18 @@ function LinkControl( { 'has-text-control': showTextControl, } ) } > + { showTextControl && ( + + ) } - { showTextControl && ( - - ) } { errorMessage && ( ) } - { isEditing && ( + { showSettings && (
- { showSettings && ( + { ! currentInputIsEmpty && ( ) } +
+ ) } -
- - -
+ { showActions && ( +
+ +
) } diff --git a/packages/block-editor/src/components/link-control/search-input.js b/packages/block-editor/src/components/link-control/search-input.js index 4e30daaa96524b..d6023b9220d630 100644 --- a/packages/block-editor/src/components/link-control/search-input.js +++ b/packages/block-editor/src/components/link-control/search-input.js @@ -46,7 +46,7 @@ const LinkControlSearchInput = forwardRef( suggestionsQuery = {}, withURLSuggestion = true, createSuggestionButtonText, - useLabel = false, + hideLabelFromVision = false, }, ref ) => { @@ -120,7 +120,7 @@ const LinkControlSearchInput = forwardRef( }; const inputClasses = classnames( className, { - 'has-no-label': ! useLabel, + // 'has-no-label': ! hideLabelFromVision, } ); return ( @@ -128,7 +128,8 @@ const LinkControlSearchInput = forwardRef( setSettingsOpen( ! settingsOpen ) } - icon={ settingsIcon } - label={ __( 'Link Settings' ) } + icon={ isRTL() ? chevronLeftSmall : chevronRightSmall } aria-controls={ settingsDrawerId } - /> + > + { _x( 'Advanced', 'Additional link settings' ) } + { settingsOpen && ( .components-base-control__field { - display: flex; - align-items: center; - } - - .components-base-control__label { - margin-right: $grid-unit-20; - margin-bottom: 0; - min-width: 29px; // align with search results. - } - input[type="text"], // Specificity overide of URLInput defaults. &.block-editor-url-input input[type="text"].block-editor-url-input__input { @include input-control; - width: calc(100% - #{$grid-unit-20 * 2}); display: block; - padding: 11px $grid-unit-20; + border: 1px solid $gray-600; + border-radius: $radius-block-ui; + height: $button-size-next-default-40px; // components do not properly support unstable-large yet. margin: 0; + padding: $grid-unit-10 $grid-unit-20; position: relative; - border: 1px solid $gray-300; - border-radius: $radius-block-ui; + width: 100%; } } @@ -94,12 +83,12 @@ $preview-image-height: 140px; flex-direction: row-reverse; // put "Cancel" on the left but retain DOM order. justify-content: flex-start; gap: $grid-unit-10; + padding: $grid-unit-10; order: 20; } .block-editor-link-control__search-results-wrapper { position: relative; - margin-top: -$grid-unit-20 + 1px; &::before, &::after { @@ -440,36 +429,7 @@ $preview-image-height: 140px; display: flex; // allow for ordering. flex-direction: column; flex-basis: 100%; // occupy full width. - margin-top: $grid-unit-20; - padding-top: $grid-unit-20; position: relative; - - &::after { - content: ""; - display: block; - height: 1px; - background-color: $gray-300; - position: absolute; - left: -$grid-unit-20; - right: -$grid-unit-20; - top: 0; - } -} - -.block-editor-link-control__tools { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - margin: 0; - padding: $grid-unit-20; - - // To hide the horizontal scrollbar on toggle. - // Margin and padding are needed to prevent cutoff of the toggle button focus outline. - // See: https://github.com/WordPress/gutenberg/pull/47986 - margin-top: calc(var(--wp-admin-border-width-focus) * -1); - padding-top: var(--wp-admin-border-width-focus); - overflow: hidden; } .block-editor-link-control__unlink { @@ -477,17 +437,10 @@ $preview-image-height: 140px; padding-right: $grid-unit-20; } -.block-editor-link-control__settings { - flex: 1; - margin: 0; - - .is-alternate & { - border-top: $border-width solid $gray-900; - } -} - .block-editor-link-control__setting { margin-bottom: $grid-unit-20; + flex: 1; + padding: $grid-unit-10 0 $grid-unit-10 $grid-unit-30; // Cancel left margin inherited from WP Admin Forms CSS. input { @@ -499,6 +452,31 @@ $preview-image-height: 140px; } } +.block-editor-link-control__tools { + padding: $grid-unit-10 $grid-unit-10 0 $grid-unit-10; + margin-top: #{$grid-unit-20 * -1}; + + .components-button.block-editor-link-control__drawer-toggle { + padding-left: 0; + gap: 0; + + // Point downwards when open (same as list view expander) + &[aria-expanded="true"] svg { + visibility: visible; + transition: transform 0.1s ease; + transform: rotate(90deg); + @include reduce-motion("transition"); + } + // Point rightwards when closed (same as list view expander) + &[aria-expanded="false"] svg { + visibility: visible; + transform: rotate(0deg); + transition: transform 0.1s ease; + @include reduce-motion("transition"); + } + } +} + .block-editor-link-control .block-editor-link-control__search-input .components-spinner { display: block; diff --git a/packages/block-editor/src/components/link-control/test/index.js b/packages/block-editor/src/components/link-control/test/index.js index e5084844946be5..20b72363391ee1 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -138,7 +138,7 @@ describe( 'Basic rendering', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); expect( searchInput ).toBeVisible(); } ); @@ -147,7 +147,7 @@ describe( 'Basic rendering', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); expect( searchInput ).toBeVisible(); // Make sure we use the ARIA 1.0 pattern with aria-owns. @@ -170,7 +170,7 @@ describe( 'Basic rendering', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, 'Hello' ); @@ -283,7 +283,7 @@ describe( 'Basic rendering', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -296,7 +296,7 @@ describe( 'Basic rendering', () => { render( ); expect( - screen.queryByRole( 'combobox', { name: 'URL' } ) + screen.queryByRole( 'combobox', { name: 'Link' } ) ).not.toBeInTheDocument(); } ); @@ -309,7 +309,7 @@ describe( 'Basic rendering', () => { ); expect( - screen.getByRole( 'combobox', { name: 'URL' } ) + screen.getByRole( 'combobox', { name: 'Link' } ) ).toBeVisible(); } ); @@ -327,7 +327,7 @@ describe( 'Basic rendering', () => { await user.click( editButton ); expect( - screen.getByRole( 'combobox', { name: 'URL' } ) + screen.getByRole( 'combobox', { name: 'Link' } ) ).toBeVisible(); // If passed `forceIsEditingLink` of `false` while editing, should @@ -340,7 +340,7 @@ describe( 'Basic rendering', () => { ); expect( - screen.queryByRole( 'combobox', { name: 'URL' } ) + screen.queryByRole( 'combobox', { name: 'Link' } ) ).not.toBeInTheDocument(); } ); @@ -424,7 +424,7 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -448,7 +448,9 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -497,7 +499,9 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -528,7 +532,7 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -571,7 +575,7 @@ describe( 'Searching for a link', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, 'anything' ); @@ -582,12 +586,43 @@ describe( 'Searching for a link', () => { expect( mockFetchSearchSuggestions ).not.toHaveBeenCalled(); } ); + it( 'should not display a URL suggestion when input is not likely to be a URL.', async () => { + const searchTerm = 'unlikelytobeaURL'; + const user = userEvent.setup(); + render( ); + + // Search Input UI. + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); + + // Simulate searching for a term. + await user.type( searchInput, searchTerm ); + + const searchResultElements = within( + await screen.findByRole( 'listbox', { + name: /Search results for.*/, + } ) + ).getAllByRole( 'option' ); + + const lastSearchResultItem = + searchResultElements[ searchResultElements.length - 1 ]; + + // We should see a search result for each of the expect search suggestions. + expect( searchResultElements ).toHaveLength( + fauxEntitySuggestions.length + ); + + // The URL search suggestion should not exist. + expect( lastSearchResultItem ).not.toHaveTextContent( + 'Press ENTER to add this link' + ); + } ); + it( 'should not display a URL suggestion as a default fallback when noURLSuggestion is passed.', async () => { const user = userEvent.setup(); render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, 'couldbeurlorentitysearchterm' ); @@ -617,7 +652,9 @@ describe( 'Manual link entry', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -653,19 +690,9 @@ describe( 'Manual link entry', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', - } ); - - let submitButton = screen.getByRole( 'button', { - name: 'Save', + name: 'Link', } ); - expect( submitButton ).toHaveAttribute( - 'aria-disabled', - 'true' - ); - expect( submitButton ).toBeVisible(); - if ( searchString.length ) { // Simulate searching for a term. await user.type( searchInput, searchString ); @@ -677,102 +704,74 @@ describe( 'Manual link entry', () => { // Attempt to submit the empty search value in the input. await user.keyboard( '[Enter]' ); - submitButton = screen.getByRole( 'button', { - name: 'Save', - } ); - - // Verify the UI hasn't allowed submission. + // Verify the UI hasn't allowed submission because + // the search input is still visible. expect( searchInput ).toBeVisible(); - expect( submitButton ).toHaveAttribute( - 'aria-disabled', - 'true' - ); - expect( submitButton ).toBeVisible(); } ); it.each( testTable )( - 'should not allow creation of links %s via the UI "submit" button', + 'should not allow editing of links to a new link %s via the UI "submit" button', async ( _desc, searchString ) => { const user = userEvent.setup(); - render( ); + render( + + ); // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); - let submitButton = screen.queryByRole( 'button', { - name: 'Save', - } ); + // Remove the existing link. + await user.clear( searchInput ); - expect( submitButton ).toHaveAttribute( - 'aria-disabled', - 'true' - ); - expect( submitButton ).toBeVisible(); - - // Simulate searching for a term. if ( searchString.length ) { - // Simulate searching for a term. await user.type( searchInput, searchString ); } else { // Simulate clearing the search term. await user.clear( searchInput ); } - // Attempt to submit the empty search value in the input. - await user.click( submitButton ); - - submitButton = screen.queryByRole( 'button', { + const submitButton = screen.queryByRole( 'button', { name: 'Save', } ); - // Verify the UI hasn't allowed submission. - expect( searchInput ).toBeVisible(); + // debug the UI state + // screen.debug(); + + // Verify the submission UI is disabled. + expect( submitButton ).toBeVisible(); expect( submitButton ).toHaveAttribute( 'aria-disabled', 'true' ); - expect( submitButton ).toBeVisible(); + + // Attempt to submit the empty search value in the input. + await user.click( submitButton ); + + // Verify the UI hasn't allowed submission because + // the search input is still visible. + expect( searchInput ).toBeVisible(); } ); } ); describe( 'Handling cancellation', () => { - it( 'should allow cancellation of the link creation process and reset any entered values', async () => { - const user = userEvent.setup(); + it( 'should not show cancellation button during link creation', async () => { const mockOnRemove = jest.fn(); - const mockOnCancel = jest.fn(); render( ); - // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { - name: 'URL', - } ); - const cancelButton = screen.queryByRole( 'button', { name: 'Cancel', } ); - expect( cancelButton ).toBeEnabled(); - expect( cancelButton ).toBeVisible(); - - // Simulate adding a link for a term. - await user.type( searchInput, 'https://www.wordpress.org' ); - - // Attempt to submit the empty search value in the input. - await user.click( cancelButton ); - - // Verify the consumer can handle the cancellation. - expect( mockOnRemove ).toHaveBeenCalled(); - - // Ensure optional callback is not called. - expect( mockOnCancel ).not.toHaveBeenCalled(); - - expect( searchInput ).toHaveValue( '' ); + expect( cancelButton ).not.toBeInTheDocument(); } ); it( 'should allow cancellation of the link editing process and reset any entered values', async () => { @@ -809,7 +808,7 @@ describe( 'Manual link entry', () => { await toggleSettingsDrawer( user ); let searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); let textInput = screen.getByRole( 'textbox', { @@ -844,7 +843,7 @@ describe( 'Manual link entry', () => { // Re-query the inputs as they have been replaced. searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); textInput = screen.getByRole( 'textbox', { @@ -860,7 +859,13 @@ describe( 'Manual link entry', () => { const user = userEvent.setup(); const mockOnCancel = jest.fn(); - render( ); + render( + + ); const cancelButton = screen.queryByRole( 'button', { name: 'Cancel', @@ -886,7 +891,7 @@ describe( 'Manual link entry', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); // Simulate searching for a term. @@ -922,7 +927,7 @@ describe( 'Default search suggestions', () => { // Verify input has no value has default suggestions should only show // when this does not have a value. // Search Input UI. - expect( screen.getByRole( 'combobox', { name: 'URL' } ) ).toHaveValue( + expect( screen.getByRole( 'combobox', { name: 'Link' } ) ).toHaveValue( '' ); @@ -951,7 +956,7 @@ describe( 'Default search suggestions', () => { } ); await user.click( currentLinkBtn ); - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Search input is set to the URL value. expect( searchInput ).toHaveValue( initialValue.url ); @@ -973,7 +978,7 @@ describe( 'Default search suggestions', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -1011,7 +1016,7 @@ describe( 'Default search suggestions', () => { render( ); - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); const searchResultsField = screen.queryByRole( 'listbox', { name: 'Suggestions', @@ -1069,7 +1074,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, entityNameText ); @@ -1136,7 +1143,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, 'Some new page to create' ); @@ -1185,7 +1192,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, entityNameText ); @@ -1228,7 +1235,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, entityNameText ); @@ -1252,7 +1259,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); const searchResultsField = screen.queryByRole( 'listbox' ); @@ -1272,7 +1279,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); const searchResultsField = screen.queryByRole( 'listbox' ); @@ -1295,7 +1304,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); // Simulate searching for a term. @@ -1329,7 +1338,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { render( ); // Search Input UI. - searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchText ); @@ -1344,7 +1353,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { await user.click( createButton ); - searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); const errorNotice = screen.getAllByText( 'API response returned invalid entity.' @@ -1417,7 +1426,7 @@ describe( 'Selecting links', () => { // Simulate searching for a term. await user.click( currentLinkBtn ); - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); currentLinkUI = screen.queryByLabelText( 'Currently selected' ); // We should be back to showing the search input. @@ -1458,7 +1467,7 @@ describe( 'Selecting links', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); // Simulate searching for a term. @@ -1520,7 +1529,7 @@ describe( 'Selecting links', () => { // Search Input UI. const searchInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); // Simulate searching for a term. @@ -1609,7 +1618,9 @@ describe( 'Selecting links', () => { ).toBeVisible(); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Step down into the search results, highlighting the first result item. triggerArrowDown( searchInput ); @@ -1665,7 +1676,9 @@ describe( 'Selecting links', () => { render( ); // focus the search input - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); fireEvent.focus( searchInput ); @@ -1707,10 +1720,7 @@ describe( 'Addition Settings UI', () => { render( ); - const settingsToggle = screen.queryByRole( 'button', { - name: 'Link Settings', - ariaControls: 'link-settings-1', - } ); + const settingsToggle = getSettingsDrawerToggle(); expect( settingsToggle ).not.toBeInTheDocument(); } ); @@ -1727,10 +1737,7 @@ describe( 'Addition Settings UI', () => { const user = userEvent.setup(); - const settingsToggle = screen.queryByRole( 'button', { - name: 'Link Settings', - ariaControls: 'link-settings-1', - } ); + const settingsToggle = getSettingsDrawerToggle(); expect( settingsToggle ).toHaveAttribute( 'aria-expanded', 'false' ); @@ -1891,7 +1898,7 @@ describe( 'Post types', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { name: 'Link' } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -1918,7 +1925,9 @@ describe( 'Post types', () => { render( ); // Search Input UI. - const searchInput = screen.getByRole( 'combobox', { name: 'URL' } ); + const searchInput = screen.getByRole( 'combobox', { + name: 'Link', + } ); // Simulate searching for a term. await user.type( searchInput, searchTerm ); @@ -2376,10 +2385,14 @@ describe( 'Controlling link title text', () => { } ); } ); -async function toggleSettingsDrawer( user ) { - const settingsToggle = screen.queryByRole( 'button', { - name: 'Link Settings', +function getSettingsDrawerToggle() { + return screen.queryByRole( 'button', { + name: 'Advanced', } ); +} + +async function toggleSettingsDrawer( user ) { + const settingsToggle = getSettingsDrawerToggle(); await user.click( settingsToggle ); } diff --git a/packages/block-editor/src/components/media-replace-flow/test/index.js b/packages/block-editor/src/components/media-replace-flow/test/index.js index 2be7e7372260df..cef747ce6be639 100644 --- a/packages/block-editor/src/components/media-replace-flow/test/index.js +++ b/packages/block-editor/src/components/media-replace-flow/test/index.js @@ -128,7 +128,7 @@ describe( 'General media replace flow', () => { ); const mediaURLInput = screen.getByRole( 'combobox', { - name: 'URL', + name: 'Link', expanded: false, } ); diff --git a/packages/block-editor/src/components/url-input/index.js b/packages/block-editor/src/components/url-input/index.js index eb448f79d1b3f5..1451397ce68e5f 100644 --- a/packages/block-editor/src/components/url-input/index.js +++ b/packages/block-editor/src/components/url-input/index.js @@ -434,6 +434,7 @@ class URLInput extends Component { placeholder = __( 'Paste URL or type to search' ), __experimentalRenderControl: renderControl, value = '', + hideLabelFromVision = false, } = this.props; const { @@ -452,6 +453,7 @@ class URLInput extends Component { className: classnames( 'block-editor-url-input', className, { 'is-full-width': isFullWidth, } ), + hideLabelFromVision, }; const inputProps = { diff --git a/packages/e2e-tests/specs/editor/various/links.test.js b/packages/e2e-tests/specs/editor/various/links.test.js index 3be80f786fbc28..719d00afe076bb 100644 --- a/packages/e2e-tests/specs/editor/various/links.test.js +++ b/packages/e2e-tests/specs/editor/various/links.test.js @@ -105,7 +105,8 @@ describe( 'Links', () => { await waitForURLFieldAutoFocus(); const urlInputValue = await page.evaluate( - () => document.querySelector( '[aria-label="URL"]' ).value + () => + document.querySelector( '.block-editor-url-input__input' ).value ); expect( urlInputValue ).toBe( '' ); @@ -496,7 +497,7 @@ describe( 'Links', () => { await pressKeyWithModifier( 'primary', 'K' ); const [ settingsToggle ] = await page.$x( - '//button[contains(@aria-label, "Link Settings")]' + '//button[contains(text(), "Advanced")]' ); await settingsToggle.click(); @@ -528,7 +529,7 @@ describe( 'Links', () => { await waitForURLFieldAutoFocus(); - await page.keyboard.press( 'Tab' ); + await pressKeyWithModifier( 'shift', 'Tab' ); // Tabbing should land us in the text input. const { isTextInput, textValue } = await page.evaluate( () => { @@ -585,8 +586,10 @@ describe( 'Links', () => { await editButton.click(); - // Tabbing forward should land us in the "Text" input. - await page.keyboard.press( 'Tab' ); + await waitForURLFieldAutoFocus(); + + // Tabbing backward should land us in the "Text" input. + await pressKeyWithModifier( 'shift', 'Tab' ); const textInputValue = await page.evaluate( () => document.activeElement.value @@ -614,8 +617,9 @@ describe( 'Links', () => { ); await editButton.click(); - // tab forward to the text input. - await page.keyboard.press( 'Tab' ); + await waitForURLFieldAutoFocus(); + + await pressKeyWithModifier( 'shift', 'Tab' ); const textInputValue = await page.evaluate( () => document.activeElement.value @@ -661,7 +665,7 @@ describe( 'Links', () => { await waitForURLFieldAutoFocus(); const [ settingsToggle ] = await page.$x( - '//button[contains(@aria-label, "Link Settings")]' + '//button[contains(text(), "Advanced")]' ); await settingsToggle.click(); @@ -679,7 +683,7 @@ describe( 'Links', () => { await pressKeyWithModifier( 'shift', 'ArrowRight' ); // Move back to the text input. - await pressKeyTimes( 'Tab', 2 ); + await pressKeyTimes( 'Tab', 1 ); // Tabbing back should land us in the text input. const textInputValue = await page.evaluate( @@ -879,8 +883,11 @@ describe( 'Links', () => { await waitForURLFieldAutoFocus(); - // Move to Link Text field. - await page.keyboard.press( 'Tab' ); + // Move to "Text" field. + await pressKeyWithModifier( 'shift', 'Tab' ); + + // Delete existing value from "Text" field + await page.keyboard.press( 'Delete' ); // Change text to "z" await page.keyboard.type( 'z' ); diff --git a/test/e2e/specs/editor/blocks/buttons.spec.js b/test/e2e/specs/editor/blocks/buttons.spec.js index 8eacb7e2bed2e9..d6bdcb167f9ac9 100644 --- a/test/e2e/specs/editor/blocks/buttons.spec.js +++ b/test/e2e/specs/editor/blocks/buttons.spec.js @@ -10,6 +10,9 @@ test.describe( 'Buttons', () => { test( 'has focus on button content', async ( { editor, page } ) => { await editor.insertBlock( { name: 'core/buttons' } ); + await expect( + editor.canvas.locator( 'role=textbox[name="Button text"i]' ) + ).toBeFocused(); await page.keyboard.type( 'Content' ); // Check the content. @@ -50,9 +53,12 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/pull/19885 await editor.insertBlock( { name: 'core/buttons' } ); + await expect( + editor.canvas.locator( 'role=textbox[name="Button text"i]' ) + ).toBeFocused(); await pageUtils.pressKeys( 'primary+k' ); await expect( - page.locator( 'role=combobox[name="URL"i]' ) + page.locator( 'role=combobox[name="Link"i]' ) ).toBeFocused(); await page.keyboard.press( 'Escape' ); await expect( @@ -78,9 +84,12 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/issues/34307 await editor.insertBlock( { name: 'core/buttons' } ); + await expect( + editor.canvas.locator( 'role=textbox[name="Button text"i]' ) + ).toBeFocused(); await pageUtils.pressKeys( 'primary+k' ); await expect( - page.locator( 'role=combobox[name="URL"i]' ) + page.locator( 'role=combobox[name="Link"i]' ) ).toBeFocused(); await page.keyboard.type( 'https://example.com' ); await page.keyboard.press( 'Enter' ); @@ -107,9 +116,12 @@ test.describe( 'Buttons', () => { } ) => { // Regression: https://github.com/WordPress/gutenberg/issues/34307 await editor.insertBlock( { name: 'core/buttons' } ); + await expect( + editor.canvas.locator( 'role=textbox[name="Button text"i]' ) + ).toBeFocused(); await pageUtils.pressKeys( 'primary+k' ); - const urlInput = page.locator( 'role=combobox[name="URL"i]' ); + const urlInput = page.locator( 'role=combobox[name="Link"i]' ); await expect( urlInput ).toBeFocused(); await page.keyboard.type( 'example.com' ); diff --git a/test/e2e/specs/editor/blocks/links.spec.js b/test/e2e/specs/editor/blocks/links.spec.js index 6493b9effe8aab..8443a04d0417e6 100644 --- a/test/e2e/specs/editor/blocks/links.spec.js +++ b/test/e2e/specs/editor/blocks/links.spec.js @@ -28,8 +28,32 @@ test.describe( 'Links', () => { // Type a URL. await page.keyboard.type( 'https://wordpress.org/gutenberg' ); + // Ensure that the contents of the post have not been changed, since at + // this point the link is still not inserted. + await expect.poll( editor.getBlocks ).toMatchObject( [ + { + name: 'core/paragraph', + attributes: { content: 'This is Gutenberg' }, + }, + ] ); + + await page.keyboard.press( 'Enter' ); + + await page.keyboard.press( 'ArrowLeft' ); + await page.keyboard.press( 'ArrowLeft' ); + + // Edit link. + await page.getByRole( 'button', { name: 'Edit' } ).click(); + // Open settings. - await page.getByRole( 'button', { name: 'Link Settings' } ).click(); + await page + .getByRole( 'region', { + name: 'Editor content', + } ) + .getByRole( 'button', { + name: 'Advanced', + } ) + .click(); // Navigate to and toggle the "Open in new tab" checkbox. const checkbox = page.getByLabel( 'Open in new tab' ); @@ -39,15 +63,6 @@ test.describe( 'Links', () => { await expect( checkbox ).toBeChecked(); await expect( checkbox ).toBeFocused(); - // Ensure that the contents of the post have not been changed, since at - // this point the link is still not inserted. - await expect.poll( editor.getBlocks ).toMatchObject( [ - { - name: 'core/paragraph', - attributes: { content: 'This is Gutenberg' }, - }, - ] ); - // Tab back to the Submit and apply the link. await page //TODO: change to a better selector when https://github.com/WordPress/gutenberg/issues/51060 is resolved. @@ -82,11 +97,7 @@ test.describe( 'Links', () => { await pageUtils.pressKeys( 'primary+k' ); await page.keyboard.type( 'w.org' ); - await page - //TODO: change to a better selector when https://github.com/WordPress/gutenberg/issues/51060 is resolved. - .locator( '.block-editor-link-control' ) - .getByRole( 'button', { name: 'Save' } ) - .click(); + await page.keyboard.press( 'Enter' ); await expect.poll( editor.getBlocks ).toMatchObject( [ { @@ -107,7 +118,11 @@ test.describe( 'Links', () => { await page.keyboard.type( 'wordpress.org' ); // Update the link. - await page.keyboard.press( 'Enter' ); + await page + //TODO: change to a better selector when https://github.com/WordPress/gutenberg/issues/51060 is resolved. + .locator( '.block-editor-link-control' ) + .getByRole( 'button', { name: 'Save' } ) + .click(); // Navigate back to the popover. await page.keyboard.press( 'ArrowLeft' ); diff --git a/test/e2e/specs/editor/blocks/navigation.spec.js b/test/e2e/specs/editor/blocks/navigation.spec.js index 67b48d528adc70..8f45e8ddcf7e34 100644 --- a/test/e2e/specs/editor/blocks/navigation.spec.js +++ b/test/e2e/specs/editor/blocks/navigation.spec.js @@ -902,7 +902,7 @@ test.describe( 'Navigation block', () => { // Immediately dismiss the Link UI thereby not populating the `url` attribute // of the block. - await linkControl.pressCancel(); + await page.keyboard.press( 'Escape' ); // Get the Inspector Tabs. const blockSettings = page.getByRole( 'region', { @@ -1263,18 +1263,10 @@ class LinkControl { getSearchInput() { return this.page.getByRole( 'combobox', { - name: 'URL', + name: 'Link', } ); } - async pressCancel() { - const cancelButton = this.page.getByRole( 'button', { - name: 'Cancel', - } ); - - return cancelButton.click(); - } - async getSearchResults() { const searchInput = this.getSearchInput();