Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove "preview" mode/step from Link Control component #50998

Draft
wants to merge 24 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c4b166f
Remove edit control from preview
getdave May 26, 2023
5be5c28
Remove image and description from preview
getdave May 26, 2023
fbb69ad
Don’t use label in preview title and this represents an entity
getdave May 26, 2023
3a59112
Remove some editing-based conditionals
getdave Sep 7, 2023
6f39472
Remove remaining state relating to editing mode
getdave Sep 7, 2023
4b3faf3
Add border to separate preview from form
getdave Sep 7, 2023
f3c1460
Always focus firstElement in Popover
getdave Sep 7, 2023
afbed95
Make Popover a dialog
getdave Sep 7, 2023
a1a41dc
Allow for Popover to be forced closed when exiting the Link UI
getdave Sep 7, 2023
87d8d99
Reintstate rich media preview for attachments
getdave Sep 7, 2023
605f11c
Fix Nav block to conform to new steps
getdave Sep 7, 2023
a14c763
Don’t show cancel button if no handler is supplied
getdave Sep 7, 2023
6a310ac
Remove stopEditing internal mode
getdave Sep 7, 2023
31cd887
Call `onChange` will original value to mimic Cancel exiting the edit …
getdave Sep 7, 2023
7b1f6e0
Update Core implemenation for button
getdave Sep 7, 2023
251597c
Close UI on submitting link in Navigation block
getdave Sep 7, 2023
19c457c
Differentiate focus handling between create and edit
getdave Sep 13, 2023
cce2a69
Revert previous change
getdave Sep 13, 2023
7214db7
Don’t set ID on direct entry results type
getdave Jan 4, 2024
dbe5b82
Conditionally display search input only for direct entry results
getdave Jan 4, 2024
8c03f78
Show non-searchable URL input for direct entry types
getdave Jan 4, 2024
656bfb1
Details entity details in preview
getdave Jan 5, 2024
95d495b
Force stop editing link to remove activeFormats
getdave Jan 8, 2024
a4a0927
Close the Link UI on value updates except upon initial creation
getdave Jan 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { page, tag, postList, category, file } from '@wordpress/icons';

// Used as a unique identifier for the "Create" option within search results.
// Used to help distinguish the "Create" suggestion within the search results in
Expand All @@ -19,6 +20,14 @@ export const LINK_ENTRY_TYPES = [
INTERNAL_TYPE,
];

export const ICONS_MAP = {
post: postList,
page,
post_tag: tag,
category,
attachment: file,
};

export const DEFAULT_LINK_SETTINGS = [
{
id: 'opensInNewTab',
Expand Down
241 changes: 108 additions & 133 deletions packages/block-editor/src/components/link-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,6 @@ import { DEFAULT_LINK_SETTINGS } from './constants';
*
* @property {(WPLinkControlSetting[])=} settings An array of settings objects. Each object will used to
* render a `ToggleControl` for that setting.
* @property {boolean=} forceIsEditingLink If passed as either `true` or `false`, controls the
* internal editing state of the component to respective
* show or not show the URL input field.
* @property {WPLinkControlValue=} value Current link value.
* @property {WPLinkControlOnChangeProp=} onChange Value change handler, called with the updated value if
* the user selects a new link or updates settings.
Expand Down Expand Up @@ -130,7 +127,6 @@ function LinkControl( {
noDirectEntry = false,
showSuggestions = true,
showInitialSuggestions,
forceIsEditingLink,
createSuggestion,
withCreateSuggestion,
inputValue: propInputValue = '',
Expand Down Expand Up @@ -198,23 +194,9 @@ function LinkControl( {
const valueHasChanges =
value && ! isShallowEqualObjects( internalControlValue, value );

const [ isEditingLink, setIsEditingLink ] = useState(
forceIsEditingLink !== undefined
? forceIsEditingLink
: ! value || ! value.url
);

const { createPage, isCreatingPage, errorMessage } =
useCreatePage( createSuggestion );

useEffect( () => {
if ( forceIsEditingLink === undefined ) {
return;
}

setIsEditingLink( forceIsEditingLink );
}, [ forceIsEditingLink ] );

useEffect( () => {
// We don't auto focus into the Link UI on mount
// because otherwise using the keyboard to select text
Expand All @@ -236,22 +218,10 @@ function LinkControl( {
nextFocusTarget.focus();

isEndingEditWithFocus.current = false;
}, [ isEditingLink, isCreatingPage ] );
}, [ isCreatingPage ] );

const hasLinkValue = value?.url?.trim()?.length > 0;

/**
* 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.
*/
const stopEditing = () => {
isEndingEditWithFocus.current = !! wrapperNode.current?.contains(
wrapperNode.current.ownerDocument.activeElement
);

setIsEditingLink( false );
};

const handleSelectSuggestion = ( updatedValue ) => {
// Suggestions may contains "settings" values (e.g. `opensInNewTab`)
// which should not overide any existing settings values set by the
Expand All @@ -274,8 +244,6 @@ function LinkControl( {
// any "title" value provided by the "suggestion".
title: internalControlValue?.title || updatedValue?.title,
} );

stopEditing();
};

const handleSubmit = () => {
Expand All @@ -288,7 +256,6 @@ function LinkControl( {
url: currentUrlInputValue,
} );
}
stopEditing();
};

const handleSubmitWithEnter = ( event ) => {
Expand All @@ -314,35 +281,53 @@ function LinkControl( {
// Ensure that any unsubmitted input changes are reset.
resetInternalValues();

if ( hasLinkValue ) {
// If there is a link then exist editing mode and show preview.
stopEditing();
} else {
if ( ! hasLinkValue ) {
// If there is no link value, then remove the link entirely.
onRemove?.();
}

onCancel?.();
// In previous iterations of this component, clicking "Cancel" would toggle
// the component's UI into "preview" mode.
//
// However, this UI state was removed in:
// https://github.com/WordPress/gutenberg/pull/50998
//
// This poses a backwards compatibility problem as current consumers will
// assume that the previous UI behaviour is maintained.
//
// To avoid this, if a dedicated onCancel hanlder is not provided then the
// component will simply call onChange with the **original** (unchanged) value.
// This should effectively save the current value and close the UI.
if ( onCancel ) {
onCancel();
} else {
onChange( value );
}
Comment on lines +289 to +305
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the current mitigation strategy for backwards compatibility problems.

};

const currentUrlInputValue =
propInputValue || internalControlValue?.url || '';

const currentInputIsEmpty = ! currentUrlInputValue?.trim()?.length;

const shownUnlinkControl =
onRemove && value && ! isEditingLink && ! isCreatingPage;
const shownUnlinkControl = onRemove && value && ! isCreatingPage;

const showActions = hasLinkValue;

const showActions = isEditingLink && hasLinkValue;
// Show the searchable input if there is not a link value.
const showSearchInput = ! hasLinkValue;

// Show the plain URL input if there is a link value and
// it does not represent a Post object.
const showURLInput = hasLinkValue && ! value?.id;

// Only show text control once a URL value has been committed
// and it isn't just empty whitespace.
// See https://github.com/WordPress/gutenberg/pull/33849/#issuecomment-932194927.
const showTextControl = hasLinkValue && hasTextControl;

const isEditing = ( isEditingLink || ! value ) && ! isCreatingPage;
const isDisabled = ! valueHasChanges || currentInputIsEmpty;
const showSettings = !! settings?.length && isEditingLink && hasLinkValue;
const showSettings = !! settings?.length && hasLinkValue;

return (
<div
Expand All @@ -356,103 +341,92 @@ function LinkControl( {
</div>
) }

{ isEditing && (
<>
<div
className={ classnames( {
'block-editor-link-control__search-input-wrapper': true,
'has-text-control': showTextControl,
'has-actions': showActions,
} ) }
>
{ showTextControl && (
<TextControl
__nextHasNoMarginBottom
ref={ textInputRef }
className="block-editor-link-control__field block-editor-link-control__text-content"
label={ __( 'Text' ) }
value={ internalControlValue?.title }
onChange={ setInternalTextInputValue }
onKeyDown={ handleSubmitWithEnter }
size="__unstable-large"
/>
) }
<LinkControlSearchInput
currentLink={ value }
className="block-editor-link-control__field block-editor-link-control__search-input"
placeholder={ searchInputPlaceholder }
value={ currentUrlInputValue }
withCreateSuggestion={ withCreateSuggestion }
onCreateSuggestion={ createPage }
onChange={ setInternalURLInputValue }
onSelect={ handleSelectSuggestion }
showInitialSuggestions={ showInitialSuggestions }
allowDirectEntry={ ! noDirectEntry }
showSuggestions={ showSuggestions }
suggestionsQuery={ suggestionsQuery }
withURLSuggestion={ ! noURLSuggestion }
createSuggestionButtonText={
createSuggestionButtonText
}
hideLabelFromVision={ ! showTextControl }
/>
{ ! showActions && (
<div className="block-editor-link-control__search-enter">
<Button
onClick={ isDisabled ? noop : handleSubmit }
label={ __( 'Submit' ) }
icon={ keyboardReturn }
className="block-editor-link-control__search-submit"
aria-disabled={ isDisabled }
/>
</div>
) }
</div>
{ errorMessage && (
<Notice
className="block-editor-link-control__search-error"
status="error"
isDismissible={ false }
>
{ errorMessage }
</Notice>
) }
</>
) }

{ value && ! isEditingLink && ! isCreatingPage && (
{ hasLinkValue && ! isCreatingPage && (
<LinkPreview
key={ value?.url } // force remount when URL changes to avoid race conditions for rich previews
value={ value }
onEditClick={ () => setIsEditingLink( true ) }
hasRichPreviews={ hasRichPreviews }
hasUnlinkControl={ shownUnlinkControl }
additionalControls={ () => {
// Expose the "Opens in new tab" settings in the preview
// as it is the most common setting to change.
if (
settings?.find(
( setting ) => setting.id === 'opensInNewTab'
)
) {
return (
<LinkSettings
value={ internalControlValue }
settings={ settings?.filter(
( { id } ) => id === 'opensInNewTab'
) }
onChange={ onChange }
/>
);
}
} }
onRemove={ () => {
onRemove();
setIsEditingLink( true );
} }
onRemove={ onRemove }
/>
) }

<div
className={ classnames( {
'block-editor-link-control__search-input-wrapper': true,
'has-text-control': showTextControl,
'has-actions': showActions,
} ) }
>
{ showSearchInput && (
<LinkControlSearchInput
currentLink={ value }
className="block-editor-link-control__field block-editor-link-control__search-input"
placeholder={ searchInputPlaceholder }
value={ currentUrlInputValue }
withCreateSuggestion={ withCreateSuggestion }
onCreateSuggestion={ createPage }
onChange={ setInternalURLInputValue }
onSelect={ handleSelectSuggestion }
showInitialSuggestions={ showInitialSuggestions }
allowDirectEntry={ ! noDirectEntry }
showSuggestions={ showSuggestions }
suggestionsQuery={ suggestionsQuery }
withURLSuggestion={ ! noURLSuggestion }
createSuggestionButtonText={
createSuggestionButtonText
}
hideLabelFromVision={ ! showTextControl }
/>
) }

{ showURLInput && (
<TextControl
__nextHasNoMarginBottom
className="block-editor-link-control__field block-editor-link-control__search-input"
label={ __( 'Link' ) }
value={ currentUrlInputValue }
onChange={ setInternalURLInputValue }
onKeyDown={ handleSubmitWithEnter }
size="__unstable-large"
/>
) }

{ showTextControl && (
<TextControl
__nextHasNoMarginBottom
ref={ textInputRef }
className="block-editor-link-control__field block-editor-link-control__text-content"
label={ __( 'Text' ) }
value={ internalControlValue?.title }
onChange={ setInternalTextInputValue }
onKeyDown={ handleSubmitWithEnter }
size="__unstable-large"
/>
) }

{ ! showActions && (
<div className="block-editor-link-control__search-enter">
<Button
onClick={ isDisabled ? noop : handleSubmit }
label={ __( 'Submit' ) }
icon={ keyboardReturn }
className="block-editor-link-control__search-submit"
aria-disabled={ isDisabled }
/>
</div>
) }
</div>
{ errorMessage && (
<Notice
className="block-editor-link-control__search-error"
status="error"
isDismissible={ false }
>
{ errorMessage }
</Notice>
) }

{ showSettings && (
<div className="block-editor-link-control__tools">
{ ! currentInputIsEmpty && (
Expand Down Expand Up @@ -480,6 +454,7 @@ function LinkControl( {
<Button variant="tertiary" onClick={ handleCancel }>
{ __( 'Cancel' ) }
</Button>

<Button
variant="primary"
onClick={ isDisabled ? noop : handleSubmit }
Expand Down
Loading
Loading