diff --git a/core-blocks/image/edit.js b/core-blocks/image/edit.js
index 7792352bc7eae..ba7426e3ba8dc 100644
--- a/core-blocks/image/edit.js
+++ b/core-blocks/image/edit.js
@@ -204,7 +204,7 @@ class ImageEdit extends Component {
/>
) }
/>
-
+
);
diff --git a/core-blocks/image/index.js b/core-blocks/image/index.js
index 712c95ce1c02f..bb30318c305a4 100644
--- a/core-blocks/image/index.js
+++ b/core-blocks/image/index.js
@@ -47,6 +47,20 @@ const blockAttributes = {
source: 'attribute',
selector: 'figure > a',
attribute: 'href',
+ default: null,
+ },
+ target: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'figure > a',
+ attribute: 'target',
+ default: null,
+ },
+ rel: {
+ type: 'string',
+ source: 'attribute',
+ selector: 'figure > a',
+ attribute: 'rel',
},
id: {
type: 'number',
@@ -193,7 +207,7 @@ export const settings = {
edit,
save( { attributes } ) {
- const { url, alt, caption, align, href, width, height, id } = attributes;
+ const { url, alt, caption, align, href, width, height, id, target, rel } = attributes;
const classes = classnames( {
[ `align${ align }` ]: align,
@@ -212,7 +226,7 @@ export const settings = {
return (
);
diff --git a/core-blocks/test/fixtures/core__image.json b/core-blocks/test/fixtures/core__image.json
index 2ac6b609a3a8c..4733e1b19a735 100644
--- a/core-blocks/test/fixtures/core__image.json
+++ b/core-blocks/test/fixtures/core__image.json
@@ -6,9 +6,11 @@
"attributes": {
"url": "https://cldup.com/uuUqE_dXzy.jpg",
"alt": "",
+ "href": null,
+ "target": null,
"caption": []
},
"innerBlocks": [],
"originalContent": ""
}
-]
+]
\ No newline at end of file
diff --git a/core-blocks/test/fixtures/core__image__center-caption.json b/core-blocks/test/fixtures/core__image__center-caption.json
index 22ccf40cd5655..3c26ff719bcd0 100644
--- a/core-blocks/test/fixtures/core__image__center-caption.json
+++ b/core-blocks/test/fixtures/core__image__center-caption.json
@@ -6,6 +6,8 @@
"attributes": {
"url": "https://cldup.com/YLYhpou2oq.jpg",
"alt": "",
+ "href": null,
+ "target": null,
"caption": [
"Give it a try. Press the \"really wide\" button on the image toolbar."
],
@@ -14,4 +16,4 @@
"innerBlocks": [],
"originalContent": ""
}
-]
+]
\ No newline at end of file
diff --git a/core-blocks/test/fixtures/core__image__center-caption.parsed.json b/core-blocks/test/fixtures/core__image__center-caption.parsed.json
index abd5611da1948..16b41c3fcec7c 100644
--- a/core-blocks/test/fixtures/core__image__center-caption.parsed.json
+++ b/core-blocks/test/fixtures/core__image__center-caption.parsed.json
@@ -11,4 +11,4 @@
"attrs": {},
"innerHTML": "\n"
}
-]
+]
\ No newline at end of file
diff --git a/editor/components/url-input/button.js b/editor/components/url-input/button.js
index f42120c8ec6a3..170107ca7166d 100644
--- a/editor/components/url-input/button.js
+++ b/editor/components/url-input/button.js
@@ -9,39 +9,112 @@ import classnames from 'classnames';
import './style.scss';
import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
-import { IconButton } from '@wordpress/components';
+import { IconButton, ToggleControl, Popover } from '@wordpress/components';
+import { keycodes } from '@wordpress/utils';
+import { filterURLForDisplay } from '../../utils/url';
+import { prependHTTP } from '@wordpress/url';
+
+const { ESCAPE, LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } = keycodes;
/**
* Internal dependencies
*/
import UrlInput from './';
+const stopKeyPropagation = ( event ) => event.stopPropagation();
class UrlInputButton extends Component {
constructor() {
super( ...arguments );
this.toggle = this.toggle.bind( this );
this.submitLink = this.submitLink.bind( this );
+ this.dropLink = this.dropLink.bind( this );
+ this.toggleLinkSettingsVisibility = this.toggleLinkSettingsVisibility.bind( this );
+ this.setLinkTarget = this.setLinkTarget.bind( this );
+ this.onChangeLinkValue = this.onChangeLinkValue.bind( this );
+ this.onKeyDown = this.onKeyDown.bind( this );
+ this.editLink = this.editLink.bind( this );
+
this.state = {
expanded: false,
+ isEditing: true,
+ settingsVisible: false,
+ opensInNewWindow: this.props.attributes.target === '_blank' ? true : false,
+ linkValue: this.props.url,
};
}
+ onKeyDown( event ) {
+ if ( event.keyCode === ESCAPE ) {
+ this.dropLink();
+ this.toggle();
+ }
+ if ( [ LEFT, DOWN, RIGHT, UP, BACKSPACE, ENTER ].indexOf( event.keyCode ) > -1 ) {
+ stopKeyPropagation( event );
+ }
+ }
+
+ dropLink() {
+ this.props.setAttributes( { href: null } );
+ this.setState( { linkValue: '', settingsVisible: false } );
+ }
+
+ editLink() {
+ this.setState( { isEditing: ! this.state.isEditing } );
+ }
+
toggle() {
this.setState( { expanded: ! this.state.expanded } );
}
+ onChangeLinkValue( value ) {
+ this.setState( { linkValue: value } );
+ }
+
submitLink( event ) {
event.preventDefault();
- this.toggle();
+ const value = prependHTTP( this.state.linkValue );
+ this.props.setAttributes( {
+ href: value,
+ } );
+ this.setState( { isEditing: false, linkValue: value } );
+ }
+
+ toggleLinkSettingsVisibility() {
+ this.setState( ( state ) => ( { settingsVisible: ! state.settingsVisible } ) );
+ }
+
+ setLinkTarget( opensInNewWindow ) {
+ this.setState( { opensInNewWindow } );
+ if ( opensInNewWindow ) {
+ this.props.setAttributes( {
+ target: '_blank',
+ rel: 'noreferrer noopener',
+ } );
+ } else {
+ this.props.setAttributes( {
+ target: null,
+ rel: null,
+ } );
+ }
}
render() {
- const { url, onChange } = this.props;
- const { expanded } = this.state;
+ const { url, attributes: { id } } = this.props;
+ const { expanded, settingsVisible, opensInNewWindow, linkValue, isEditing } = this.state;
const buttonLabel = url ? __( 'Edit Link' ) : __( 'Insert Link' );
+ const linkSettings = settingsVisible && (
+
+
+
+ );
+
return (
-
+
- { expanded &&
-
+ {
+ expanded && (
+
+ { isEditing && (
+ // Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
+ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
+
+ ) }
+
+ { ! isEditing && (
+ // Disable reason: KeyPress must be suppressed so the block doesn't hide the toolbar
+ /* eslint-disable jsx-a11y/no-static-element-interactions */
+
+ /* eslint-enable jsx-a11y/no-static-element-interactions */
+ ) }
+
+ )
}
-
+
);
}
}
diff --git a/editor/components/url-input/index.js b/editor/components/url-input/index.js
index 21d1e98ef5578..f174140cee74c 100644
--- a/editor/components/url-input/index.js
+++ b/editor/components/url-input/index.js
@@ -13,6 +13,7 @@ import { __, sprintf, _n } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
import { keycodes, decodeEntities } from '@wordpress/utils';
import { Spinner, withInstanceId, withSpokenMessages, Popover } from '@wordpress/components';
+import { prependHTTP } from '@wordpress/url';
const { UP, DOWN, ENTER } = keycodes;
@@ -106,7 +107,8 @@ class UrlInput extends Component {
}
onChange( event ) {
- const inputValue = event.target.value;
+ const inputValue = prependHTTP( event.target.value );
+
this.props.onChange( inputValue );
this.updateSuggestions( inputValue );
}
diff --git a/editor/components/url-input/style.scss b/editor/components/url-input/style.scss
index 3789f52fc5d6b..28db4b5bc0bf7 100644
--- a/editor/components/url-input/style.scss
+++ b/editor/components/url-input/style.scss
@@ -109,3 +109,10 @@ $input-size: 230px;
height: $icon-button-size;
}
}
+
+.editor-format-toolbar__link-value {
+ &:focus {
+ box-shadow: none;
+ }
+}
+
diff --git a/editor/components/url-input/test/button.js b/editor/components/url-input/test/button.js
index 19bfc72c36b9c..ebb12b3ee18f1 100644
--- a/editor/components/url-input/test/button.js
+++ b/editor/components/url-input/test/button.js
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
-import { shallow, mount } from 'enzyme';
+import { shallow } from 'enzyme';
/**
* Internal dependencies
@@ -49,19 +49,4 @@ describe( 'UrlInputButton', () => {
wrapper.find( '[data-test=\'UrlInput\']' ).simulate( 'change' );
expect( onChangeMock ).toHaveBeenCalledTimes( 2 );
} );
- it( 'should close the form when user clicks Close button', () => {
- const wrapper = shallow( );
- clickEditLink( wrapper );
- expect( wrapper.state().expanded ).toBe( true );
- wrapper.find( '.editor-url-input__back' ).simulate( 'click' );
- expect( wrapper.state().expanded ).toBe( false );
- } );
- it( 'should close the form when user submits it', () => {
- const wrapper = mount( );
- clickEditLink( wrapper );
- expect( wrapper.state().expanded ).toBe( true );
- wrapper.find( 'form' ).simulate( 'submit' );
- expect( wrapper.state().expanded ).toBe( false );
- wrapper.unmount();
- } );
} );