fix(ui): unsaved changes allows for scheduled publish missing changes (#11001)
### What? When you first edit a document and then open the Schedule publish drawer, you can schedule publish changes but the current changes made to the form won't be included. ### Why? The UX does not make it clear that the changes you have in the form are not actually going to be published. ### How? Instead of allowing that we just disable the Schedule Publish drawer toggler so that users are forced to save a draft first. In addition to the above, this change also passes a defaultType so that an already published document will default the radio type have "Unpublish" selected.
This commit is contained in:
@@ -185,6 +185,7 @@ export const Button: React.FC<Props> = (props) => {
|
||||
className={disabled && !enableSubMenu ? `${baseClass}--popup-disabled` : ''}
|
||||
disabled={disabled && !enableSubMenu}
|
||||
horizontalAlign="right"
|
||||
id={`${id}-popup`}
|
||||
noBackground
|
||||
render={({ close }) => SubMenuPopupContent({ close: () => close() })}
|
||||
size="large"
|
||||
|
||||
@@ -19,6 +19,7 @@ const baseClass = 'date-time-picker'
|
||||
|
||||
const DatePicker: React.FC<Props> = (props) => {
|
||||
const {
|
||||
id,
|
||||
displayFormat: customDisplayFormat,
|
||||
maxDate,
|
||||
maxTime,
|
||||
@@ -113,7 +114,7 @@ const DatePicker: React.FC<Props> = (props) => {
|
||||
}, [i18n.language, i18n.dateFNS])
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className={classes} id={id}>
|
||||
<div className={`${baseClass}__icon-wrap`}>
|
||||
{dateTimePickerProps.selected && (
|
||||
<button
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { DayPickerProps, SharedProps, TimePickerProps } from 'payload'
|
||||
|
||||
export type Props = {
|
||||
id?: string
|
||||
onChange?: (val: Date) => void
|
||||
placeholder?: string
|
||||
readOnly?: boolean
|
||||
|
||||
@@ -25,6 +25,7 @@ export type PopupProps = {
|
||||
disabled?: boolean
|
||||
forceOpen?: boolean
|
||||
horizontalAlign?: 'center' | 'left' | 'right'
|
||||
id?: string
|
||||
initActive?: boolean
|
||||
noBackground?: boolean
|
||||
onToggleOpen?: (active: boolean) => void
|
||||
@@ -37,6 +38,7 @@ export type PopupProps = {
|
||||
|
||||
export const Popup: React.FC<PopupProps> = (props) => {
|
||||
const {
|
||||
id,
|
||||
boundingRef,
|
||||
button,
|
||||
buttonClassName,
|
||||
@@ -168,7 +170,7 @@ export const Popup: React.FC<PopupProps> = (props) => {
|
||||
.join(' ')
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className={classes} id={id}>
|
||||
<div className={`${baseClass}__trigger-wrap`}>
|
||||
{showOnHover ? (
|
||||
<div
|
||||
|
||||
@@ -36,6 +36,7 @@ import { buildUpcomingColumns } from './buildUpcomingColumns.js'
|
||||
const baseClass = 'schedule-publish'
|
||||
|
||||
type Props = {
|
||||
defaultType?: PublishType
|
||||
slug: string
|
||||
}
|
||||
|
||||
@@ -44,7 +45,7 @@ const defaultLocaleOption = {
|
||||
value: 'all',
|
||||
}
|
||||
|
||||
export const ScheduleDrawer: React.FC<Props> = ({ slug }) => {
|
||||
export const ScheduleDrawer: React.FC<Props> = ({ slug, defaultType }) => {
|
||||
const { toggleModal } = useModal()
|
||||
const {
|
||||
config: {
|
||||
@@ -60,7 +61,7 @@ export const ScheduleDrawer: React.FC<Props> = ({ slug }) => {
|
||||
const { id, collectionSlug, globalSlug, title } = useDocumentInfo()
|
||||
const { i18n, t } = useTranslation()
|
||||
const { schedulePublish } = useServerFunctions()
|
||||
const [type, setType] = React.useState<PublishType>('publish')
|
||||
const [type, setType] = React.useState<PublishType>(defaultType || 'publish')
|
||||
const [date, setDate] = React.useState<Date>()
|
||||
const [timezone, setTimezone] = React.useState<string>(defaultTimezone)
|
||||
const [locale, setLocale] = React.useState<{ label: string; value: string }>(defaultLocaleOption)
|
||||
@@ -313,8 +314,9 @@ export const ScheduleDrawer: React.FC<Props> = ({ slug }) => {
|
||||
</li>
|
||||
</ul>
|
||||
<br />
|
||||
<FieldLabel label={t('general:time')} required />
|
||||
<FieldLabel label={t('general:time')} path={'time'} required />
|
||||
<DatePickerField
|
||||
id="time"
|
||||
minDate={new Date()}
|
||||
onChange={(e) => onChangeDate(e)}
|
||||
pickerAppearance="dayAndTime"
|
||||
@@ -343,7 +345,13 @@ export const ScheduleDrawer: React.FC<Props> = ({ slug }) => {
|
||||
</React.Fragment>
|
||||
)}
|
||||
<div className={`${baseClass}__actions`}>
|
||||
<Button buttonStyle="primary" disabled={processing} onClick={handleSave} type="button">
|
||||
<Button
|
||||
buttonStyle="primary"
|
||||
disabled={processing}
|
||||
id="scheduled-publish-save"
|
||||
onClick={handleSave}
|
||||
type="button"
|
||||
>
|
||||
{t('general:save')}
|
||||
</Button>
|
||||
{processing ? <span>{t('general:saving')}</span> : null}
|
||||
|
||||
@@ -73,7 +73,10 @@ export function PublishButton({ label: labelProp }: PublishButtonClientProps) {
|
||||
entityConfig?.versions?.drafts.schedulePublish
|
||||
|
||||
const canSchedulePublish = Boolean(
|
||||
scheduledPublishEnabled && hasPublishPermission && (globalSlug || (collectionSlug && id)),
|
||||
scheduledPublishEnabled &&
|
||||
hasPublishPermission &&
|
||||
(globalSlug || (collectionSlug && id)) &&
|
||||
!modified,
|
||||
)
|
||||
|
||||
const operation = useOperation()
|
||||
@@ -209,7 +212,10 @@ export function PublishButton({ label: labelProp }: PublishButtonClientProps) {
|
||||
<React.Fragment>
|
||||
{canSchedulePublish && (
|
||||
<PopupList.ButtonGroup key="schedule-publish">
|
||||
<PopupList.Button onClick={() => [toggleModal(drawerSlug), close()]}>
|
||||
<PopupList.Button
|
||||
id="schedule-publish"
|
||||
onClick={() => [toggleModal(drawerSlug), close()]}
|
||||
>
|
||||
{t('version:schedulePublish')}
|
||||
</PopupList.Button>
|
||||
</PopupList.ButtonGroup>
|
||||
@@ -230,7 +236,12 @@ export function PublishButton({ label: labelProp }: PublishButtonClientProps) {
|
||||
>
|
||||
{localization ? defaultLabel : label}
|
||||
</FormSubmit>
|
||||
{canSchedulePublish && isModalOpen(drawerSlug) && <ScheduleDrawer slug={drawerSlug} />}
|
||||
{canSchedulePublish && isModalOpen(drawerSlug) && (
|
||||
<ScheduleDrawer
|
||||
defaultType={!hasNewerVersions ? 'unpublish' : 'publish'}
|
||||
slug={drawerSlug}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -743,6 +743,49 @@ describe('Versions', () => {
|
||||
expect(versionsTabUpdated).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Scheduled publish', () => {
|
||||
beforeAll(() => {
|
||||
url = new AdminUrlUtil(serverURL, draftCollectionSlug)
|
||||
})
|
||||
|
||||
test('should schedule publish', async () => {
|
||||
await page.goto(url.create)
|
||||
await page.waitForURL(url.create)
|
||||
await page.locator('#field-title').fill('scheduled publish')
|
||||
await page.locator('#field-description').fill('scheduled publish description')
|
||||
|
||||
// schedule publish should not be available before document has been saved
|
||||
await page.locator('#action-save-popup').click()
|
||||
await expect(page.locator('#schedule-publish')).not.toBeVisible()
|
||||
|
||||
// save draft then try to schedule publish
|
||||
await saveDocAndAssert(page)
|
||||
await page.locator('#action-save-popup').click()
|
||||
await page.locator('#schedule-publish').click()
|
||||
|
||||
// drawer should open
|
||||
await expect(page.locator('.schedule-publish__drawer-header')).toBeVisible()
|
||||
// nothing in scheduled
|
||||
await expect(page.locator('.drawer__content')).toContainText('No upcoming events scheduled.')
|
||||
|
||||
// set date and time
|
||||
await page.locator('.date-time-picker input').fill('Feb 21, 2050 12:00 AM')
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// save the scheduled publish
|
||||
await page.locator('#scheduled-publish-save').click()
|
||||
|
||||
// delete the scheduled event after it was made
|
||||
await page.locator('.cell-delete').locator('.btn').click()
|
||||
|
||||
// see toast deleted successfully
|
||||
await expect(
|
||||
page.locator('.payload-toast-item:has-text("Deleted successfully.")'),
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Collections - publish specific locale', () => {
|
||||
beforeAll(() => {
|
||||
url = new AdminUrlUtil(serverURL, localizedCollectionSlug)
|
||||
|
||||
Reference in New Issue
Block a user