diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index d2bac48450bb0..d16bd26d2b600 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -7,7 +7,6 @@ import { noop } from 'lodash'; * WordPress dependencies */ import { Button, Spinner, Notice } from '@wordpress/components'; -import { keyboardReturn } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; import { useRef, useState, useEffect } from '@wordpress/element'; import { focus } from '@wordpress/dom'; @@ -200,13 +199,14 @@ function LinkControl( { > { isCreatingPage && (
- { __( 'Creating' ) }… + + { __( 'Creating page' ) }…
) } { ( isEditingLink || ! value ) && ! isCreatingPage && ( <> -
+
-
-
+
+ { errorMessage && ( ) } + + ) } @@ -253,12 +260,6 @@ function LinkControl( { onEditClick={ () => setIsEditingLink( true ) } /> ) } - -
); } diff --git a/packages/block-editor/src/components/link-control/link-preview.js b/packages/block-editor/src/components/link-control/link-preview.js index ef6bd09fcf270..4cf7622821919 100644 --- a/packages/block-editor/src/components/link-control/link-preview.js +++ b/packages/block-editor/src/components/link-control/link-preview.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { Button, ExternalLink } from '@wordpress/components'; +import { Button } from '@wordpress/components'; import { filterURLForDisplay, safeDecodeURI } from '@wordpress/url'; /** @@ -24,31 +24,28 @@ export default function LinkPreview( { value, onEditClick } ) {
- - - { ( value && value.title ) || displayURL } - - { value && value.title && ( - - { displayURL } - - ) } - - + + +
); diff --git a/packages/block-editor/src/components/link-control/search-create-button.js b/packages/block-editor/src/components/link-control/search-create-button.js index 679c5df55c0fc..025f3341e3985 100644 --- a/packages/block-editor/src/components/link-control/search-create-button.js +++ b/packages/block-editor/src/components/link-control/search-create-button.js @@ -30,7 +30,7 @@ export const LinkControlSearchCreate = ( { text = createInterpolateElement( sprintf( /* translators: %s: search term. */ - __( 'Create: %s' ), + __( '"%s"' ), searchTerm ), { mark: } @@ -48,16 +48,16 @@ export const LinkControlSearchCreate = ( { ) } onClick={ onClick } > + + Create draft + + + { text } + - - - - { text } - - ); }; 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 c8ae882ae1aba..b22d444d85eb6 100644 --- a/packages/block-editor/src/components/link-control/search-input.js +++ b/packages/block-editor/src/components/link-control/search-input.js @@ -123,7 +123,7 @@ const LinkControlSearchInput = forwardRef( className={ className } value={ value } onChange={ onInputChange } - placeholder={ placeholder ?? __( 'Search or type url' ) } + placeholder={ placeholder ?? __( 'Search or type a url…' ) } __experimentalRenderSuggestions={ showSuggestions ? handleRenderSuggestions : null } diff --git a/packages/block-editor/src/components/link-control/search-item.js b/packages/block-editor/src/components/link-control/search-item.js index 804e5c95f05af..d52c85d2a9655 100644 --- a/packages/block-editor/src/components/link-control/search-item.js +++ b/packages/block-editor/src/components/link-control/search-item.js @@ -7,9 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url'; -import { __ } from '@wordpress/i18n'; import { Button, TextHighlight } from '@wordpress/components'; -import { Icon, globe } from '@wordpress/icons'; export const LinkControlSearchItem = ( { itemProps, @@ -30,12 +28,6 @@ export const LinkControlSearchItem = ( { 'is-entity': ! isURL, } ) } > - { isURL && ( - - ) } - - { ! isURL && - ( filterURLForDisplay( - safeDecodeURI( suggestion.url ) - ) || - '' ) } - { isURL && __( 'Press ENTER to add this link' ) } + + { filterURLForDisplay( safeDecodeURI( suggestion.url ) ) } { shouldShowType && suggestion.type && ( diff --git a/packages/block-editor/src/components/link-control/search-results.js b/packages/block-editor/src/components/link-control/search-results.js index cc24b81f18b17..cf2c700b4f0ca 100644 --- a/packages/block-editor/src/components/link-control/search-results.js +++ b/packages/block-editor/src/components/link-control/search-results.js @@ -114,6 +114,14 @@ export default function LinkControlSearchResults( { return null; } + if ( + directLinkEntryTypes.includes( + suggestion.type.toLowerCase() + ) + ) { + return null; + } + return ( .block-editor-link-control__search-item.block-editor-link-control__search-item { - padding: 10px; -} -.block-editor-link-control__settings { - border-top: $border-width solid $gray-300; - margin: 0; - padding: $grid-unit-20 $grid-unit-30; + .block-editor-link-control__search-create-description { + display: block; + font-size: 12px; + color: $gray-900; - :last-child { - margin-bottom: 0; + mark { + font-weight: normal; + } } - .is-alternate & { - border-top: $border-width solid $gray-900; + .block-editor-link-control__search-create-icon { + position: absolute; + top: 12px; + right: 8px; } } -.block-editor-link-control__setting { - margin-bottom: $grid-unit-20; +// ## Link Settings +// Links can be set to open in a new window +.block-editor-link-control__settings { + padding: $grid-unit-15; + margin: ($grid-unit-15 / 2) (-$grid-unit-15/2) (-$grid-unit-15/2) (-$grid-unit-15/2); + border-top: 1px solid $gray-900; - :last-child { - margin-bottom: 0; + .components-base-control__field { + margin: 0; } } -.block-editor-link-control .block-editor-link-control__search-input .components-spinner { - display: block; - - &.components-spinner { // Specificity override. - position: absolute; - left: auto; - bottom: auto; - /* - * Position spinner to the left of the actions. - * - * Compensate for: - * - Input margin ($grid-unit-20) - * - Border (1px) - * - Vertically, for the difference in height between the input (40px) - * and the spinner. - * - Horizontally, adjust for the width occupied by the icon buttons, - * then artificially create spacing that mimics as if the spinner - * were center-padded to the same width as an icon button. - */ - top: $grid-unit-20 + 1px + ( ( 40px - $spinner-size ) / 2 ); - right: $grid-unit-20 + 1px + ( $button-size * $block-editor-link-control-number-of-actions ) + ( ( $button-size - $spinner-size ) / 2 ); - } +// ## Link Preview +// When clicking an existing link, the preview popover +// shows the current link and a button to edit. +.block-editor-link-control__link { + display: flex; + align-items: center; + flex-direction: row-reverse; } -.block-editor-link-control__search-item-action { - margin-left: auto; // push to far right hand side - flex-shrink: 0; +.block-editor-link-control__link-current-url { + margin-right: $grid-unit-10; + + // An extra span is required as buttons normally display + // with inline-flex, but this doesn't support the truncation. + span { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 280px; + } } diff --git a/packages/block-editor/src/components/link-control/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/link-control/test/__snapshots__/index.js.snap index dd0bc6f80877c..26ff6d4006ba1 100644 --- a/packages/block-editor/src/components/link-control/test/__snapshots__/index.js.snap +++ b/packages/block-editor/src/components/link-control/test/__snapshots__/index.js.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Basic rendering should render 1`] = `""`; +exports[`Basic rendering should render 1`] = `""`; 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 c4feb75eca8e9..9fd55ec0e2124 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -3,7 +3,7 @@ */ import { render, unmountComponentAtNode } from 'react-dom'; import { act, Simulate } from 'react-dom/test-utils'; -import { queryByText, queryByRole } from '@testing-library/react'; +import { queryByText } from '@testing-library/react'; import { default as lodash, first, last, nth, uniqueId } from 'lodash'; /** * WordPress dependencies @@ -79,7 +79,7 @@ function getSearchResults() { function getCurrentLink() { return container.querySelector( - '.block-editor-link-control__search-item.is-current' + '.block-editor-link-control__link.is-current' ); } @@ -338,8 +338,8 @@ describe( 'Searching for a link', () => { const mockFetchSuggestionsFirstArg = mockFetchSearchSuggestions.mock.calls[ 0 ][ 0 ]; - // Given we're mocking out the results we should always have 4 mark elements. - expect( searchResultTextHighlightElements ).toHaveLength( 4 ); + // Given we're mocking out the results we should always have 3 mark elements. + expect( searchResultTextHighlightElements ).toHaveLength( 3 ); // Make sure there are no `mark` elements which contain anything other // than the trimmed search term (ie: no whitespace). @@ -378,26 +378,9 @@ describe( 'Searching for a link', () => { const searchResultElements = getSearchResults(); - const lastSearchResultItemHTML = last( searchResultElements ) - .innerHTML; - const additionalDefaultFallbackURLSuggestionLength = 1; - // We should see a search result for each of the expect search suggestions - // plus 1 additional one for the fallback URL suggestion expect( searchResultElements ).toHaveLength( - fauxEntitySuggestions.length + - additionalDefaultFallbackURLSuggestionLength - ); - - // The last item should be a URL search suggestion - expect( lastSearchResultItemHTML ).toEqual( - expect.stringContaining( searchTerm ) - ); - expect( lastSearchResultItemHTML ).toEqual( - expect.stringContaining( 'URL' ) - ); - expect( lastSearchResultItemHTML ).toEqual( - expect.stringContaining( 'Press ENTER to add this link' ) + fauxEntitySuggestions.length ); } ); @@ -456,23 +439,7 @@ describe( 'Manual link entry', () => { await eventLoopTick(); const searchResultElements = getSearchResults(); - - const firstSearchResultItemHTML = - searchResultElements[ 0 ].innerHTML; - const expectedResultsLength = 1; - - expect( searchResultElements ).toHaveLength( - expectedResultsLength - ); - expect( firstSearchResultItemHTML ).toEqual( - expect.stringContaining( searchTerm ) - ); - expect( firstSearchResultItemHTML ).toEqual( - expect.stringContaining( 'URL' ) - ); - expect( firstSearchResultItemHTML ).toEqual( - expect.stringContaining( 'Press ENTER to add this link' ) - ); + expect( searchResultElements[ 0 ] ).toBeUndefined(); } ); @@ -483,7 +450,7 @@ describe( 'Manual link entry', () => { [ '#internal-anchor', 'internal' ], ] )( 'should recognise "%s" as a %s link and handle as manual entry by displaying a single suggestion', - async ( searchTerm, searchType ) => { + async ( searchTerm ) => { act( () => { render( , container ); } ); @@ -502,23 +469,7 @@ describe( 'Manual link entry', () => { await eventLoopTick(); const searchResultElements = getSearchResults(); - - const firstSearchResultItemHTML = - searchResultElements[ 0 ].innerHTML; - const expectedResultsLength = 1; - - expect( searchResultElements ).toHaveLength( - expectedResultsLength - ); - expect( firstSearchResultItemHTML ).toEqual( - expect.stringContaining( searchTerm ) - ); - expect( firstSearchResultItemHTML ).toEqual( - expect.stringContaining( searchType ) - ); - expect( firstSearchResultItemHTML ).toEqual( - expect.stringContaining( 'Press ENTER to add this link' ) - ); + expect( searchResultElements[ 0 ] ).toBeUndefined(); } ); } ); @@ -584,7 +535,7 @@ describe( 'Default search suggestions', () => { expect( mockFetchSearchSuggestions ).not.toHaveBeenCalled(); // - // Click the "Edit/Change" button and check initial suggestions are not + // Click the "Edit" button and check initial suggestions are not // shown. // const currentLinkUI = getCurrentLink(); @@ -604,8 +555,8 @@ describe( 'Default search suggestions', () => { // search input is set to the URL value expect( searchInput.value ).toEqual( fauxEntitySuggestions[ 0 ].url ); - // it should match any url that's like ?p= and also include a URL option - expect( searchResultElements ).toHaveLength( 5 ); + // it should match any url that's like ?p= + expect( searchResultElements ).toHaveLength( 4 ); expect( searchInput.getAttribute( 'aria-expanded' ) ).toBe( 'true' ); @@ -760,7 +711,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { const createButton = first( Array.from( searchResultElements ).filter( ( result ) => - result.innerHTML.includes( 'Create:' ) + result.innerHTML.includes( 'Create draft' ) ) ); @@ -803,9 +754,6 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { const currentLinkHTML = currentLink.innerHTML; - expect( currentLinkHTML ).toEqual( - expect.stringContaining( entityNameText ) - ); expect( currentLinkHTML ).toEqual( expect.stringContaining( '/?p=123' ) ); @@ -857,7 +805,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { const createButton = first( Array.from( searchResultElements ).filter( ( result ) => - result.innerHTML.includes( 'Create:' ) + result.innerHTML.includes( 'Create draft' ) ) ); @@ -873,9 +821,6 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { const currentLinkHTML = currentLink.innerHTML; - expect( currentLinkHTML ).toEqual( - expect.stringContaining( 'Some new page to create' ) - ); expect( currentLinkHTML ).toEqual( expect.stringContaining( '/?p=123' ) ); @@ -930,7 +875,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { const form = container.querySelector( 'form' ); const createButton = first( Array.from( searchResultElements ).filter( ( result ) => - result.innerHTML.includes( 'Create:' ) + result.innerHTML.includes( 'Create draft' ) ) ); @@ -1028,7 +973,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { ); const createButton = first( Array.from( searchResultElements ).filter( ( result ) => - result.innerHTML.includes( 'Create:' ) + result.innerHTML.includes( 'Create draft' ) ) ); @@ -1153,7 +1098,7 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => { ); let createButton = first( Array.from( searchResultElements ).filter( ( result ) => - result.innerHTML.includes( 'Create:' ) + result.innerHTML.includes( 'Create draft' ) ) ); @@ -1217,17 +1162,10 @@ describe( 'Selecting links', () => { // TODO: select by aria role or visible text const currentLink = getCurrentLink(); - const currentLinkHTML = currentLink.innerHTML; const currentLinkAnchor = currentLink.querySelector( `[href="${ selectedLink.url }"]` ); - expect( currentLinkHTML ).toEqual( - expect.stringContaining( selectedLink.title ) - ); - expect( - queryByRole( currentLink, 'button', { name: 'Edit' } ) - ).toBeTruthy(); expect( currentLinkAnchor ).not.toBeNull(); } ); @@ -1270,16 +1208,6 @@ describe( 'Selecting links', () => { describe( 'Selection using mouse click', () => { it.each( [ [ 'entity', 'hello world', first( fauxEntitySuggestions ) ], // entity search - [ - 'url', - 'https://www.wordpress.org', - { - id: '1', - title: 'https://www.wordpress.org', - url: 'https://www.wordpress.org', - type: 'URL', - }, - ], // url ] )( 'should display a current selected link UI when a %s suggestion for the search "%s" is clicked', async ( type, searchTerm, selectedLink ) => { @@ -1321,7 +1249,7 @@ describe( 'Selecting links', () => { } ); const currentLink = container.querySelector( - '.block-editor-link-control__search-item.is-current' + '.block-editor-link-control__link.is-current' ); const currentLinkHTML = currentLink.innerHTML; const currentLinkAnchor = currentLink.querySelector( @@ -1329,9 +1257,6 @@ describe( 'Selecting links', () => { ); // Check that this suggestion is now shown as selected - expect( currentLinkHTML ).toEqual( - expect.stringContaining( selectedLink.title ) - ); expect( currentLinkHTML ).toEqual( expect.stringContaining( 'Edit' ) ); @@ -1343,16 +1268,6 @@ describe( 'Selecting links', () => { describe( 'Selection using keyboard', () => { it.each( [ [ 'entity', 'hello world', first( fauxEntitySuggestions ) ], // entity search - [ - 'url', - 'https://www.wordpress.org', - { - id: '1', - title: 'https://www.wordpress.org', - url: 'https://www.wordpress.org', - type: 'URL', - }, - ], // url ] )( 'should display a current selected link UI when an %s suggestion for the search "%s" is selected using the keyboard', async ( type, searchTerm, selectedLink ) => { @@ -1446,7 +1361,7 @@ describe( 'Selecting links', () => { // Check that the suggestion selected via is now shown as selected const currentLink = container.querySelector( - '.block-editor-link-control__search-item.is-current' + '.block-editor-link-control__link.is-current' ); const currentLinkHTML = currentLink.innerHTML; const currentLinkAnchor = currentLink.querySelector( @@ -1458,9 +1373,6 @@ describe( 'Selecting links', () => { true ); - expect( currentLinkHTML ).toEqual( - expect.stringContaining( selectedLink.title ) - ); expect( currentLinkHTML ).toEqual( expect.stringContaining( 'Edit' ) ); @@ -1559,6 +1471,15 @@ describe( 'Addition Settings UI', () => { render( , container ); } ); + // Click the "Edit" button to trigger into the editing mode. + const editButton = Array.from( + container.querySelectorAll( 'button' ) + ).find( ( button ) => button.innerHTML.includes( 'Edit' ) ); + + act( () => { + Simulate.click( editButton ); + } ); + const newTabSettingLabel = Array.from( container.querySelectorAll( 'label' ) ).find( @@ -1611,6 +1532,15 @@ describe( 'Addition Settings UI', () => { render( , container ); } ); + // Click the "Edit" button to trigger into the editing mode. + const editButton = Array.from( + container.querySelectorAll( 'button' ) + ).find( ( button ) => button.innerHTML.includes( 'Edit' ) ); + + act( () => { + Simulate.click( editButton ); + } ); + // Grab the elements using user perceivable DOM queries const settingsLegend = Array.from( container.querySelectorAll( 'legend' ) diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index fe32f2ab5d837..718f7df49e814 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -129,6 +129,7 @@ function URLPicker( { position="bottom center" onClose={ () => setIsURLPickerOpen( false ) } anchorRef={ anchorRef?.current } + isAlternate={ true } > setIsLinkOpen( false ) } > %s' + '"%s" (page)' ); } else { /* translators: %s: search term. */ format = __( - 'Create draft page: %s' + '"%s" (page)' ); } return createInterpolateElement( diff --git a/packages/e2e-tests/specs/editor/blocks/buttons.test.js b/packages/e2e-tests/specs/editor/blocks/buttons.test.js index 9253d29b1cbd4..eb302a3aa128f 100644 --- a/packages/e2e-tests/specs/editor/blocks/buttons.test.js +++ b/packages/e2e-tests/specs/editor/blocks/buttons.test.js @@ -46,9 +46,7 @@ describe( 'Buttons', () => { await page.keyboard.press( 'Enter' ); // Make sure that the dialog is still opened, and that focus is retained // within (focusing on the link preview). - await page.waitForSelector( - ':focus.block-editor-link-control__search-item-title' - ); + await page.waitForSelector( ':focus.block-editor-link-control__link' ); expect( await getEditedPostContent() ).toMatchSnapshot(); } ); diff --git a/packages/format-library/src/link/inline.js b/packages/format-library/src/link/inline.js index 4c78c9d1475a9..766261702cf3c 100644 --- a/packages/format-library/src/link/inline.js +++ b/packages/format-library/src/link/inline.js @@ -135,6 +135,7 @@ function InlineLinkUI( { focusOnMount={ focusOnMount.current } onClose={ stopAddingLink } position="bottom center" + isAlternate={ true } >