diff --git a/packages/block-editor/src/components/link-control/README.md b/packages/block-editor/src/components/link-control/README.md index d458448175ee9e..af25dfc2f42fe3 100644 --- a/packages/block-editor/src/components/link-control/README.md +++ b/packages/block-editor/src/components/link-control/README.md @@ -14,8 +14,20 @@ ### currentSettings -- Type: `Object` -- Required: Yes +- Type: `Array` +- Required: No +- Default: +``` +[ + { + id: 'newTab', + title: 'Open in New Tab', + checked: false, + }, +]; +``` + +An array of settings objects. Each object will used to render a `ToggleControl` for that setting. See also `onSettingsChange`. ### fetchSearchSuggestions @@ -71,7 +83,27 @@ The function callback will receive the selected item, or Null. /> ``` -### onSettingChange +### onSettingsChange - Type: `Function` - Required: No +- Args: + - `id` - the `id` property of the setting that changed (eg: `newTab`). + - `value` - the `checked` value of the control. + - `settings` - the current settings object. + +Called when any of the settings supplied as `currentSettings` are changed/toggled. May be used to attribute a Block's `attributes` with the current state of the control. + +``` + setAttributes( { [ setting ]: value } ) } +/> +``` + diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index f78124f1e51d8d..e1ad86afc773d5 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -53,7 +53,7 @@ function LinkControl( { onKeyDown = noop, onKeyPress = noop, onLinkChange = noop, - onSettingsChange = { noop }, + onSettingsChange = noop, } ) { // State const [ inputValue, setInputValue ] = useState( '' ); diff --git a/packages/block-editor/src/components/link-control/settings-drawer.js b/packages/block-editor/src/components/link-control/settings-drawer.js index 372426e4e821ff..3d9f957e207a83 100644 --- a/packages/block-editor/src/components/link-control/settings-drawer.js +++ b/packages/block-editor/src/components/link-control/settings-drawer.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { partial } from 'lodash'; +import { noop } from 'lodash'; /** * WordPress dependencies @@ -11,18 +11,39 @@ import { ToggleControl, } from '@wordpress/components'; -const LinkControlSettingsDrawer = ( { settings, onSettingChange } ) => { - if ( ! settings || settings.length ) { +const defaultSettings = [ + { + id: 'newTab', + title: __( 'Open in New Tab' ), + checked: false, + }, +]; + +const LinkControlSettingsDrawer = ( { settings = defaultSettings, onSettingChange = noop } ) => { + if ( ! settings || ! settings.length ) { return null; } + const handleSettingChange = ( setting ) => ( value ) => { + onSettingChange( setting.id, value, settings ); + }; + + const theSettings = settings.map( ( setting ) => ( + + ) ); + return ( -
- -
+
+ + { __( 'Currently selected link settings' ) } + + { theSettings } +
); }; diff --git a/packages/block-editor/src/components/link-control/style.scss b/packages/block-editor/src/components/link-control/style.scss index 2b1c9f050522db..5f99891b2370da 100644 --- a/packages/block-editor/src/components/link-control/style.scss +++ b/packages/block-editor/src/components/link-control/style.scss @@ -178,6 +178,14 @@ } } +.block-editor-link-control__setting { + margin-bottom: $grid-size-large; + + :last-child { + margin-bottom: 0; + } +} + .block-editor-link-control .block-editor-link-control__search-input .components-spinner { display: block; z-index: 100; 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 95e19973a0c073..5a69142b5f495e 100644 --- a/packages/block-editor/src/components/link-control/test/index.js +++ b/packages/block-editor/src/components/link-control/test/index.js @@ -536,3 +536,94 @@ describe( 'Selecting links', () => { } ); } ); } ); + +describe( 'Addition Settings UI', () => { + it( 'should display "New Tab" setting (in "off" mode) by default when a link is selected', async () => { + const selectedLink = first( fauxEntitySuggestions ); + const expectedSettingText = 'Open in New Tab'; + + const LinkControlConsumer = () => { + const [ link ] = useState( selectedLink ); + + return ( + + ); + }; + + act( () => { + render( + , container + ); + } ); + + // console.log( container.innerHTML ); + + const newTabSettingLabel = Array.from( container.querySelectorAll( 'label' ) ).find( ( label ) => label.innerHTML && label.innerHTML.includes( expectedSettingText ) ); + expect( newTabSettingLabel ).not.toBeUndefined(); // find() returns "undefined" if not found + + const newTabSettingLabelForAttr = newTabSettingLabel.getAttribute( 'for' ); + const newTabSettingInput = container.querySelector( `#${ newTabSettingLabelForAttr }` ); + expect( newTabSettingInput ).not.toBeNull(); + expect( newTabSettingInput.checked ).toBe( false ); + } ); + + it( 'should display a setting control with correct default state for each of the custom settings provided', async () => { + const selectedLink = first( fauxEntitySuggestions ); + + const customSettings = [ + { + id: 'newTab', + title: 'Open in New Tab', + checked: false, + }, + { + id: 'noFollow', + title: 'No follow', + checked: true, + }, + ]; + + const customSettingsLabelsText = customSettings.map( ( setting ) => setting.title ); + + const LinkControlConsumer = () => { + const [ link ] = useState( selectedLink ); + + return ( + + ); + }; + + act( () => { + render( + , container + ); + } ); + + // Grab the elements using user perceivable DOM queries + const settingsLegend = Array.from( container.querySelectorAll( 'legend' ) ).find( ( legend ) => legend.innerHTML && legend.innerHTML.includes( 'Currently selected link settings' ) ); + const settingsFieldset = settingsLegend.closest( 'fieldset' ); + const settingControlsLabels = Array.from( settingsFieldset.querySelectorAll( 'label' ) ); + const settingControlsInputs = settingControlsLabels.map( ( label ) => { + return settingsFieldset.querySelector( `#${ label.getAttribute( 'for' ) }` ); + } ); + + const settingControlLabelsText = Array.from( settingControlsLabels ).map( ( label ) => label.innerHTML ); + + // Check we have the correct number of controls + expect( settingControlsLabels ).toHaveLength( 2 ); + + // Check the labels match + expect( settingControlLabelsText ).toEqual( expect.arrayContaining( customSettingsLabelsText ) ); + + // Assert the default "checked" states match the expected + expect( settingControlsInputs[ 0 ].checked ).toEqual( false ); + expect( settingControlsInputs[ 1 ].checked ).toEqual( true ); + } ); +} ); diff --git a/packages/block-library/src/navigation-menu-item/edit.js b/packages/block-library/src/navigation-menu-item/edit.js index 9a0273c5de3c57..e9c3881695fadb 100644 --- a/packages/block-library/src/navigation-menu-item/edit.js +++ b/packages/block-library/src/navigation-menu-item/edit.js @@ -45,9 +45,7 @@ import { Fragment, useState, useEffect } from '@wordpress/element'; * @param {Function} setter Setter attribute function. */ const updateLinkSetting = ( setter ) => ( setting, value ) => { - if ( setting === 'new-tab' ) { - setter( { opensInNewTab: value } ); - } + setter( { [ setting ]: value } ); }; /** @@ -217,7 +215,13 @@ function NavigationMenuItemEdit( { onClose={ () => { onCloseTimerId = setTimeout( () => setIsLinkOpen( false ), 100 ); } } - currentSettings={ { 'new-tab': opensInNewTab } } + currentSettings={ [ + { + id: 'opensInNewTab', + title: __( 'Open in New Tab' ), + checked: opensInNewTab, + }, + ] } onSettingsChange={ updateLinkSetting( setAttributes ) } /> ) }