Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try/publish in site editor #51408

Merged
merged 41 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
7213d66
add required inputs
SaxonF Jun 9, 2023
c938235
schedule date
SaxonF Jun 12, 2023
e46f5ff
add password and fix a few things
SaxonF Jun 12, 2023
511bacc
re-add status modal
SaxonF Jun 13, 2023
73f89b4
update change status modal
SaxonF Jun 13, 2023
d5013da
revert in sidebar status change
SaxonF Jun 13, 2023
fc692d0
refine a few status details
SaxonF Jun 13, 2023
0d25392
fix merge issues
SaxonF Jun 14, 2023
42145f0
fix status label
SaxonF Jun 14, 2023
736fac9
use null for status date
SaxonF Jun 14, 2023
46f7ad5
handle empty date
SaxonF Jun 15, 2023
2df2fc9
change to add fields instead of modal
SaxonF Jun 15, 2023
710e36c
default publish not draft
SaxonF Jun 15, 2023
01b0a07
remove style import
SaxonF Jun 15, 2023
7dec51c
add required inputs
SaxonF Jun 9, 2023
368cdef
schedule date
SaxonF Jun 12, 2023
12554e3
add password and fix a few things
SaxonF Jun 12, 2023
01f58b3
re-add status modal
SaxonF Jun 13, 2023
94031eb
update change status modal
SaxonF Jun 13, 2023
6582538
revert in sidebar status change
SaxonF Jun 13, 2023
2c05b56
refine a few status details
SaxonF Jun 13, 2023
41e15e9
fix merge issues
SaxonF Jun 14, 2023
fc295e0
fix status label
SaxonF Jun 14, 2023
96ba33f
use null for status date
SaxonF Jun 14, 2023
1fa27ab
handle empty date
SaxonF Jun 15, 2023
14e2254
change to add fields instead of modal
SaxonF Jun 15, 2023
053c3cf
default publish not draft
SaxonF Jun 15, 2023
a3949c2
remove style import
SaxonF Jun 15, 2023
2f088b9
Merge branch 'try/publish-in-site-editor' of https://github.com/WordP…
SaxonF Jun 16, 2023
cfbb607
Merge branch 'trunk' into try/publish-in-site-editor
SaxonF Jun 16, 2023
3e51dff
remove css
SaxonF Jun 16, 2023
d07a143
only autofocus password field if empty
SaxonF Jun 19, 2023
ff96c4c
aria label on status popover
SaxonF Jun 20, 2023
9e28a27
remove end line
SaxonF Jun 20, 2023
2a13222
Merge branch 'trunk' into try/publish-in-site-editor
SaxonF Jun 20, 2023
ca88703
create radio with help component
SaxonF Jun 20, 2023
8dbad15
Merge branch 'trunk' into try/publish-in-site-editor
SaxonF Jun 20, 2023
d0b09cc
adjust to use radiocontrol and hide password
SaxonF Jun 20, 2023
d4d7fde
radio label alignment
SaxonF Jun 20, 2023
7cf6f3c
add form wrapper
SaxonF Jun 20, 2023
6c55aa1
aria label
SaxonF Jun 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/**
* WordPress dependencies
*/
import { PanelBody } from '@wordpress/components';
import {
PanelBody,
__experimentalText as Text,
__experimentalVStack as VStack,
} from '@wordpress/components';
import { page as pageIcon } from '@wordpress/icons';
import { __, sprintf } from '@wordpress/i18n';
import { humanTimeDiff } from '@wordpress/date';
Expand All @@ -15,25 +19,32 @@ import { decodeEntities } from '@wordpress/html-entities';
import { store as editSiteStore } from '../../../store';
import SidebarCard from '../sidebar-card';
import PageContent from './page-content';
import PageSummary from './page-summary';
import EditTemplate from './edit-template';

export default function PagePanels() {
const { hasResolved, title, modified } = useSelect( ( select ) => {
const { getEditedPostContext } = select( editSiteStore );
const { getEditedEntityRecord, hasFinishedResolution } =
select( coreStore );
const context = getEditedPostContext();
const queryArgs = [ 'postType', context.postType, context.postId ];
const page = getEditedEntityRecord( ...queryArgs );
return {
hasResolved: hasFinishedResolution(
'getEditedEntityRecord',
queryArgs
),
title: page?.title,
modified: page?.modified,
};
}, [] );
const { id, type, hasResolved, status, date, password, title, modified } =
useSelect( ( select ) => {
const { getEditedPostContext } = select( editSiteStore );
const { getEditedEntityRecord, hasFinishedResolution } =
select( coreStore );
const context = getEditedPostContext();
const queryArgs = [ 'postType', context.postType, context.postId ];
const page = getEditedEntityRecord( ...queryArgs );
return {
hasResolved: hasFinishedResolution(
'getEditedEntityRecord',
queryArgs
),
title: page?.title,
id: page?.id,
type: page?.type,
status: page?.status,
date: page?.date,
password: page?.password,
modified: page?.modified,
};
}, [] );

if ( ! hasResolved ) {
return null;
Expand All @@ -45,11 +56,26 @@ export default function PagePanels() {
<SidebarCard
title={ decodeEntities( title ) }
icon={ pageIcon }
description={ sprintf(
// translators: %s: Human-readable time difference, e.g. "2 days ago".
__( 'Last edited %s' ),
humanTimeDiff( modified )
) }
description={
<VStack>
<Text>
{ sprintf(
// translators: %s: Human-readable time difference, e.g. "2 days ago".
__( 'Last edited %s' ),
humanTimeDiff( modified )
) }
</Text>
</VStack>
}
/>
</PanelBody>
<PanelBody title={ __( 'Summary' ) }>
<PageSummary
status={ status }
date={ date }
password={ password }
postId={ id }
postType={ type }
/>
</PanelBody>
<PanelBody title={ __( 'Content' ) }>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/**
* WordPress dependencies
*/
import {
Button,
BaseControl,
ToggleControl,
Dropdown,
__experimentalText as Text,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
TextControl,
RadioControl,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useDispatch } from '@wordpress/data';
import { useState, useMemo } from '@wordpress/element';
import { store as coreStore } from '@wordpress/core-data';
import { store as noticesStore } from '@wordpress/notices';
import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import StatusLabel from '../../sidebar-navigation-screen-page/status-label';

const STATUS_OPTIONS = [
{
label: (
<>
{ __( 'Draft' ) }
<Text variant="muted">{ __( 'Not ready to publish.' ) }</Text>
</>
),
value: 'draft',
},
{
label: (
<>
{ __( 'Pending' ) }
<Text variant="muted">
{ __( 'Waiting for review before publishing.' ) }
</Text>
</>
),
value: 'pending',
},
{
label: (
<>
{ __( 'Private' ) }
<Text variant="muted">
{ __( 'Only visible to site admins and editors.' ) }
</Text>
</>
),
value: 'private',
},
{
label: (
<>
{ __( 'Scheduled' ) }
<Text variant="muted">
{ __( 'Publish automatically on a chosen date.' ) }
</Text>
</>
),
value: 'future',
},
{
label: (
<>
{ __( 'Published' ) }
<Text variant="muted">{ __( 'Visible to everyone.' ) }</Text>
</>
),
value: 'publish',
},
];

export default function PageStatus( {
postType,
postId,
status,
password,
date,
} ) {
const [ showPassword, setShowPassword ] = useState( !! password );

const { editEntityRecord } = useDispatch( coreStore );
const { createErrorNotice } = useDispatch( noticesStore );

const [ popoverAnchor, setPopoverAnchor ] = useState( null );
// Memoize popoverProps to avoid returning a new object every time.
const popoverProps = useMemo(
() => ( {
// Anchor the popover to the middle of the entire row so that it doesn't
// move around when the label changes.
anchor: popoverAnchor,
'aria-label': __( 'Change status' ),
placement: 'bottom-end',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The popover doesn't have a descriptive name for screen readers, users might not even know that they land in a popover. Maybe we should add aria-label or aria-labelledby here.

The other thing is that Popover traps the focus but it doesn't set the dialog role. This could be a bigger problem in general of the component design and I personally think it's an accessibility bug. We could set a role: 'dialog' here but I'm not sure if it's the right move. Possibly better to consult an accessibility expert on this.

It's still usable thought just a bit confusing so I don't think this should be blocking 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added an aria-label for the time being since I don't want to modify InspectorPopoverHeader or PublishDateTimePicker as part of this PR to add ID to title.

The other thing is that Popover traps the focus but it doesn't set the dialog role. This could be a bigger problem in general of the component design and I personally think it's an accessibility bug. We could set a role: 'dialog' here but I'm not sure if it's the right move. Possibly better to consult an accessibility expert on this.

Let's create a separate issue referencing the Dropdown and Popover components in wordpress/components to review any accessibility concerns. Modal has a title prop that uses aria-labelledby.

} ),
[ popoverAnchor ]
);

const saveStatus = async ( {
status: newStatus = status,
password: newPassword = password,
date: newDate = date,
} ) => {
try {
await editEntityRecord( 'postType', postType, postId, {
status: newStatus,
date: newDate,
password: newPassword,
} );
} catch ( error ) {
const errorMessage =
error.message && error.code !== 'unknown_error'
? error.message
: __( 'An error occurred while updating the status' );

createErrorNotice( errorMessage, {
type: 'snackbar',
} );
}
};

const handleTogglePassword = ( value ) => {
setShowPassword( value );
if ( ! value ) {
saveStatus( { password: '' } );
}
};

const handleStatus = ( value ) => {
let newDate = date;
let newPassword = password;
if ( value === 'publish' ) {
if ( new Date( date ) > new Date() ) {
newDate = null;
}
} else if ( value === 'future' ) {
if ( ! date || new Date( date ) < new Date() ) {
newDate = new Date();
newDate.setDate( newDate.getDate() + 7 );
}
} else if ( value === 'private' && password ) {
setShowPassword( false );
newPassword = '';
}
saveStatus( {
status: value,
date: newDate,
password: newPassword,
} );
};

return (
<HStack className="edit-site-summary-field">
<Text className="edit-site-summary-field__label">
{ __( 'Status' ) }
</Text>
<Dropdown
contentClassName="edit-site-change-status__content"
popoverProps={ popoverProps }
focusOnMount
ref={ setPopoverAnchor }
renderToggle={ ( { onToggle } ) => (
<Button
className="edit-site-summary-field__trigger"
variant="tertiary"
onClick={ onToggle }
>
<StatusLabel
status={ password ? 'protected' : status }
/>
</Button>
) }
renderContent={ ( { onClose } ) => (
<>
<InspectorPopoverHeader
title={ __( 'Status' ) }
onClose={ onClose }
/>
<form>
<VStack spacing={ 5 }>
<RadioControl
className="edit-site-change-status__options"
hideLabelFromVision
label={ __( 'Status' ) }
options={ STATUS_OPTIONS }
onChange={ handleStatus }
selected={ status }
/>
{ status !== 'private' && (
<BaseControl
id={ `edit-site-change-status__password` }
label={ __( 'Password' ) }
>
<ToggleControl
label={ __(
'Hide this page behind a password'
) }
checked={ showPassword }
onChange={ handleTogglePassword }
/>
{ showPassword && (
<TextControl
onChange={ ( value ) =>
saveStatus( {
password: value,
} )
}
value={ password }
/* eslint-disable jsx-a11y/no-autofocus */
autoFocus={ ! password }
/* eslint-enable jsx-a11y/no-autofocus */
placeholder={ __(
'Enter a secure password'
) }
type="password"
/>
) }
</BaseControl>
) }
</VStack>
</form>
</>
) }
/>
</HStack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* WordPress dependencies
*/
import { __experimentalVStack as VStack } from '@wordpress/components';
/**
* Internal dependencies
*/
import PageStatus from './page-status';
import PublishDate from './publish-date';

export default function PageSummary( {
status,
date,
password,
postId,
postType,
} ) {
return (
<VStack>
<PageStatus
status={ status }
date={ date }
password={ password }
postId={ postId }
postType={ postType }
/>
<PublishDate
status={ status }
date={ date }
postId={ postId }
postType={ postType }
/>
</VStack>
);
}
Loading