diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js
index 5b333a683cdb33..fb6b532c3c70c3 100644
--- a/packages/block-editor/src/components/link-control/index.js
+++ b/packages/block-editor/src/components/link-control/index.js
@@ -2,11 +2,12 @@
* External dependencies
*/
import { noop } from 'lodash';
+import classnames from 'classnames';
/**
* WordPress dependencies
*/
-import { Button, Spinner, Notice } from '@wordpress/components';
+import { Button, Spinner, Notice, TextControl } from '@wordpress/components';
import { keyboardReturn } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { useRef, useState, useEffect } from '@wordpress/element';
@@ -119,6 +120,7 @@ function LinkControl( {
noURLSuggestion = false,
createSuggestionButtonText,
hasRichPreviews = false,
+ hasTextControl = false,
} ) {
if ( withCreateSuggestion === undefined && createSuggestion ) {
withCreateSuggestion = true;
@@ -126,8 +128,12 @@ function LinkControl( {
const isMounting = useRef( true );
const wrapperNode = useRef();
+
const [ internalInputValue, setInternalInputValue ] = useState(
- ( value && value.url ) || ''
+ value?.url || ''
+ );
+ const [ internalTextValue, setInternalTextValue ] = useState(
+ value?.text || ''
);
const currentInputValue = propInputValue || internalInputValue;
const [ isEditingLink, setIsEditingLink ] = useState(
@@ -152,10 +158,14 @@ function LinkControl( {
return;
}
+ const linkURLInputIndex = 1;
+
+ // When editing, the 2nd focusable element is the Link URL input.
+ const whichFocusTarget = isEditingLink ? linkURLInputIndex : 0;
// When switching between editable and non editable LinkControl
- // move focus to the first element to avoid focus loss.
+ // move focus to the most appropriate element to avoid focus loss.
const nextFocusTarget =
- focus.focusable.find( wrapperNode.current )[ 0 ] ||
+ focus.focusable.find( wrapperNode.current )[ whichFocusTarget ] ||
wrapperNode.current;
nextFocusTarget.focus();
@@ -163,6 +173,16 @@ function LinkControl( {
isEndingEditWithFocus.current = false;
}, [ isEditingLink ] );
+ /**
+ * If the value's `text` property changes then sync this
+ * back up with state.
+ */
+ useEffect( () => {
+ if ( value?.text && value.text !== internalTextValue ) {
+ setInternalTextValue( value.text );
+ }
+ }, [ value ] );
+
/**
* Cancels editing state and marks that focus may need to be restored after
* the next render, if focus was within the wrapper when editing finished.
@@ -180,13 +200,22 @@ function LinkControl( {
);
const handleSelectSuggestion = ( updatedValue ) => {
- onChange( updatedValue );
+ onChange( {
+ ...updatedValue,
+ text: internalTextValue,
+ } );
stopEditing();
};
const handleSubmitButton = () => {
- if ( currentInputValue !== value?.url ) {
- onChange( { url: currentInputValue } );
+ if (
+ currentInputValue !== value?.url ||
+ internalTextValue !== value?.text
+ ) {
+ onChange( {
+ url: currentInputValue,
+ text: internalTextValue,
+ } );
}
stopEditing();
};
@@ -196,6 +225,9 @@ function LinkControl( {
const showSettingsDrawer = !! settings?.length;
+ // Only show once a URL value has been committed.
+ const showTextControl = hasTextControl;
+
return (
-
+
+ { showTextControl && (
+
+ ) }
+
- { richData?.title || value?.title || displayURL }
+ { richData?.title ||
+ value?.text ||
+ value?.title ||
+ displayURL }
{ value?.url && (
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 3a90bfb75344b6..eb0272e1847007 100644
--- a/packages/block-editor/src/components/link-control/search-input.js
+++ b/packages/block-editor/src/components/link-control/search-input.js
@@ -120,6 +120,7 @@ const LinkControlSearchInput = forwardRef(
return (
.components-base-control__field {
+ display: flex;
+ align-items: center;
+ margin: 0;
+ }
+
+ .components-base-control__label {
+ margin-right: $grid-unit-20;
+ margin-bottom: 0;
+ }
+
+ 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;
padding-right: ( $button-size * $block-editor-link-control-number-of-actions ); // width of reset and submit buttons
- margin: $grid-unit-20;
+ margin: 0;
position: relative;
border: 1px solid $gray-300;
border-radius: $radius-block-ui;
}
-
- .components-base-control__field {
- margin-bottom: 0;
- }
}
.block-editor-link-control__search-error {
@@ -61,17 +71,20 @@ $preview-image-height: 140px;
* when suggestions are rendered.
*
* Compensate for:
- * - Input margin ($grid-unit-20)
* - Border (1px)
* - Vertically, for the difference in height between the input (40px) and
* the icon buttons.
* - Horizontally, pad to the minimum of: default input padding, or the
* equivalent of the vertical padding.
*/
- top: $grid-unit-20 + 1px + ( ( 40px - $button-size ) * 0.5 );
+ top: 1px + ( ( 40px - $button-size ) * 0.5 );
right: $grid-unit-20 + 1px + min($grid-unit-10, ( 40px - $button-size ) * 0.5);
}
+.block-editor-link-control__search-input-wrapper.has-text-control .block-editor-link-control__search-actions {
+ top: 59px; // TODO: figure this out in variables!
+}
+
.components-button .block-editor-link-control__search-submit .has-icon {
margin: -1px;
}
diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js
index f390210bf19330..7f44ad03b0f95f 100644
--- a/packages/block-library/src/navigation-link/edit.js
+++ b/packages/block-library/src/navigation-link/edit.js
@@ -229,21 +229,32 @@ export const updateNavigationLinkBlockAttributes = (
const {
title = '',
url = '',
+ text = '',
opensInNewTab,
id,
kind: newKind = originalKind,
type: newType = originalType,
} = updatedValue;
-
const normalizedTitle = title.replace( /http(s?):\/\//gi, '' );
const normalizedURL = url.replace( /http(s?):\/\//gi, '' );
const escapeTitle =
title !== '' &&
normalizedTitle !== normalizedURL &&
originalLabel !== title;
- const label = escapeTitle
- ? escape( title )
- : originalLabel || escape( normalizedURL );
+
+ let label;
+
+ if ( text ) {
+ label = text;
+ } else if ( text === '' ) {
+ // If the user deliberately cleared out the link text
+ // then reset to default to the URL.
+ label = escape( normalizedURL );
+ } else {
+ label = escapeTitle
+ ? escape( title )
+ : originalLabel || escape( normalizedURL );
+ }
// In https://github.com/WordPress/gutenberg/pull/24670 we decided to use "tag" in favor of "post_tag"
const type = newType === 'post_tag' ? 'tag' : newType.replace( '-', '_' );
@@ -286,10 +297,13 @@ export default function NavigationLinkEdit( {
title,
kind,
} = attributes;
+
const link = {
url,
opensInNewTab,
+ text: label,
};
+
const { showSubmenuIcon } = context;
const { saveEntityRecord } = useDispatch( coreStore );
const { insertBlock } = useDispatch( blockEditorStore );
@@ -654,6 +668,7 @@ export default function NavigationLinkEdit( {
} }
/>