Skip to content

Commit

Permalink
Editor: Update post URL component (#60632)
Browse files Browse the repository at this point in the history
Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
Co-authored-by: jameskoster <jameskoster@git.wordpress.org>
Co-authored-by: jorgefilipecosta <jorgefilipecosta@git.wordpress.org>
Co-authored-by: jasmussen <joen@git.wordpress.org>
  • Loading branch information
5 people authored Apr 11, 2024
1 parent b92efc1 commit faf15b5
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 117 deletions.
232 changes: 131 additions & 101 deletions packages/editor/src/components/post-url/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,122 +6,152 @@ import { safeDecodeURIComponent, cleanForSlug } from '@wordpress/url';
import { useState } from '@wordpress/element';
import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import { TextControl, ExternalLink } from '@wordpress/components';
import {
ExternalLink,
Button,
__experimentalInputControl as InputControl,
__experimentalInputControlPrefixWrapper as InputControlPrefixWrapper,
__experimentalVStack as VStack,
} from '@wordpress/components';
import { store as noticesStore } from '@wordpress/notices';
import { copySmall } from '@wordpress/icons';
import { store as coreStore } from '@wordpress/core-data';
import { useCopyToClipboard } from '@wordpress/compose';

/**
* Internal dependencies
*/
import { usePostURLLabel } from './label';
import { store as editorStore } from '../../store';

export default function PostURL( { onClose } ) {
const {
isEditable,
postSlug,
viewPostLabel,
postLink,
permalinkPrefix,
permalinkSuffix,
} = useSelect( ( select ) => {
const post = select( editorStore ).getCurrentPost();
const postTypeSlug = select( editorStore ).getCurrentPostType();
const postType = select( coreStore ).getPostType( postTypeSlug );
const permalinkParts = select( editorStore ).getPermalinkParts();
const hasPublishAction = post?._links?.[ 'wp:action-publish' ] ?? false;

return {
isEditable:
select( editorStore ).isPermalinkEditable() && hasPublishAction,
postSlug: safeDecodeURIComponent(
select( editorStore ).getEditedPostSlug()
),
viewPostLabel: postType?.labels.view_item,
postLink: post.link,
permalinkPrefix: permalinkParts?.prefix,
permalinkSuffix: permalinkParts?.suffix,
};
}, [] );
const { isEditable, postSlug, postLink, permalinkPrefix, permalinkSuffix } =
useSelect( ( select ) => {
const post = select( editorStore ).getCurrentPost();
const postTypeSlug = select( editorStore ).getCurrentPostType();
const postType = select( coreStore ).getPostType( postTypeSlug );
const permalinkParts = select( editorStore ).getPermalinkParts();
const hasPublishAction =
post?._links?.[ 'wp:action-publish' ] ?? false;

return {
isEditable:
select( editorStore ).isPermalinkEditable() &&
hasPublishAction,
postSlug: safeDecodeURIComponent(
select( editorStore ).getEditedPostSlug()
),
viewPostLabel: postType?.labels.view_item,
postLink: post.link,
permalinkPrefix: permalinkParts?.prefix,
permalinkSuffix: permalinkParts?.suffix,
};
}, [] );
const { editPost } = useDispatch( editorStore );

const { createNotice } = useDispatch( noticesStore );
const [ forceEmptyField, setForceEmptyField ] = useState( false );

const postUrlLabel = usePostURLLabel();
const copyButtonRef = useCopyToClipboard( postUrlLabel, () => {
createNotice( 'info', __( 'Copied URL to clipboard.' ), {
isDismissible: true,
type: 'snackbar',
} );
} );
return (
<div className="editor-post-url">
<InspectorPopoverHeader title={ __( 'URL' ) } onClose={ onClose } />
{ isEditable && (
<TextControl
__nextHasNoMarginBottom
label={ __( 'Permalink' ) }
value={ forceEmptyField ? '' : postSlug }
autoComplete="off"
spellCheck="false"
help={
<>
{ __( 'The last part of the URL.' ) }{ ' ' }
<ExternalLink
href={ __(
'https://wordpress.org/documentation/article/page-post-settings-sidebar/#permalink'
) }
>
{ __( 'Learn more.' ) }
</ExternalLink>
</>
}
onChange={ ( newValue ) => {
editPost( { slug: newValue } );
// When we delete the field the permalink gets
// reverted to the original value.
// The forceEmptyField logic allows the user to have
// the field temporarily empty while typing.
if ( ! newValue ) {
if ( ! forceEmptyField ) {
setForceEmptyField( true );
<InspectorPopoverHeader
title={ __( 'Link' ) }
onClose={ onClose }
/>
<VStack spacing={ 3 }>
{ isEditable && (
<div>
{ __( 'Customize the last part of the URL. ' ) }
<ExternalLink
href={ __(
'https://wordpress.org/documentation/article/page-post-settings-sidebar/#permalink'
) }
>
{ __( 'Learn more.' ) }
</ExternalLink>
</div>
) }
<div>
{ isEditable && (
<InputControl
__next40pxDefaultSize
prefix={
<InputControlPrefixWrapper>
/
</InputControlPrefixWrapper>
}
return;
}
if ( forceEmptyField ) {
setForceEmptyField( false );
}
} }
onBlur={ ( event ) => {
editPost( {
slug: cleanForSlug( event.target.value ),
} );
if ( forceEmptyField ) {
setForceEmptyField( false );
}
} }
/>
) }
{ isEditable && (
<h3 className="editor-post-url__link-label">
{ viewPostLabel ?? __( 'View post' ) }
</h3>
) }
<p>
<ExternalLink
className="editor-post-url__link"
href={ postLink }
target="_blank"
>
{ isEditable ? (
<>
<span className="editor-post-url__link-prefix">
{ permalinkPrefix }
</span>
<span className="editor-post-url__link-slug">
{ postSlug }
</span>
<span className="editor-post-url__link-suffix">
{ permalinkSuffix }
</span>
</>
) : (
postLink
suffix={
<Button
icon={ copySmall }
ref={ copyButtonRef }
/>
}
label={ __( 'Link' ) }
hideLabelFromVision
value={ forceEmptyField ? '' : postSlug }
autoComplete="off"
spellCheck="false"
type="text"
className="editor-post-url__input"
onChange={ ( newValue ) => {
editPost( { slug: newValue } );
// When we delete the field the permalink gets
// reverted to the original value.
// The forceEmptyField logic allows the user to have
// the field temporarily empty while typing.
if ( ! newValue ) {
if ( ! forceEmptyField ) {
setForceEmptyField( true );
}
return;
}
if ( forceEmptyField ) {
setForceEmptyField( false );
}
} }
onBlur={ ( event ) => {
editPost( {
slug: cleanForSlug( event.target.value ),
} );
if ( forceEmptyField ) {
setForceEmptyField( false );
}
} }
help={
<ExternalLink
className="editor-post-url__link"
href={ postLink }
target="_blank"
>
<span className="editor-post-url__link-prefix">
{ permalinkPrefix }
</span>
<span className="editor-post-url__link-slug">
{ postSlug }
</span>
<span className="editor-post-url__link-suffix">
{ permalinkSuffix }
</span>
</ExternalLink>
}
/>
) }
{ ! isEditable && (
<ExternalLink
className="editor-post-url__link"
href={ postLink }
target="_blank"
>
{ postLink }
</ExternalLink>
) }
</ExternalLink>
</p>
</div>
</VStack>
</div>
);
}
27 changes: 20 additions & 7 deletions packages/editor/src/components/post-url/panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,39 @@
* WordPress dependencies
*/
import { useMemo, useState } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { Dropdown, Button } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { safeDecodeURIComponent } from '@wordpress/url';

/**
* Internal dependencies
*/
import PostURLCheck from './check';
import PostURL from './index';
import { usePostURLLabel } from './label';
import PostPanelRow from '../post-panel-row';
import { store as editorStore } from '../../store';

export default function PostURLPanel() {
// Use internal state instead of a ref to make sure that the component
// re-renders when the popover's anchor updates.
const [ popoverAnchor, setPopoverAnchor ] = useState( null );
// Memoize popoverProps to avoid returning a new object every time.
const popoverProps = useMemo(
() => ( { anchor: popoverAnchor, placement: 'bottom-end' } ),
() => ( {
// Anchor the popover to the middle of the entire row so that it doesn't
// move around when the label changes.
anchor: popoverAnchor,
placement: 'left-start',
offset: 36,
shift: true,
} ),
[ popoverAnchor ]
);

return (
<PostURLCheck>
<PostPanelRow label={ __( 'URL' ) } ref={ setPopoverAnchor }>
<PostPanelRow label={ __( 'Link' ) } ref={ setPopoverAnchor }>
<Dropdown
popoverProps={ popoverProps }
className="editor-post-url__panel-dropdown"
Expand All @@ -44,18 +53,22 @@ export default function PostURLPanel() {
}

function PostURLToggle( { isOpen, onClick } ) {
const label = usePostURLLabel();
const slug = useSelect(
( select ) => select( editorStore ).getEditedPostSlug(),
[]
);
const decodedSlug = safeDecodeURIComponent( slug );
return (
<Button
__next40pxDefaultSize
className="editor-post-url__panel-toggle"
variant="tertiary"
aria-expanded={ isOpen }
// translators: %s: Current post URL.
aria-label={ sprintf( __( 'Change URL: %s' ), label ) }
// translators: %s: Current post link.
aria-label={ sprintf( __( 'Change link: %s' ), decodedSlug ) }
onClick={ onClick }
>
{ label }
{ decodedSlug }
</Button>
);
}
14 changes: 8 additions & 6 deletions packages/editor/src/components/post-url/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@
margin: $grid-unit-10;
}

.editor-post-url__link-label {
font-size: $default-font-size;
font-weight: 400;
margin: 0;
}

/* rtl:begin:ignore */
.editor-post-url__link {
direction: ltr;
word-break: break-word;
margin-top: $grid-unit-05;
color: $gray-700;
}
/* rtl:end:ignore */

.editor-post-url__link-slug {
font-weight: 600;
}

// TODO: This might indicate a need to update the InputControl itself, as
// there is no way currently to control the padding when adding prefix/suffix.
.editor-post-url__input input.components-input-control__input {
padding-inline-start: 0 !important;
}
6 changes: 3 additions & 3 deletions test/e2e/specs/editor/various/sidebar-permalink.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test.describe( 'Sidebar Permalink', () => {
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'aaaa (Updated)' );
await expect(
page.getByRole( 'button', { name: 'Change URL' } )
page.getByRole( 'button', { name: 'Change link' } )
).toBeHidden();
} );

Expand All @@ -54,7 +54,7 @@ test.describe( 'Sidebar Permalink', () => {
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'aaaa (Updated)' );
await expect(
page.getByRole( 'button', { name: 'Change URL' } )
page.getByRole( 'button', { name: 'Change link' } )
).toBeHidden();
} );

Expand All @@ -75,7 +75,7 @@ test.describe( 'Sidebar Permalink', () => {
.getByRole( 'textbox', { name: 'Add title' } )
.fill( 'aaaa (Updated)' );
await expect(
page.getByRole( 'button', { name: 'Change URL' } )
page.getByRole( 'button', { name: 'Change link' } )
).toBeVisible();
} );
} );

0 comments on commit faf15b5

Please sign in to comment.