Skip to content

Commit

Permalink
Update LinkControl component to utilitse dynamic settings for addit…
Browse files Browse the repository at this point in the history
…ional settings "drawer" (#18285)

* Adds basic coverage test for new tab setting

* Refactor ‘new-tab` to camelCase for consistency

* Fix bug with null render if settings has a length and convert to array

Make settings an array so that it can be ordered. Fix incorrect conditional testing for .length to ensure it passes if settings _does_ have a length.

* Updates to loop over supplied settings

Previously only a “new tab” setting was hardcoded. Update so retain “new tab” as the default, but also allow for custom settings if/when provided.

* Updates docs

* Updates Nav Item Block to utilise new setting format

* Updates spacing between individual setting controls

* Update docs for settings

* Updates setting key/id to match attribute to simply and improve consistency

* Fix Nav Menu Block dynamic setting of attribute

Addresses #18285 (comment)
  • Loading branch information
getdave authored Nov 6, 2019
1 parent 0e1564b commit 3ab9fb6
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 17 deletions.
38 changes: 35 additions & 3 deletions packages/block-editor/src/components/link-control/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
```
<LinkControl
currentSettings={ [
{
id: 'opensInNewTab',
title: __( 'Open in New Tab' ),
checked: attributes.opensInNewTab, // Block attributes persist control state
},
] }
onSettingsChange={ ( setting, value ) => setAttributes( { [ setting ]: value } ) }
/>
```
2 changes: 1 addition & 1 deletion packages/block-editor/src/components/link-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function LinkControl( {
onKeyDown = noop,
onKeyPress = noop,
onLinkChange = noop,
onSettingsChange = { noop },
onSettingsChange = noop,
} ) {
// State
const [ inputValue, setInputValue ] = useState( '' );
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { partial } from 'lodash';
import { noop } from 'lodash';

/**
* WordPress dependencies
Expand All @@ -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 ) => (
<ToggleControl
className="block-editor-link-control__setting"
key={ setting.id }
label={ setting.title }
onChange={ handleSettingChange( setting ) }
checked={ setting.checked } />
) );

return (
<div className="block-editor-link-control__settings">
<ToggleControl
label={ __( 'Open in New Tab' ) }
onChange={ partial( onSettingChange, 'new-tab' ) }
checked={ settings[ 'new-tab' ] } />
</div>
<fieldset className="block-editor-link-control__settings">
<legend className="screen-reader-text">
{ __( 'Currently selected link settings' ) }
</legend>
{ theSettings }
</fieldset>
);
};

Expand Down
8 changes: 8 additions & 0 deletions packages/block-editor/src/components/link-control/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
91 changes: 91 additions & 0 deletions packages/block-editor/src/components/link-control/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<LinkControl
currentLink={ link }
fetchSearchSuggestions={ fetchFauxEntitySuggestions }
/>
);
};

act( () => {
render(
<LinkControlConsumer />, 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 (
<LinkControl
currentLink={ link }
fetchSearchSuggestions={ fetchFauxEntitySuggestions }
currentSettings={ customSettings }
/>
);
};

act( () => {
render(
<LinkControlConsumer />, 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 );
} );
} );
12 changes: 8 additions & 4 deletions packages/block-library/src/navigation-menu-item/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 } );
};

/**
Expand Down Expand Up @@ -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 ) }
/>
) }
Expand Down

0 comments on commit 3ab9fb6

Please sign in to comment.