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

Add settings "drawer" to Link Control #47328

Merged
merged 23 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
849290d
Add basic panel toggle
getdave Jan 21, 2023
9bf55e6
Implement drawer like effect
getdave Jan 21, 2023
52c98eb
Implement basic animation
getdave Jan 21, 2023
f6f1972
Update to have drawer below button
getdave Jan 26, 2023
a8f5530
Update timing to 0.2s
getdave Jan 26, 2023
a5085ea
Fix janky animation
getdave Feb 2, 2023
bf9bfde
Close settings drawer when editing is stopped
getdave Feb 2, 2023
c5d4602
Fix input overflowing in settings drawer
getdave Feb 2, 2023
01b586d
Disable animations when user requests reduced motion
getdave Feb 3, 2023
cdd293b
Force no animations in component tests
getdave Feb 3, 2023
21facf6
Fix text input tests broken due to move into settings drawer
getdave Feb 3, 2023
526ec3e
Fix remaining tests broken by settings drawer
getdave Feb 3, 2023
176461b
speed up animation
Feb 3, 2023
d080336
Move drawer to dedicated component
getdave Feb 3, 2023
a683fe7
Add test for settings drawer with unique ID
getdave Feb 3, 2023
ae8c766
Check for aria expanded attribute
getdave Feb 3, 2023
dc60897
Avoid extra div when reduced motion is active
getdave Feb 3, 2023
ced642a
Don’t show settings unless in edit mode
getdave Feb 3, 2023
8fe8bb9
Add test for link settings toggle not being displayed unless editing
getdave Feb 6, 2023
5800d01
Fix e2e tests due to change of text input location
getdave Feb 6, 2023
227b970
Fix more e2e tests
getdave Feb 6, 2023
a88a3a9
Fix final e2e test
getdave Feb 6, 2023
c126405
Fix e2e test that arrived following rebase
getdave Feb 6, 2023
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
44 changes: 22 additions & 22 deletions packages/block-editor/src/components/link-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { Button, Spinner, Notice, TextControl } from '@wordpress/components';
import { Button, Spinner, Notice } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useRef, useState, useEffect } from '@wordpress/element';
import { focus } from '@wordpress/dom';
Expand Down Expand Up @@ -136,6 +136,8 @@ function LinkControl( {
const textInputRef = useRef();
const isEndingEditWithFocus = useRef( false );

const [ settingsOpen, setSettingsOpen ] = useState( false );

const [ internalUrlInputValue, setInternalUrlInputValue ] =
useInternalInputValue( value?.url || '' );

Expand Down Expand Up @@ -201,6 +203,7 @@ function LinkControl( {
wrapperNode.current.ownerDocument.activeElement
);

setSettingsOpen( false );
setIsEditingLink( false );
};

Expand Down Expand Up @@ -267,14 +270,15 @@ function LinkControl( {
const shownUnlinkControl =
onRemove && value && ! isEditingLink && ! isCreatingPage;

const showSettingsDrawer = !! settings?.length;
const showSettings = !! settings?.length;

// 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;

return (
<div
tabIndex={ -1 }
Expand All @@ -295,18 +299,6 @@ function LinkControl( {
'has-text-control': showTextControl,
} ) }
>
{ showTextControl && (
<TextControl
__nextHasNoMarginBottom
ref={ textInputRef }
className="block-editor-link-control__field block-editor-link-control__text-content"
label="Text"
value={ internalTextInputValue }
onChange={ setInternalTextInputValue }
onKeyDown={ handleSubmitWithEnter }
/>
) }

<LinkControlSearchInput
currentLink={ value }
className="block-editor-link-control__field block-editor-link-control__search-input"
Expand Down Expand Up @@ -350,18 +342,26 @@ function LinkControl( {
/>
) }

<div className="block-editor-link-control__drawer">
{ showSettingsDrawer && (
<div className="block-editor-link-control__tools">
{ isEditing && (
<div className="block-editor-link-control__tools">
{ ( showSettings || showTextControl ) && (
<LinkControlSettingsDrawer
settingsOpen={ settingsOpen }
setSettingsOpen={ setSettingsOpen }
showTextControl={ showTextControl }
showSettings={ showSettings }
textInputRef={ textInputRef }
internalTextInputValue={ internalTextInputValue }
setInternalTextInputValue={
setInternalTextInputValue
}
handleSubmitWithEnter={ handleSubmitWithEnter }
value={ value }
settings={ settings }
onChange={ onChange }
/>
</div>
) }
) }

{ isEditing && (
<div className="block-editor-link-control__search-actions">
<Button
variant="primary"
Expand All @@ -375,8 +375,8 @@ function LinkControl( {
{ __( 'Cancel' ) }
</Button>
</div>
) }
</div>
</div>
) }

{ renderControlBottom && renderControlBottom() }
</div>
Expand Down
115 changes: 85 additions & 30 deletions packages/block-editor/src/components/link-control/settings-drawer.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,96 @@
/**
* WordPress dependencies
*/
import {
Button,
TextControl,
__unstableMotion as motion,
__unstableAnimatePresence as AnimatePresence,
} from '@wordpress/components';
import { settings as settingsIcon } from '@wordpress/icons';
import { useReducedMotion, useInstanceId } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { ToggleControl, VisuallyHidden } from '@wordpress/components';

const noop = () => {};
import { Fragment } from '@wordpress/element';
/**
* Internal dependencies
*/
import Settings from './settings';

const LinkControlSettingsDrawer = ( { value, onChange = noop, settings } ) => {
if ( ! settings || ! settings.length ) {
return null;
}
function LinkSettingsDrawer( {
settingsOpen,
setSettingsOpen,
showTextControl,
showSettings,
textInputRef,
internalTextInputValue,
setInternalTextInputValue,
handleSubmitWithEnter,
value,
settings,
onChange,
} ) {
const prefersReducedMotion = useReducedMotion();
const MaybeAnimatePresence = prefersReducedMotion
? Fragment
: AnimatePresence;
const MaybeMotionDiv = prefersReducedMotion ? 'div' : motion.div;

const handleSettingChange = ( setting ) => ( newValue ) => {
onChange( {
...value,
[ setting.id ]: newValue,
} );
};
const id = useInstanceId( LinkSettingsDrawer );

const theSettings = settings.map( ( setting ) => (
<ToggleControl
className="block-editor-link-control__setting"
key={ setting.id }
label={ setting.title }
onChange={ handleSettingChange( setting ) }
checked={ value ? !! value[ setting.id ] : false }
/>
) );
const settingsDrawerId = `link-control-settings-drawer-${ id }`;

return (
<fieldset className="block-editor-link-control__settings">
<VisuallyHidden as="legend">
{ __( 'Currently selected link settings' ) }
</VisuallyHidden>
{ theSettings }
</fieldset>
<>
<Button
className="block-editor-link-control__drawer-toggle"
aria-expanded={ settingsOpen }
onClick={ () => setSettingsOpen( ! settingsOpen ) }
icon={ settingsIcon }
label={ __( 'Toggle link settings' ) }
aria-controls={ settingsDrawerId }
/>
<MaybeAnimatePresence>
{ settingsOpen && (
<MaybeMotionDiv
className="block-editor-link-control__drawer"
hidden={ ! settingsOpen }
id={ settingsDrawerId }
initial="collapsed"
animate="open"
exit="collapsed"
variants={ {
open: { opacity: 1, height: 'auto' },
collapsed: { opacity: 0, height: 0 },
} }
transition={ {
duration: 0.1,
} }
>
<div className="block-editor-link-control__drawer-inner">
{ showTextControl && (
<TextControl
__nextHasNoMarginBottom
ref={ textInputRef }
className="block-editor-link-control__setting block-editor-link-control__text-content"
label="Text"
value={ internalTextInputValue }
onChange={ setInternalTextInputValue }
onKeyDown={ handleSubmitWithEnter }
/>
) }
{ showSettings && (
<Settings
value={ value }
settings={ settings }
onChange={ onChange }
/>
) }
</div>
</MaybeMotionDiv>
) }
</MaybeAnimatePresence>
</>
);
};
}

export default LinkControlSettingsDrawer;
export default LinkSettingsDrawer;
41 changes: 41 additions & 0 deletions packages/block-editor/src/components/link-control/settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { ToggleControl, VisuallyHidden } from '@wordpress/components';

const noop = () => {};

const LinkControlSettings = ( { value, onChange = noop, settings } ) => {
if ( ! settings || ! settings.length ) {
return null;
}

const handleSettingChange = ( setting ) => ( newValue ) => {
onChange( {
...value,
[ setting.id ]: newValue,
} );
};

const theSettings = settings.map( ( setting ) => (
<ToggleControl
className="block-editor-link-control__setting"
key={ setting.id }
label={ setting.title }
onChange={ handleSettingChange( setting ) }
checked={ value ? !! value[ setting.id ] : false }
/>
) );

return (
<fieldset className="block-editor-link-control__settings">
<VisuallyHidden as="legend">
{ __( 'Currently selected link settings' ) }
</VisuallyHidden>
{ theSettings }
</fieldset>
);
};

export default LinkControlSettings;
40 changes: 33 additions & 7 deletions packages/block-editor/src/components/link-control/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ $preview-image-height: 140px;
flex-direction: row-reverse; // put "Cancel" on the left but retain DOM order.
justify-content: flex-start;
gap: $grid-unit-10;
order: 20;
}

.components-button .block-editor-link-control__search-submit .has-icon {
Expand Down Expand Up @@ -427,12 +428,42 @@ $preview-image-height: 140px;
}

.block-editor-link-control__drawer {
display: flex; // allow for ordering.
order: 30;
flex-direction: column;
flex-basis: 100%; // occupy full width.
}

// Inner div required to avoid padding/margin
// causing janky animation.
.block-editor-link-control__drawer-inner {
display: flex; // allow for ordering.
flex-direction: column;
flex-basis: 100%; // occupy full width.
margin-top: $grid-unit-20;
padding-top: $grid-unit-20;
position: relative;

&::after {
content: "";
display: block;
height: 1px;
background-color: $gray-300;
position: absolute;
left: -$grid-unit-20;
right: -$grid-unit-20;
top: 0;
}
}

.block-editor-link-control__tools {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
border-top: $border-width solid $gray-300;
margin: 0;
padding: $grid-unit-20;
padding-top: 0;
}

.block-editor-link-control__unlink {
Expand All @@ -444,11 +475,6 @@ $preview-image-height: 140px;
flex: 1;
margin: 0;


:last-child {
margin-bottom: 0;
}

.is-alternate & {
border-top: $border-width solid $gray-900;
}
Expand All @@ -457,7 +483,7 @@ $preview-image-height: 140px;
.block-editor-link-control__setting {
margin-bottom: $grid-unit-20;

:last-child {
&.block-editor-link-control__setting:last-child {
margin-bottom: 0;
}
}
Expand Down
Loading