-
Notifications
You must be signed in to change notification settings - Fork 427
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
feat(releases): adding correct confirmation dialog to archive release flow #7827
Changes from all commits
14c8d7b
ca8361b
2e5a153
2459a6f
41196cf
d419899
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {type ReleaseDocument} from '../store/types' | ||
|
||
export const activeScheduledRelease: ReleaseDocument = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking that we'd continue to create others here eg There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😚 👌 |
||
_id: '_.releases.activeRelease', | ||
_type: 'system.release', | ||
createdBy: '', | ||
_createdAt: '2023-10-01T08:00:00Z', | ||
_updatedAt: '2023-10-01T09:00:00Z', | ||
state: 'active', | ||
name: 'activeRelease', | ||
metadata: { | ||
title: 'active Release', | ||
releaseType: 'scheduled', | ||
intendedPublishAt: '2023-10-01T10:00:00Z', | ||
description: 'active Release description', | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import {type Mocked, vi} from 'vitest' | ||
|
||
import {type ReleaseOperationsStore} from '../../createReleaseOperationStore' | ||
|
||
export const useReleaseOperationsMock: Mocked<ReleaseOperationsStore> = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like a great pattern to me! |
||
archive: vi.fn(), | ||
unarchive: vi.fn(), | ||
createRelease: vi.fn(), | ||
createVersion: vi.fn(), | ||
discardVersion: vi.fn(), | ||
publishRelease: vi.fn(), | ||
schedule: vi.fn(), | ||
unschedule: vi.fn(), | ||
updateRelease: vi.fn(), | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,16 @@ | ||
import {ArchiveIcon, ArrowRightIcon, EllipsisHorizontalIcon, UnarchiveIcon} from '@sanity/icons' | ||
import {ArchiveIcon, EllipsisHorizontalIcon, UnarchiveIcon} from '@sanity/icons' | ||
import {useTelemetry} from '@sanity/telemetry/react' | ||
import {Box, Flex, Menu, Spinner, Text} from '@sanity/ui' | ||
import {type FormEventHandler, useState} from 'react' | ||
import {Menu, Spinner, Text, useToast} from '@sanity/ui' | ||
import {useCallback, useMemo, useState} from 'react' | ||
|
||
import {Button, Dialog, MenuButton, MenuItem} from '../../../../../ui-components' | ||
import {LoadingBlock} from '../../../../components/loadingBlock' | ||
import {useTranslation} from '../../../../i18n' | ||
import {ArchivedRelease, UnarchivedRelease} from '../../../__telemetry__/releases.telemetry' | ||
import {Translate, useTranslation} from '../../../../i18n' | ||
import {ArchivedRelease} from '../../../__telemetry__/releases.telemetry' | ||
import {releasesLocaleNamespace} from '../../../i18n' | ||
import {type ReleaseDocument} from '../../../store/types' | ||
import {useReleaseOperations} from '../../../store/useReleaseOperations' | ||
import {getBundleIdFromReleaseDocumentId} from '../../../util/getBundleIdFromReleaseDocumentId' | ||
import {useBundleDocuments} from '../../detail/useBundleDocuments' | ||
|
||
export type ReleaseMenuButtonProps = { | ||
disabled?: boolean | ||
|
@@ -19,35 +20,125 @@ export type ReleaseMenuButtonProps = { | |
const ARCHIVABLE_STATES = ['active', 'published'] | ||
|
||
export const ReleaseMenuButton = ({disabled, release}: ReleaseMenuButtonProps) => { | ||
const {archive, unarchive} = useReleaseOperations() | ||
const toast = useToast() | ||
const {archive} = useReleaseOperations() | ||
const {loading: isLoadingReleaseDocuments, results: releaseDocuments} = useBundleDocuments( | ||
getBundleIdFromReleaseDocumentId(release._id), | ||
) | ||
const [isPerformingOperation, setIsPerformingOperation] = useState(false) | ||
const [selectedAction, setSelectedAction] = useState<'edit' | 'confirm-archive'>() | ||
|
||
const releaseMenuDisabled = !release || disabled | ||
const releaseMenuDisabled = !release || isLoadingReleaseDocuments || disabled | ||
const {t} = useTranslation(releasesLocaleNamespace) | ||
const telemetry = useTelemetry() | ||
|
||
const handleArchive = async (e: Parameters<FormEventHandler<HTMLFormElement>>[0]) => { | ||
const handleArchive = useCallback(async () => { | ||
if (releaseMenuDisabled) return | ||
e.preventDefault() | ||
|
||
setIsPerformingOperation(true) | ||
await archive(release._id) | ||
try { | ||
setIsPerformingOperation(true) | ||
await archive(release._id) | ||
|
||
// it's in the process of becoming true, so the event we want to track is archive | ||
telemetry.log(ArchivedRelease) | ||
setIsPerformingOperation(false) | ||
} | ||
// it's in the process of becoming true, so the event we want to track is archive | ||
telemetry.log(ArchivedRelease) | ||
toast.push({ | ||
closable: true, | ||
status: 'success', | ||
title: ( | ||
<Text muted size={1}> | ||
<Translate | ||
t={t} | ||
i18nKey="toast.archive.success" | ||
values={{title: release.metadata.title}} | ||
/> | ||
</Text> | ||
), | ||
}) | ||
} catch (archivingError) { | ||
toast.push({ | ||
status: 'error', | ||
title: ( | ||
<Text muted size={1}> | ||
<Translate | ||
t={t} | ||
i18nKey="toast.archive.error" | ||
values={{title: release.metadata.title, error: archivingError.toString()}} | ||
/> | ||
</Text> | ||
), | ||
}) | ||
console.error(archivingError) | ||
} finally { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rogue console log or meant to be here? :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I followed similar approach to what we do in Publish and schedule, so this is intended |
||
setIsPerformingOperation(false) | ||
setSelectedAction(undefined) | ||
} | ||
}, [archive, release._id, release.metadata.title, releaseMenuDisabled, t, telemetry, toast]) | ||
|
||
const handleUnarchive = async () => { | ||
setIsPerformingOperation(true) | ||
await unarchive(release._id) | ||
|
||
// it's in the process of becoming false, so the event we want to track is unarchive | ||
telemetry.log(UnarchivedRelease) | ||
setIsPerformingOperation(false) | ||
// noop | ||
// TODO: similar to handleArchive - complete once server action exists | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the reason for deleting this? Do we expect unarchive to change significantly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will change from what it currently is in |
||
|
||
const confirmArchiveDialog = useMemo(() => { | ||
if (selectedAction !== 'confirm-archive') return null | ||
|
||
const dialogDescription = | ||
releaseDocuments.length === 1 | ||
? 'archive-dialog.confirm-archive-description_one' | ||
jordanl17 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
: 'archive-dialog.confirm-archive-description_other' | ||
|
||
return ( | ||
<Dialog | ||
id="confirm-archive-dialog" | ||
data-testid="confirm-archive-dialog" | ||
header={ | ||
<Translate | ||
t={t} | ||
i18nKey={'archive-dialog.confirm-archive-title'} | ||
values={{ | ||
title: release.metadata.title, | ||
}} | ||
/> | ||
} | ||
onClose={() => setSelectedAction(undefined)} | ||
footer={{ | ||
confirmButton: { | ||
text: t('archive-dialog.confirm-archive-button'), | ||
tone: 'positive', | ||
onClick: handleArchive, | ||
loading: isPerformingOperation, | ||
disabled: isPerformingOperation, | ||
}, | ||
}} | ||
> | ||
<Text muted size={1}> | ||
<Translate | ||
t={t} | ||
i18nKey={dialogDescription} | ||
values={{ | ||
count: releaseDocuments.length, | ||
}} | ||
/> | ||
</Text> | ||
</Dialog> | ||
) | ||
}, [ | ||
handleArchive, | ||
isPerformingOperation, | ||
release.metadata.title, | ||
releaseDocuments.length, | ||
selectedAction, | ||
t, | ||
]) | ||
|
||
const handleOnInitiateArchive = useCallback(() => { | ||
if (releaseDocuments.length > 0) { | ||
setSelectedAction('confirm-archive') | ||
} else { | ||
handleArchive() | ||
} | ||
}, [handleArchive, releaseDocuments.length]) | ||
|
||
return ( | ||
<> | ||
<MenuButton | ||
|
@@ -67,17 +158,19 @@ export const ReleaseMenuButton = ({disabled, release}: ReleaseMenuButtonProps) = | |
{!release?.state || release.state === 'archived' ? ( | ||
<MenuItem | ||
onClick={handleUnarchive} | ||
// TODO: disabled as CL action not yet impl | ||
disabled | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a linear story for this? 👀 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes we do corel-256 |
||
icon={UnarchiveIcon} | ||
text={t('action.unarchive')} | ||
data-testid="unarchive-release" | ||
/> | ||
) : ( | ||
<MenuItem | ||
tooltipProps={{ | ||
disabled: ARCHIVABLE_STATES.includes(release.state), | ||
disabled: ARCHIVABLE_STATES.includes(release.state) || isPerformingOperation, | ||
content: t('action.archive.tooltip'), | ||
}} | ||
onClick={() => setSelectedAction('confirm-archive')} | ||
onClick={handleOnInitiateArchive} | ||
icon={ArchiveIcon} | ||
text={t('action.archive')} | ||
data-testid="archive-release" | ||
|
@@ -94,36 +187,7 @@ export const ReleaseMenuButton = ({disabled, release}: ReleaseMenuButtonProps) = | |
tone: 'default', | ||
}} | ||
/> | ||
{selectedAction === 'confirm-archive' && ( | ||
<Dialog | ||
header="Confirm archiving release" | ||
id="create-release-dialog" | ||
onClose={() => setSelectedAction(undefined)} | ||
width={1} | ||
> | ||
<form onSubmit={handleArchive}> | ||
<Box padding={4}> | ||
{/* TODO localize string */} | ||
{/* eslint-disable-next-line i18next/no-literal-string */} | ||
<Text>Are you sure you want to archive the release? There's no going back (yet)</Text> | ||
{isPerformingOperation && <LoadingBlock showText title={'archiving, wait'} />} | ||
</Box> | ||
<Flex justify="flex-end" paddingTop={5}> | ||
<Button | ||
size="large" | ||
iconRight={ArrowRightIcon} | ||
type="submit" | ||
text={ | ||
// TODO localize string | ||
// eslint-disable-next-line @sanity/i18n/no-attribute-string-literals | ||
'Archive release' | ||
} | ||
data-testid="archive-release-button" | ||
/> | ||
</Flex> | ||
</form> | ||
</Dialog> | ||
)} | ||
{confirmArchiveDialog} | ||
</> | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice nice nice nice