diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index 5f95d64adde6d9..7a7f93588027d1 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -2,7 +2,7 @@ * External dependencies */ import classnames from 'classnames'; -import { noop, startsWith } from 'lodash'; +import { noop, startsWith, pick, map } from 'lodash'; /** * WordPress dependencies @@ -211,6 +211,19 @@ function LinkControl( { setIsEditingLink( false ); } + /** + * Given a selected suggestion, returns the merged next value. The merged + * value includes settings values from the previous values. + * + * @param {WPLinkControlValue} suggestion Next value suggestion. + * + * @return {WPLinkControlValue} Merged next value. + */ + const getNextValue = ( suggestion ) => ( { + ...pick( value, map( settings, 'id' ) ), + ...suggestion, + } ); + // Effects const getSearchHandler = useCallback( ( val, args ) => { @@ -292,7 +305,7 @@ function LinkControl( { ) } suggestion={ suggestion } onClick={ () => { - onChange( { ...value, ...suggestion } ); + onChange( getNextValue( suggestion ) ); stopEditing(); } } isSelected={ index === selectedSuggestion } @@ -318,7 +331,7 @@ function LinkControl( { value={ inputValue } onChange={ onInputChange } onSelect={ ( suggestion ) => { - onChange( { ...value, ...suggestion } ); + onChange( getNextValue( suggestion ) ); stopEditing(); } } renderSuggestions={ renderSearchResults } 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 65d7729c973dc4..17b8fe59ddaa25 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -816,6 +816,54 @@ describe( 'Selecting links', () => { ); expect( isExpectedFocusTarget ).toBe( true ); } ); + + it( 'should update a selected link value', ( done ) => { + // Regression: Previously, the behavior of updating a value was to merge + // the previous value with the newly selected suggestion. If the keys + // between the two objects were not the same, it could wrongly leave + // lingering values from the previous value. + + const LinkControlConsumer = () => ( + { + expect( nextValue ).toEqual( { + url: 'https://example.com', + } ); + + done(); + } } + /> + ); + + act( () => { + render( , container ); + } ); + + // Toggle edit. + document + .querySelector( '.block-editor-link-control__search-item-action' ) + .click(); + + // Change value. + const form = container.querySelector( 'form' ); + const searchInput = container.querySelector( + 'input[aria-label="URL"]' + ); + + // Simulate searching for a term + act( () => { + Simulate.change( searchInput, { + target: { value: 'https://example.com' }, + } ); + } ); + act( () => { + Simulate.keyDown( searchInput, { keyCode: ENTER } ); + } ); + act( () => { + Simulate.submit( form ); + } ); + } ); } ); describe( 'Addition Settings UI', () => {