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

feat(releases): archive and unarchive releases #7072

Merged
merged 43 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
808647c
refactor(sanity): update types to bundle
RitaDias Jul 1, 2024
4e3b76a
feat(sanity): add ability to create releases from dialog + update types
RitaDias Jul 2, 2024
932dea8
feat(sanity): update list on global and document to use store
RitaDias Jul 2, 2024
c0f8cf4
feat(sanity): update types, add icon and hue picker to bundles, updat…
RitaDias Jul 2, 2024
dfd5d40
feat(sanity): add date picker to bundleform
RitaDias Jul 3, 2024
8aa727a
refactor(sanity): update publishAt fields in menus
RitaDias Jul 3, 2024
263b2ca
refactor(sanity): update type in dummyGetter
RitaDias Jul 3, 2024
269f279
chore(sanity): remove BUNDLES const
RitaDias Jul 3, 2024
4d9cb73
chore(sanity): update LATEST type to Partial
RitaDias Jul 3, 2024
c233a70
chore(sanity): update validation for bundle date creation
RitaDias Jul 3, 2024
f8794fc
chore(sanity): clean up code
RitaDias Jul 3, 2024
e3c762d
refactor(sanity): add archived filter
RitaDias Jul 3, 2024
8feccd4
refactor(sanity): make single bundle menu + clean up
RitaDias Jul 3, 2024
42feacd
chore(sanity): add missing properties
RitaDias Jul 3, 2024
810f692
feat(sanity): add loading & remove unused code
RitaDias Jul 3, 2024
d7515f5
feat(sanity): add loading to document version, update filter
RitaDias Jul 3, 2024
788cf21
chore(sanity): rename VersionBadge to BundleBadge
RitaDias Jul 3, 2024
0181e4d
chore(sanity): clean up methods and style
RitaDias Jul 3, 2024
b5b7a6f
Merge branch 'corel' into corel-35
RitaDias Jul 3, 2024
f58b961
Merge branch 'corel' into corel-35
RitaDias Jul 3, 2024
af68e88
chore(sanity): remove unused import
RitaDias Jul 3, 2024
9ab6d65
refactor(sanity): use Bundle provider instead of store
RitaDias Jul 3, 2024
62ffd3e
chore(sanity): update bundleRow to use the new name for badge
RitaDias Jul 3, 2024
acdb5ce
chore(sanity): clean up code
RitaDias Jul 4, 2024
e904eed
refactor(sanity): re-add oncancel and oncreate props in dialog
RitaDias Jul 4, 2024
dc9ca47
refactor(sanity): add scroll to bundle menu
RitaDias Jul 4, 2024
12abc2b
refactor(sanity): move from archived to archivedAt
RitaDias Jul 4, 2024
aba518d
refactor(sanity): move version provider to its own file + organise + …
RitaDias Jul 4, 2024
7de4af9
refactor(sanity): remove provider + context, move logic to hook
RitaDias Jul 4, 2024
fd8b9d9
chore(sanity): remove comments + change name from setCurrentVersion t…
RitaDias Jul 4, 2024
421d68a
chore(sanity): rename useVersion to useBundle
RitaDias Jul 4, 2024
5c78e5d
chore(sanity): update comments
RitaDias Jul 4, 2024
7360256
chore(sanity): Rename currentVersion
RitaDias Jul 4, 2024
194c03f
refactor(sanity): rename currentBudnle and setGlobalBundle
RitaDias Jul 4, 2024
137996f
chore(sanity): rename versions to bundles in core directory
RitaDias Jul 4, 2024
0a98e28
chore(sanity): rename to GlobalPerspectiveMenu and move to navbar dir…
RitaDias Jul 4, 2024
ccd4cf6
chore(sanity): rename to DocumentPerspectiveMenu and move to navbar d…
RitaDias Jul 4, 2024
70b18d7
feat(releases): support for (un)archive
jordanl17 Jul 4, 2024
484b480
chore(releases): updating testing for BundlesOverview
jordanl17 Jul 4, 2024
cbc845a
chore(releases): new tests for BundleMenuButton and (un)archive
jordanl17 Jul 4, 2024
602143a
fix(releases): disabling bundle menu btn when action is performed
jordanl17 Jul 5, 2024
f8716ce
Merge branch 'corel' into corel-23-archive-unarchive
jordanl17 Jul 5, 2024
1ff7fb0
Merge branch 'corel' into corel-23-archive-unarchive
jordanl17 Jul 8, 2024
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,5 +1,4 @@
/* eslint-disable @sanity/i18n/no-attribute-string-literals */
import {EllipsisHorizontalIcon, TrashIcon} from '@sanity/icons'
import {ArchiveIcon, EllipsisHorizontalIcon, TrashIcon} from '@sanity/icons'
import {Button, Menu, MenuButton, MenuItem, Spinner} from '@sanity/ui'
import {useState} from 'react'
import {useRouter} from 'sanity/router'
Expand All @@ -12,8 +11,9 @@ type Props = {
}

export const BundleMenuButton = ({bundle}: Props) => {
const {deleteBundle} = useBundleOperations()
const {deleteBundle, updateBundle} = useBundleOperations()
const router = useRouter()
const isBundleArchived = !!bundle?.archivedAt
const [isPerformingOperation, setIsPerformingOperation] = useState(false)

const bundleMenuDisabled = !bundle
Expand All @@ -30,11 +30,22 @@ export const BundleMenuButton = ({bundle}: Props) => {
}
}

const handleOnToggleArchive = async () => {
if (bundleMenuDisabled) return

setIsPerformingOperation(true)
await updateBundle({
...bundle,
archivedAt: isBundleArchived ? undefined : new Date().toISOString(),
})
setIsPerformingOperation(false)
}

return (
<MenuButton
button={
<Button
disabled={bundleMenuDisabled}
disabled={bundleMenuDisabled || isPerformingOperation}
icon={isPerformingOperation ? Spinner : EllipsisHorizontalIcon}
mode="bleed"
padding={2}
Expand All @@ -44,7 +55,14 @@ export const BundleMenuButton = ({bundle}: Props) => {
id="bundle-menu"
menu={
<Menu>
<MenuItem onClick={handleOnDeleteBundle} icon={TrashIcon} text="Delete release" />
<MenuItem
onClick={handleOnToggleArchive}
// TODO: swap line once UnaryIcon is available
// icon={isBundleArchived ? UnarchiveIcon : ArchiveIcon}
Copy link
Member Author

Choose a reason for hiding this comment

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

I can do this as a fast-follow once sanity-icons has been updated with this icon

icon={isBundleArchived ? ArchiveIcon : ArchiveIcon}
text={isBundleArchived ? 'Unarchive' : 'Archive'}
/>
<MenuItem onClick={handleOnDeleteBundle} icon={TrashIcon} text="Delete" />
</Menu>
}
popover={{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import {describe, expect, jest, test} from '@jest/globals'
import {fireEvent, render, screen} from '@testing-library/react'
import {act} from 'react'
import {useRouter} from 'sanity/router'

import {createTestProvider} from '../../../../../../test/testUtils/TestProvider'
import {type BundleDocument} from '../../../../store/bundles/types'
import {useBundleOperations} from '../../../../store/bundles/useBundleOperations'
import {releasesUsEnglishLocaleBundle} from '../../../i18n'
import {BundleMenuButton} from '../BundleMenuButton'

jest.mock('../../../../store/bundles/useBundleOperations', () => ({
useBundleOperations: jest.fn().mockReturnValue({
deleteBundle: jest.fn(),
updateBundle: jest.fn(),
}),
}))

jest.mock('sanity/router', () => ({
...(jest.requireActual('sanity/router') || {}),
useRouter: jest.fn().mockReturnValue({state: {}, navigate: jest.fn()}),
}))

const renderTest = async (bundle: BundleDocument) => {
const wrapper = await createTestProvider({
resources: [releasesUsEnglishLocaleBundle],
})
return render(<BundleMenuButton bundle={bundle} />, {wrapper})
}

describe('BundleMenuButton', () => {
test('will archive an unarchived bundle', async () => {
const activeBundle: BundleDocument = {
_id: 'activeBundle',
_type: 'bundle',
archivedAt: undefined,
title: 'activeBundle',
name: 'activeBundle',
authorId: 'author',
_createdAt: new Date().toISOString(),
_updatedAt: new Date().toISOString(),
_rev: '',
}

await renderTest(activeBundle)

fireEvent.click(screen.getByLabelText('Release menu'))

await act(() => {
fireEvent.click(screen.getByText('Archive'))
})

expect(useBundleOperations().updateBundle).toHaveBeenCalledWith({
...activeBundle,
archivedAt: expect.any(String),
})
})

test('will unarchive an archived bundle', async () => {
const archivedBundle: BundleDocument = {
_id: 'activeBundle',
_type: 'bundle',
archivedAt: new Date().toISOString(),
title: 'activeBundle',
name: 'activeBundle',
authorId: 'author',
_createdAt: new Date().toISOString(),
_updatedAt: new Date().toISOString(),
_rev: '',
}
await renderTest(archivedBundle)

fireEvent.click(screen.getByLabelText('Release menu'))

await act(() => {
fireEvent.click(screen.getByText('Unarchive'))
})

expect(useBundleOperations().updateBundle).toHaveBeenCalledWith({
...archivedBundle,
archivedAt: undefined,
})
})

test('will delete a bundle', async () => {
const activeBundle: BundleDocument = {
_id: 'activeBundle',
_type: 'bundle',
archivedAt: new Date().toISOString(),
title: 'activeBundle',
name: 'activeBundle',
authorId: 'author',
_createdAt: new Date().toISOString(),
_updatedAt: new Date().toISOString(),
_rev: '',
}
await renderTest(activeBundle)

fireEvent.click(screen.getByLabelText('Release menu'))

await act(() => {
fireEvent.click(screen.getByText('Delete'))
})

expect(useBundleOperations().deleteBundle).toHaveBeenCalledWith(activeBundle._id)
expect(useRouter().navigate).not.toHaveBeenCalled()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Box, Card, Flex, Stack, Text} from '@sanity/ui'
import {useRouter} from 'sanity/router'

import {BundleBadge} from '../../../bundles/components/BundleBadge'
import {RelativeTime} from '../../../components/RelativeTime'
import {RelativeTime} from '../../../components'
import {type BundleDocument} from '../../../store/bundles/types'
import {BundleMenuButton} from '../BundleMenuButton/BundleMenuButton'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable i18next/no-literal-string */
import {Card, Stack, Text} from '@sanity/ui'
import {useMemo} from 'react'
import {styled} from 'styled-components'
Expand Down Expand Up @@ -51,7 +50,7 @@ export function BundlesTable({bundles, searchTerm, setSearchTerm}: BundlesTableP
return (
<Stack as="table" space={1}>
<BundleHeader
searchDisabled={!bundles.length}
searchDisabled={!searchTerm && !bundles.length}
searchTerm={searchTerm}
setSearchTerm={setSearchTerm}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('BundlesTable', () => {
const bundleRow = screen.getAllByTestId('bundle-row')[0]
fireEvent.click(within(bundleRow).getByLabelText('Release menu'))

fireEvent.click(screen.getByText('Delete release'))
fireEvent.click(screen.getByText('Delete'))

await waitFor(() => {
expect(useBundleOperations().deleteBundle).toHaveBeenCalledWith('123')
Expand Down
24 changes: 17 additions & 7 deletions packages/sanity/src/core/releases/tool/BundlesOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {AddIcon} from '@sanity/icons'
import {Box, Button, type ButtonMode, Card, Container, Flex, Heading, Stack, Text} from '@sanity/ui'
import {isBefore} from 'date-fns'
import {type MouseEventHandler, useCallback, useMemo, useState} from 'react'
import {type MouseEventHandler, useCallback, useEffect, useMemo, useState} from 'react'

import {Button as StudioButton} from '../../../ui-components'
import {CreateBundleDialog} from '../../bundles/components/dialog/CreateBundleDialog'
Expand All @@ -28,19 +28,28 @@ export default function BundlesOverview() {
const groupedBundles = useMemo(
() =>
data?.reduce<{open: BundleDocument[]; archived: BundleDocument[]}>((groups, bundle) => {
const group =
bundle.publishedAt && isBefore(new Date(bundle.publishedAt), new Date())
? 'archived'
: 'open'
const isBundleArchived =
bundle.archivedAt ||
(bundle.publishedAt && isBefore(new Date(bundle.publishedAt), new Date()))
const group = isBundleArchived ? 'archived' : 'open'

return {...groups, [group]: [...groups[group], bundle]}
}, EMPTY_BUNDLE_GROUPS) || EMPTY_BUNDLE_GROUPS,
[data],
)

// switch to open mode if on archived mode and there are no archived bundles
useEffect(() => {
if (bundleGroupMode === 'archived' && !groupedBundles.archived.length) {
setBundleGroupMode('open')
}
}, [bundleGroupMode, groupedBundles.archived.length])

// clear search when mode changes
useEffect(() => setSearchTerm(''), [bundleGroupMode])

const handleBundleGroupModeChange = useCallback<MouseEventHandler<HTMLButtonElement>>(
({currentTarget: {value: groupMode}}) => {
setSearchTerm('') // clear the table search applied
setBundleGroupMode(groupMode as Mode)
},
[],
Expand Down Expand Up @@ -111,7 +120,8 @@ export default function BundlesOverview() {
}

const applySearchTermToBundles = useCallback(
(bundle: BundleDocument) => !searchTerm || bundle.title.includes(searchTerm),
(bundle: BundleDocument) =>
!searchTerm || bundle.title.toLocaleLowerCase().includes(searchTerm.toLocaleLowerCase()),
[searchTerm],
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {beforeEach, describe, expect, it, jest} from '@jest/globals'
import {fireEvent, render, screen} from '@testing-library/react'
import {fireEvent, render, screen, waitFor} from '@testing-library/react'
import {type ReactNode} from 'react'

import {queryByDataUi} from '../../../../../test/setup/customQueries'
Expand Down Expand Up @@ -108,7 +108,8 @@ describe('BundlesOverview', () => {
const bundles = [
{title: 'Bundle 1'},
{title: 'Bundle 2'},
{title: 'Bundle 3', publishedAt: new Date()},
{title: 'Bundle 3', publishedAt: new Date().toISOString()},
{title: 'Bundle 4', archivedAt: new Date().toISOString()},
] as unknown as BundleDocument[]

beforeEach(async () => {
Expand All @@ -132,11 +133,14 @@ describe('BundlesOverview', () => {
expect(screen.getByText('Archived').closest('button')).not.toBeDisabled()
})

it('shows published bundles', () => {
it('shows published bundles', async () => {
fireEvent.click(screen.getByText('Archived'))

screen.getByText('Bundle 3')
expect(screen.queryByText('Bundle 1')).toBeNull()
await waitFor(() => {
screen.getByText('Bundle 3')
screen.getByText('Bundle 4')
expect(screen.queryByText('Bundle 1')).toBeNull()
})
})

it('allows for searching bundles', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/sanity/src/core/store/bundles/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface BundleDocument extends SanityDocument {
icon?: IconSymbol
authorId: string
publishedAt?: string
archivedAt?: string
}

/**
Expand Down
13 changes: 11 additions & 2 deletions packages/sanity/src/core/store/bundles/useBundleOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,22 @@ export function useBundleOperations() {

const handleUpdateBundle = useCallback(
async (bundle: BundleDocument) => {
if (!client) return null

const document = {
...bundle,
_type: 'bundle',
} as BundleDocument
const unsetKeys = Object.entries(bundle)
.filter(([_, value]) => value === undefined)
.map(([key]) => key)

const res = await client?.patch(bundle._id).set(document).commit()
return res
let clientOperation = client.patch(bundle._id).set(document)
if (unsetKeys.length) {
clientOperation = clientOperation.unset(unsetKeys)
}

return clientOperation.commit()
},
[client],
)
Expand Down
Loading