diff --git a/packages/block-editor/src/components/link-control/README.md b/packages/block-editor/src/components/link-control/README.md index d143b7b5fcb3ed..45da4a723c07c2 100644 --- a/packages/block-editor/src/components/link-control/README.md +++ b/packages/block-editor/src/components/link-control/README.md @@ -127,6 +127,14 @@ This `suggestion` will then be _automatically_ passed to the `onChange` handler As a result of the above, this prop is often used to allow on the fly creation of new entities (eg: `Posts`, `Pages`) based on the text the user has entered into the link search UI. As an example, the Navigation Block uses `createSuggestion` to create Pages on the fly from within the Block itself. +### onRemove + +- Type: `Function` +- Required: No +- Default: null + +An optional handler, which when passed will trigger the display of an `Unlink` UI within the control. This handler is expected to remove the current `value` of the control thus resetting it back to a default state. The key use case for this is allowing users to remove a link from the control without relying on there being an "unlink" control in the block toolbar. + #### Search `suggestion` values A `suggestion` should have the following shape: diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index b9a63ceb71da3c..db6b005ac79fab 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -106,6 +106,7 @@ function LinkControl( { value, settings, onChange = noop, + onRemove, noDirectEntry = false, showSuggestions = true, showInitialSuggestions, @@ -260,11 +261,23 @@ function LinkControl( { /> ) } - +
+ + { onRemove && value && ! isEditingLink && ! isCreatingPage && ( + + ) } +
); } diff --git a/packages/block-editor/src/components/link-control/style.scss b/packages/block-editor/src/components/link-control/style.scss index b1b6958dbe5f8c..3d5d5941bb7749 100644 --- a/packages/block-editor/src/components/link-control/style.scss +++ b/packages/block-editor/src/components/link-control/style.scss @@ -380,10 +380,23 @@ $preview-image-height: 140px; padding: 10px; } -.block-editor-link-control__settings { +.block-editor-link-control__tools { + display: flex; + align-items: center; border-top: $border-width solid $gray-300; margin: 0; padding: $grid-unit-20 $grid-unit-30; +} + +.block-editor-link-control__unlink { + padding-left: $grid-unit-20; + padding-right: $grid-unit-20; +} + +.block-editor-link-control__settings { + flex: 1; + margin: 0; + :last-child { margin-bottom: 0; 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 27e1665d869ff2..250e4656d3708d 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -227,6 +227,49 @@ describe( 'Basic rendering', () => { expect( isEditing() ).toBe( false ); } ); } ); + + describe( 'Unlinking', () => { + it( 'should not show "Unlink" button if no onRemove handler is provided', () => { + act( () => { + render( + , + container + ); + } ); + + const unLinkButton = queryByRole( container, 'button', { + name: 'Unlink', + } ); + + expect( unLinkButton ).toBeNull(); + expect( unLinkButton ).not.toBeInTheDocument(); + } ); + + it( 'should show "Unlink" button if a onRemove handler is provided', () => { + const mockOnRemove = jest.fn(); + act( () => { + render( + , + container + ); + } ); + + const unLinkButton = queryByRole( container, 'button', { + name: 'Unlink', + } ); + expect( unLinkButton ).toBeTruthy(); + expect( unLinkButton ).toBeInTheDocument(); + + act( () => { + Simulate.click( unLinkButton ); + } ); + + expect( mockOnRemove ).toHaveBeenCalled(); + } ); + } ); } ); describe( 'Searching for a link', () => { diff --git a/packages/format-library/src/link/inline.js b/packages/format-library/src/link/inline.js index d015267a8e38f7..8a9e7efae2fa1e 100644 --- a/packages/format-library/src/link/inline.js +++ b/packages/format-library/src/link/inline.js @@ -11,6 +11,7 @@ import { isCollapsed, applyFormat, useAnchorRef, + removeFormat, } from '@wordpress/rich-text'; import { __experimentalLinkControl as LinkControl } from '@wordpress/block-editor'; @@ -48,6 +49,13 @@ function InlineLinkUI( { ...nextLinkValue, }; + function removeLink() { + const newValue = removeFormat( value, 'core/link' ); + onChange( newValue ); + stopAddingLink(); + speak( __( 'Link removed.' ), 'assertive' ); + } + function onChangeLink( nextValue ) { // Merge with values from state, both for the purpose of assigning the // next state value, and for use in constructing the new link format if @@ -139,6 +147,7 @@ function InlineLinkUI( {