From f33498b0d6746e64a65a5dbccf31507842bf4e15 Mon Sep 17 00:00:00 2001 From: William Earnhardt Date: Tue, 20 Nov 2018 02:25:27 -0500 Subject: [PATCH] Update displayed permalink when slug is cleared (#11783) --- .../src/components/sidebar/post-link/index.js | 26 +++++++++++++++---- .../src/components/post-permalink/editor.js | 11 +++++--- .../src/components/post-permalink/index.js | 24 ++++++++++++----- packages/editor/src/utils/index.js | 1 + packages/editor/src/utils/test/url.js | 10 +++++++ packages/editor/src/utils/url.js | 25 ++++++++++++++++++ 6 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 packages/editor/src/utils/test/url.js diff --git a/packages/edit-post/src/components/sidebar/post-link/index.js b/packages/edit-post/src/components/sidebar/post-link/index.js index 744d96a6ec37aa..9073402804dc9d 100644 --- a/packages/edit-post/src/components/sidebar/post-link/index.js +++ b/packages/edit-post/src/components/sidebar/post-link/index.js @@ -12,6 +12,7 @@ import { PanelBody, TextControl, ExternalLink } from '@wordpress/components'; import { withSelect, withDispatch } from '@wordpress/data'; import { compose, ifCondition, withState } from '@wordpress/compose'; import { addQueryArgs } from '@wordpress/url'; +import { cleanForSlug } from '@wordpress/editor'; /** * Module Constants @@ -27,15 +28,19 @@ function PostLink( { editPermalink, forceEmptyField, setState, + postTitle, + postSlug, + postID, } ) { - const { prefix, postName, suffix } = permalinkParts; + const { prefix, suffix } = permalinkParts; let prefixElement, postNameElement, suffixElement; + const currentSlug = postSlug || cleanForSlug( postTitle ) || postID; if ( isEditable ) { prefixElement = prefix && ( { prefix } ); - postNameElement = postName && ( - { postName } + postNameElement = currentSlug && ( + { currentSlug } ); suffixElement = suffix && ( { suffix } @@ -51,7 +56,7 @@ function PostLink( { { isEditable && ( { editPermalink( newValue ); // When we delete the field the permalink gets @@ -72,6 +77,14 @@ function PostLink( { } ); } } } + onBlur={ ( event ) => { + editPermalink( cleanForSlug( event.target.value ) ); + if ( forceEmptyField ) { + setState( { + forceEmptyField: false, + } ); + } + } } /> ) }

@@ -117,7 +130,7 @@ export default compose( [ getPostType, } = select( 'core' ); - const { link } = getCurrentPost(); + const { link, id } = getCurrentPost(); const postTypeName = getEditedPostAttribute( 'type' ); const postType = getPostType( postTypeName ); return { @@ -128,6 +141,9 @@ export default compose( [ isOpened: isEditorPanelOpened( PANEL_NAME ), permalinkParts: getPermalinkParts(), isViewable: get( postType, [ 'viewable' ], false ), + postTitle: getEditedPostAttribute( 'title' ), + postSlug: getEditedPostAttribute( 'slug' ), + postID: id, }; } ), ifCondition( ( { isNew, postLink, isViewable } ) => { diff --git a/packages/editor/src/components/post-permalink/editor.js b/packages/editor/src/components/post-permalink/editor.js index 3831d8a800579f..593af16f2c4686 100644 --- a/packages/editor/src/components/post-permalink/editor.js +++ b/packages/editor/src/components/post-permalink/editor.js @@ -7,19 +7,24 @@ import { __ } from '@wordpress/i18n'; import { Button } from '@wordpress/components'; import { compose } from '@wordpress/compose'; +/** + * Internal dependencies + */ +import { cleanForSlug } from '../../utils/url'; + class PostPermalinkEditor extends Component { - constructor( { permalinkParts } ) { + constructor( { permalinkParts, slug } ) { super( ...arguments ); this.state = { - editedPostName: permalinkParts.postName, + editedPostName: slug || permalinkParts.postName, }; this.onSavePermalink = this.onSavePermalink.bind( this ); } onSavePermalink( event ) { - const postName = this.state.editedPostName.replace( /\s+/g, '-' ); + const postName = cleanForSlug( this.state.editedPostName ); event.preventDefault(); diff --git a/packages/editor/src/components/post-permalink/index.js b/packages/editor/src/components/post-permalink/index.js index a7364322d4c7df..767f2507f77172 100644 --- a/packages/editor/src/components/post-permalink/index.js +++ b/packages/editor/src/components/post-permalink/index.js @@ -17,7 +17,7 @@ import { safeDecodeURI } from '@wordpress/url'; * Internal Dependencies */ import PostPermalinkEditor from './editor.js'; -import { getWPAdminURL } from '../../utils/url'; +import { getWPAdminURL, cleanForSlug } from '../../utils/url'; class PostPermalink extends Component { constructor() { @@ -57,14 +57,19 @@ class PostPermalink extends Component { } render() { - const { isNew, postLink, isEditable, samplePermalink, isPublished } = this.props; - const { isCopied, isEditingPermalink } = this.state; - const ariaLabel = isCopied ? __( 'Permalink copied' ) : __( 'Copy the permalink' ); + const { isNew, postLink, permalinkParts, postSlug, postTitle, postID, isEditable, isPublished } = this.props; if ( isNew || ! postLink ) { return null; } + const { isCopied, isEditingPermalink } = this.state; + const ariaLabel = isCopied ? __( 'Permalink copied' ) : __( 'Copy the permalink' ); + + const { prefix, suffix } = permalinkParts; + const slug = postSlug || cleanForSlug( postTitle ) || postID; + const samplePermalink = ( isEditable ) ? prefix + slug + suffix : prefix; + return (

this.setState( { isEditingPermalink: false } ) } /> } @@ -128,18 +134,22 @@ export default compose( [ isEditedPostNew, isPermalinkEditable, getCurrentPost, - getPermalink, + getPermalinkParts, + getEditedPostAttribute, isCurrentPostPublished, } = select( 'core/editor' ); - const { link } = getCurrentPost(); + const { id, link } = getCurrentPost(); return { isNew: isEditedPostNew(), postLink: link, + permalinkParts: getPermalinkParts(), + postSlug: getEditedPostAttribute( 'slug' ), isEditable: isPermalinkEditable(), - samplePermalink: getPermalink(), isPublished: isCurrentPostPublished(), + postTitle: getEditedPostAttribute( 'title' ), + postID: id, }; } ), withDispatch( ( dispatch ) => { diff --git a/packages/editor/src/utils/index.js b/packages/editor/src/utils/index.js index 3a49f66b86c902..0f246c16e4aec8 100644 --- a/packages/editor/src/utils/index.js +++ b/packages/editor/src/utils/index.js @@ -4,3 +4,4 @@ import mediaUpload from './media-upload'; export { mediaUpload }; +export { cleanForSlug } from './url.js'; diff --git a/packages/editor/src/utils/test/url.js b/packages/editor/src/utils/test/url.js new file mode 100644 index 00000000000000..9aca0fc8503e50 --- /dev/null +++ b/packages/editor/src/utils/test/url.js @@ -0,0 +1,10 @@ +/** + * Internal dependencies + */ +import { cleanForSlug } from '../url'; + +describe( 'cleanForSlug()', () => { + it( 'Should return string prepared for use as url slug', () => { + expect( cleanForSlug( ' /Déjà_vu. ' ) ).toBe( 'deja-vu' ); + } ); +} ); diff --git a/packages/editor/src/utils/url.js b/packages/editor/src/utils/url.js index 566775289f9777..371cdedc93ff45 100644 --- a/packages/editor/src/utils/url.js +++ b/packages/editor/src/utils/url.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import { deburr, toLower, trim } from 'lodash'; + /** * WordPress dependencies */ @@ -16,3 +21,23 @@ import { addQueryArgs } from '@wordpress/url'; export function getWPAdminURL( page, query ) { return addQueryArgs( page, query ); } + +/** + * Performs some basic cleanup of a string for use as a post slug + * + * This replicates some of what santize_title() does in WordPress core, but + * is only designed to approximate what the slug will be. + * + * Converts whitespace, periods, forward slashes and underscores to hyphens. + * Converts Latin-1 Supplement and Latin Extended-A letters to basic Latin + * letters. Removes combining diacritical marks. Converts remaining string + * to lowercase. It does not touch octets, HTML entities, or other encoded + * characters. + * + * @param {string} string Title or slug to be processed + * + * @return {string} Processed string + */ +export function cleanForSlug( string ) { + return toLower( deburr( trim( string.replace( /[\s\./_]+/g, '-' ), '-' ) ) ); +}