Skip to content

Commit

Permalink
Link Control: persist advanced settings toggle state to preferences i…
Browse files Browse the repository at this point in the history
…f available (#52799)

* Link Control: Create a preference for whether the advanced section is open

* move the useSelect to the component that uses it

* Supply preference setter via settings

* Pass setter to Post Editor

* Provide local state fallbacks in absence of preference store settings

* Conditionalise display of settings drawer to “edit” mode only

* Extract to constant to improve comprehension

* Fix bug in preferences resolution

* Improve comments

* Add e2e scaffold

* Fix e2e test to correctly assert on feature

* Remove focused test

* Reinstate original logic to hide settings when not required

* Scaffold documentation

* Revert providing prefs via settings

* Refactor to use `core/block-editor` as preference scope

* Update docs

* Reinstate remaining original conditional

* tentative fix for the e2e test

* Try a different syntax for shiftAlt

* another attempt to fix the e2e test

---------

Co-authored-by: Dave Smith <getdavemail@gmail.com>
Co-authored-by: MaggieCabrera <maggie.cabrera@automattic.com>
  • Loading branch information
3 people authored and tellthemachines committed Aug 15, 2023
1 parent 864ad25 commit a3d7887
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 10 deletions.
15 changes: 12 additions & 3 deletions packages/block-editor/src/components/link-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ The distinction between the two components is perhaps best summarized by the fol
- `<URLInput>` - an input for presenting and managing selection behaviors associated with choosing a URL, optionally from a pool of available candidates.
- `<LinkControl>` - includes the features of `<URLInput>`, plus additional UI and behaviors to control how this URL applies to the concept of a "link". This includes link "settings" (eg: "opens in new tab", etc) and dynamic, "on the fly" link creation capabilities.

## Persistent "Advanced" (settings) toggle state

By default the link "settings" are hidden and can be toggled open/closed by way of a button labelled `Advanced` in the UI.

In some circumstances if may be desirable to persist the toggle state of this portion of the UI so that it remains in the last state triggered by user interaction.

For example, once the user has toggled the UI to "open", then it may remain open across all links on the site until such time as the user toggles the UI back again.

Consumers who which to take advantage of this functionality should ensure that their block editor environment utilizes the [`@wordpress/preferences`](packages/preferences/README.md) package. By default the `<LinkControl>` component will attempt to persist the state of UI to a setting named `linkControlSettingsDrawer` with a scope of `core/block-editor`. If the preferences package is not available then local state is used and the setting will not be persisted.

## Search Suggestions

When creating links the `LinkControl` component will handle two kinds of input from users:
Expand Down Expand Up @@ -69,9 +79,7 @@ An array of settings objects associated with a link (for example: a setting to d
To disable settings, pass in an empty array. for example:

```jsx
<LinkControl
settings={ [] }
/>
<LinkControl settings={ [] } />
```

### onChange
Expand Down Expand Up @@ -192,6 +200,7 @@ A `suggestion` should have the following shape:
)}
/>
```

### renderControlBottom

- Type: `Function`
Expand Down
49 changes: 43 additions & 6 deletions packages/block-editor/src/components/link-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { useRef, useState, useEffect } from '@wordpress/element';
import { focus } from '@wordpress/dom';
import { ENTER } from '@wordpress/keycodes';
import { isShallowEqualObjects } from '@wordpress/is-shallow-equal';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as preferencesStore } from '@wordpress/preferences';

/**
* Internal dependencies
Expand Down Expand Up @@ -101,6 +103,9 @@ import { DEFAULT_LINK_SETTINGS } from './constants';

const noop = () => {};

const PREFERENCE_SCOPE = 'core/block-editor';
const PREFERENCE_KEY = 'linkControlSettingsDrawer';

/**
* Renders a link control. A link control is a controlled input which maintains
* a value associated with a link (HTML anchor element) and relevant settings
Expand Down Expand Up @@ -133,15 +138,48 @@ function LinkControl( {
withCreateSuggestion = true;
}

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

const { advancedSettingsPreference } = useSelect( ( select ) => {
const prefsStore = select( preferencesStore );

return {
advancedSettingsPreference:
prefsStore.get( PREFERENCE_SCOPE, PREFERENCE_KEY ) ?? false,
};
}, [] );

const { set: setPreference } = useDispatch( preferencesStore );

/**
* Sets the open/closed state of the Advanced Settings Drawer,
* optionlly persisting the state to the user's preferences.
*
* Note that Block Editor components can be consumed by non-WordPress
* environments which may not have preferences setup.
* Therefore a local state is also used as a fallback.
*
* @param {boolean} prefVal the open/closed state of the Advanced Settings Drawer.
*/
const setSettingsOpenWithPreference = ( prefVal ) => {
if ( setPreference ) {
setPreference( PREFERENCE_SCOPE, PREFERENCE_KEY, prefVal );
}
setSettingsOpen( prefVal );
};

// Block Editor components can be consumed by non-WordPress environments
// which may not have these preferences setup.
// Therefore a local state is used as a fallback.
const isSettingsOpen = advancedSettingsPreference || settingsOpen;

const isMounting = useRef( true );
const wrapperNode = useRef();
const textInputRef = useRef();
const isEndingEditWithFocus = useRef( false );

const settingsKeys = settings.map( ( { id } ) => id );

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

const [
internalControlValue,
setInternalControlValue,
Expand Down Expand Up @@ -207,7 +245,6 @@ function LinkControl( {
wrapperNode.current.ownerDocument.activeElement
);

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

Expand Down Expand Up @@ -292,7 +329,6 @@ function LinkControl( {
const shownUnlinkControl =
onRemove && value && ! isEditingLink && ! isCreatingPage;

const showSettings = !! settings?.length && isEditingLink && hasLinkValue;
const showActions = isEditingLink && hasLinkValue;

// Only show text control once a URL value has been committed
Expand All @@ -302,6 +338,7 @@ function LinkControl( {

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

return (
<div
Expand Down Expand Up @@ -382,8 +419,8 @@ function LinkControl( {
<div className="block-editor-link-control__tools">
{ ! currentInputIsEmpty && (
<LinkControlSettingsDrawer
settingsOpen={ settingsOpen }
setSettingsOpen={ setSettingsOpen }
settingsOpen={ isSettingsOpen }
setSettingsOpen={ setSettingsOpenWithPreference }
>
<LinkSettings
value={ internalControlValue }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1708,7 +1708,7 @@ describe( 'Selecting links', () => {
} );

describe( 'Addition Settings UI', () => {
it( 'should not show a means to toggle the link settings when not editing a link', async () => {
it( 'should hide advanced link settings when not editing a link', async () => {
const selectedLink = fauxEntitySuggestions[ 0 ];

const LinkControlConsumer = () => {
Expand All @@ -1723,6 +1723,7 @@ describe( 'Addition Settings UI', () => {

expect( settingsToggle ).not.toBeInTheDocument();
} );

it( 'should provides a means to toggle the link settings', async () => {
const selectedLink = fauxEntitySuggestions[ 0 ];

Expand Down
88 changes: 88 additions & 0 deletions test/e2e/specs/editor/blocks/links.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,92 @@ test.describe( 'Links', () => {
},
] );
} );

test( 'toggle state of advanced link settings is preserved across editing links', async ( {
page,
editor,
pageUtils,
} ) => {
// Create a block with some text.
await editor.insertBlock( {
name: 'core/paragraph',
} );
await page.keyboard.type( 'This is Gutenberg WordPress' );

// Select "WordPress".
await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' );

// Create a link.
await pageUtils.pressKeys( 'primary+k' );
await page.keyboard.type( 'w.org' );
await page.keyboard.press( 'Enter' );

// Move to edge of text "Gutenberg".
await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); // If you just use Alt here it won't work on windows.
await pageUtils.pressKeys( 'ArrowLeft' );
await pageUtils.pressKeys( 'ArrowLeft' );

// Select "Gutenberg".
await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' );

// Create a link.
await pageUtils.pressKeys( 'primary+k' );
await page.keyboard.type( 'https://wordpress.org/plugins/gutenberg/' );
await page.keyboard.press( 'Enter' );

// Move back into the link.
await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' );
await pageUtils.pressKeys( 'primary+k' );

// Toggle the Advanced settings to be open.
// This should set the editor preference to persist this
// UI state.
await page
.getByRole( 'region', {
name: 'Editor content',
} )
.getByRole( 'button', {
name: 'Advanced',
} )
.click();

// Move focus out of Link UI and into Paragraph block.
await pageUtils.pressKeys( 'Escape' );

// Move caret back into the "WordPress" link to trigger
// the Link UI for that link.
await pageUtils.pressKeys( 'Alt+ArrowRight' );
await pageUtils.pressKeys( 'ArrowRight' );
await pageUtils.pressKeys( 'ArrowRight' );

// Switch Link UI to "edit" mode.
await page.getByRole( 'button', { name: 'Edit' } ).click();

// Check that the Advanced settings are still expanded/open
// and I can see the open in new tab checkbox. This verifies
// that the editor preference was persisted.
await expect( page.getByLabel( 'Open in new tab' ) ).toBeVisible();

// Toggle the Advanced settings back to being closed.
await page
.getByRole( 'region', {
name: 'Editor content',
} )
.getByRole( 'button', {
name: 'Advanced',
} )
.click();

// Move focus out of Link UI and into Paragraph block.
await pageUtils.pressKeys( 'Escape' );

// Move caret back into the "Gutenberg" link and open
// the Link UI for that link.
await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' );
await pageUtils.pressKeys( 'primary+k' );

// Check that the Advanced settings are still closed.
// This verifies that the editor preference was persisted.
await expect( page.getByLabel( 'Open in new tab' ) ).not.toBeVisible();
} );
} );

0 comments on commit a3d7887

Please sign in to comment.