Skip to content

Commit 9f17db8

Browse files

File tree

8 files changed

+167
-8
lines changed

8 files changed

+167
-8
lines changed

packages/ui/src/elements/EditMany/DrawerContent.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ import { useTranslation } from '../../providers/Translation/index.js'
2929
import { abortAndIgnore, handleAbortRef } from '../../utilities/abortAndIgnore.js'
3030
import { parseSearchParams } from '../../utilities/parseSearchParams.js'
3131
import { FieldSelect } from '../FieldSelect/index.js'
32-
import { baseClass, type EditManyProps } from './index.js'
3332
import './index.scss'
3433
import '../../forms/RenderFields/index.scss'
34+
import { baseClass, type EditManyProps } from './index.js'
3535

3636
const Submit: React.FC<{
3737
readonly action: string
@@ -123,6 +123,10 @@ type EditManyDrawerContentProps = {
123123
* The IDs of the selected items
124124
*/
125125
ids?: (number | string)[]
126+
/**
127+
* The function to call after a successful action
128+
*/
129+
onSuccess?: () => void
126130
/**
127131
* Whether all items are selected
128132
*/
@@ -143,6 +147,7 @@ export const EditManyDrawerContent: React.FC<EditManyDrawerContentProps> = (prop
143147
count,
144148
drawerSlug,
145149
ids,
150+
onSuccess: onSuccessFromProps,
146151
selectAll,
147152
selectedFields,
148153
setSelectedFields,
@@ -263,6 +268,10 @@ export const EditManyDrawerContent: React.FC<EditManyDrawerContentProps> = (prop
263268
)
264269
clearRouteCache()
265270
closeModal(drawerSlug)
271+
272+
if (typeof onSuccessFromProps === 'function') {
273+
onSuccessFromProps()
274+
}
266275
}
267276

268277
const onFieldSelect = useCallback<OnFieldSelect>(

packages/ui/src/elements/EditMany/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ export type EditManyProps = {
2222
}
2323

2424
export const EditMany: React.FC<EditManyProps> = (props) => {
25-
const { count, selectAll, selected } = useSelection()
25+
const { count, selectAll, selected, toggleAll } = useSelection()
2626
return (
2727
<EditMany_v4
2828
{...props}
2929
count={count}
3030
ids={Array.from(selected.keys())}
31+
onSuccess={() => toggleAll(false)}
3132
selectAll={selectAll === SelectAllStatus.AllAvailable}
3233
/>
3334
)
@@ -37,9 +38,10 @@ export const EditMany_v4: React.FC<
3738
{
3839
count: number
3940
ids: (number | string)[]
41+
onSuccess?: () => void
4042
selectAll: boolean
4143
} & EditManyProps
42-
> = ({ collection, count, ids, selectAll }) => {
44+
> = ({ collection, count, ids, onSuccess, selectAll }) => {
4345
const { permissions } = useAuth()
4446
const { openModal } = useModal()
4547

@@ -73,6 +75,7 @@ export const EditMany_v4: React.FC<
7375
count={count}
7476
drawerSlug={drawerSlug}
7577
ids={ids}
78+
onSuccess={onSuccess}
7679
selectAll={selectAll}
7780
selectedFields={selectedFields}
7881
setSelectedFields={setSelectedFields}

packages/ui/src/elements/PublishMany/DrawerContent.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ConfirmationModal } from '../ConfirmationModal/index.js'
2020
type PublishManyDrawerContentProps = {
2121
drawerSlug: string
2222
ids: (number | string)[]
23+
onSuccess?: () => void
2324
selectAll: boolean
2425
} & PublishManyProps
2526
export function PublishManyDrawerContent(props: PublishManyDrawerContentProps) {
@@ -28,6 +29,7 @@ export function PublishManyDrawerContent(props: PublishManyDrawerContentProps) {
2829
collection: { slug, labels: { plural, singular } } = {},
2930
drawerSlug,
3031
ids,
32+
onSuccess,
3133
selectAll,
3234
} = props
3335

@@ -136,6 +138,11 @@ export function PublishManyDrawerContent(props: PublishManyDrawerContentProps) {
136138
)
137139

138140
clearRouteCache()
141+
142+
if (typeof onSuccess === 'function') {
143+
onSuccess()
144+
}
145+
139146
return null
140147
}
141148

@@ -163,6 +170,7 @@ export function PublishManyDrawerContent(props: PublishManyDrawerContentProps) {
163170
selectAll,
164171
clearRouteCache,
165172
addDefaultError,
173+
onSuccess,
166174
])
167175

168176
return (

packages/ui/src/elements/PublishMany/index.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ export type PublishManyProps = {
1515
}
1616

1717
export const PublishMany: React.FC<PublishManyProps> = (props) => {
18-
const { count, selectAll, selected } = useSelection()
18+
const { count, selectAll, selected, toggleAll } = useSelection()
1919

2020
return (
2121
<PublishMany_v4
2222
{...props}
2323
count={count}
2424
ids={Array.from(selected.keys())}
25+
onSuccess={() => toggleAll(false)}
2526
selectAll={selectAll === SelectAllStatus.AllAvailable}
2627
/>
2728
)
@@ -30,10 +31,18 @@ export const PublishMany: React.FC<PublishManyProps> = (props) => {
3031
type PublishMany_v4Props = {
3132
count: number
3233
ids: (number | string)[]
34+
onSuccess?: () => void
3335
selectAll: boolean
3436
} & PublishManyProps
3537
export const PublishMany_v4: React.FC<PublishMany_v4Props> = (props) => {
36-
const { collection, collection: { slug, versions } = {}, count, ids, selectAll } = props
38+
const {
39+
collection,
40+
collection: { slug, versions } = {},
41+
count,
42+
ids,
43+
onSuccess,
44+
selectAll,
45+
} = props
3746

3847
const { permissions } = useAuth()
3948
const { t } = useTranslation()
@@ -63,6 +72,7 @@ export const PublishMany_v4: React.FC<PublishMany_v4Props> = (props) => {
6372
collection={collection}
6473
drawerSlug={drawerSlug}
6574
ids={ids}
75+
onSuccess={onSuccess}
6676
selectAll={selectAll}
6777
/>
6878
</React.Fragment>

packages/ui/src/elements/UnpublishMany/DrawerContent.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ConfirmationModal } from '../ConfirmationModal/index.js'
2020
type UnpublishManyDrawerContentProps = {
2121
drawerSlug: string
2222
ids: (number | string)[]
23+
onSuccess?: () => void
2324
selectAll: boolean
2425
} & UnpublishManyProps
2526

@@ -29,6 +30,7 @@ export function UnpublishManyDrawerContent(props: UnpublishManyDrawerContentProp
2930
collection: { slug, labels: { plural, singular } } = {},
3031
drawerSlug,
3132
ids,
33+
onSuccess,
3234
selectAll,
3335
} = props
3436

@@ -126,6 +128,11 @@ export function UnpublishManyDrawerContent(props: UnpublishManyDrawerContentProp
126128
)
127129

128130
clearRouteCache()
131+
132+
if (typeof onSuccess === 'function') {
133+
onSuccess()
134+
}
135+
129136
return null
130137
}
131138

@@ -153,6 +160,7 @@ export function UnpublishManyDrawerContent(props: UnpublishManyDrawerContentProp
153160
selectAll,
154161
clearRouteCache,
155162
addDefaultError,
163+
onSuccess,
156164
])
157165

158166
return (

packages/ui/src/elements/UnpublishMany/index.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ export type UnpublishManyProps = {
1515
}
1616

1717
export const UnpublishMany: React.FC<UnpublishManyProps> = (props) => {
18-
const { count, selectAll, selected } = useSelection()
18+
const { count, selectAll, selected, toggleAll } = useSelection()
1919

2020
return (
2121
<UnpublishMany_v4
2222
{...props}
2323
count={count}
2424
ids={Array.from(selected.keys())}
25+
onSuccess={() => toggleAll(false)}
2526
selectAll={selectAll === SelectAllStatus.AllAvailable}
2627
/>
2728
)
@@ -31,10 +32,18 @@ export const UnpublishMany_v4: React.FC<
3132
{
3233
count: number
3334
ids: (number | string)[]
35+
onSuccess?: () => void
3436
selectAll: boolean
3537
} & UnpublishManyProps
3638
> = (props) => {
37-
const { collection, collection: { slug, versions } = {}, count, ids, selectAll } = props
39+
const {
40+
collection,
41+
collection: { slug, versions } = {},
42+
count,
43+
ids,
44+
onSuccess,
45+
selectAll,
46+
} = props
3847

3948
const { t } = useTranslation()
4049
const { permissions } = useAuth()
@@ -63,6 +72,7 @@ export const UnpublishMany_v4: React.FC<
6372
collection={collection}
6473
drawerSlug={drawerSlug}
6574
ids={ids}
75+
onSuccess={onSuccess}
6676
selectAll={selectAll}
6777
/>
6878
</React.Fragment>

packages/ui/src/views/List/ListSelection/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22
import type { ClientCollectionConfig } from 'payload'
33

4-
import React, { Fragment } from 'react'
4+
import React, { Fragment, useCallback } from 'react'
55

66
import { DeleteMany } from '../../../elements/DeleteMany/index.js'
77
import { EditMany_v4 } from '../../../elements/EditMany/index.js'
@@ -27,6 +27,8 @@ export const ListSelection: React.FC<ListSelectionProps> = ({
2727
const { count, getSelectedIds, selectAll, toggleAll, totalDocs } = useSelection()
2828
const { t } = useTranslation()
2929

30+
const onActionSuccess = useCallback(() => toggleAll(false), [toggleAll])
31+
3032
if (count === 0) {
3133
return null
3234
}
@@ -53,18 +55,21 @@ export const ListSelection: React.FC<ListSelectionProps> = ({
5355
collection={collectionConfig}
5456
count={count}
5557
ids={getSelectedIds()}
58+
onSuccess={onActionSuccess}
5659
selectAll={selectAll === SelectAllStatus.AllAvailable}
5760
/>
5861
<PublishMany_v4
5962
collection={collectionConfig}
6063
count={count}
6164
ids={getSelectedIds()}
65+
onSuccess={onActionSuccess}
6266
selectAll={selectAll === SelectAllStatus.AllAvailable}
6367
/>
6468
<UnpublishMany_v4
6569
collection={collectionConfig}
6670
count={count}
6771
ids={getSelectedIds()}
72+
onSuccess={onActionSuccess}
6873
selectAll={selectAll === SelectAllStatus.AllAvailable}
6974
/>
7075
</Fragment>

test/bulk-edit/e2e.spec.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,112 @@ test.describe('Bulk Edit', () => {
488488
await expect(field.locator('#field-array__0__noRead')).toBeHidden()
489489
await expect(field.locator('#field-array__0__noUpdate')).toBeDisabled()
490490
})
491+
492+
test('should toggle list selections off on successful publish', async () => {
493+
await deleteAllPosts()
494+
495+
const postCount = 3
496+
Array.from({ length: postCount }).forEach(async (_, i) => {
497+
await createPost({ title: `Post ${i + 1}` }, { draft: true })
498+
})
499+
500+
await page.goto(postsUrl.list)
501+
await page.locator('input#select-all').check()
502+
503+
await page.locator('.list-selection__button[aria-label="Publish"]').click()
504+
await page.locator('#publish-posts #confirm-action').click()
505+
506+
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
507+
`Updated ${postCount} Posts successfully.`,
508+
)
509+
510+
// eslint-disable-next-line jest-dom/prefer-checked
511+
await expect(page.locator('input#select-all')).not.toHaveAttribute('checked', '')
512+
513+
for (let i = 0; i < postCount; i++) {
514+
// eslint-disable-next-line jest-dom/prefer-checked
515+
await expect(findTableCell(page, '_select', `Post ${i + 1}`)).not.toHaveAttribute(
516+
'checked',
517+
'',
518+
)
519+
}
520+
})
521+
522+
test('should toggle list selections off on successful unpublish', async () => {
523+
await deleteAllPosts()
524+
525+
const postCount = 3
526+
Array.from({ length: postCount }).forEach(async (_, i) => {
527+
await createPost({ title: `Post ${i + 1}`, _status: 'published' })
528+
})
529+
530+
await page.goto(postsUrl.list)
531+
await page.locator('input#select-all').check()
532+
533+
await page.locator('.list-selection__button[aria-label="Unpublish"]').click()
534+
await page.locator('#unpublish-posts #confirm-action').click()
535+
536+
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
537+
`Updated ${postCount} Posts successfully.`,
538+
)
539+
540+
// eslint-disable-next-line jest-dom/prefer-checked
541+
await expect(page.locator('input#select-all')).not.toHaveAttribute('checked', '')
542+
543+
for (let i = 0; i < postCount; i++) {
544+
// eslint-disable-next-line jest-dom/prefer-checked
545+
await expect(findTableCell(page, '_select', `Post ${i + 1}`)).not.toHaveAttribute(
546+
'checked',
547+
'',
548+
)
549+
}
550+
})
551+
552+
test('should toggle list selections off on successful edit', async () => {
553+
await deleteAllPosts()
554+
555+
const postCount = 3
556+
Array.from({ length: postCount }).forEach(async (_, i) => {
557+
await createPost({ title: `Post ${i + 1}` })
558+
})
559+
560+
await page.goto(postsUrl.list)
561+
await page.locator('input#select-all').check()
562+
563+
await page.locator('.list-selection__button[aria-label="Edit"]').click()
564+
565+
const editDrawer = page.locator('dialog#edit-posts')
566+
await expect(editDrawer).toBeVisible()
567+
568+
const fieldSelect = editDrawer.locator('.field-select')
569+
await expect(fieldSelect).toBeVisible()
570+
571+
const fieldSelectControl = fieldSelect.locator('.rs__control')
572+
await expect(fieldSelectControl).toBeVisible()
573+
await fieldSelectControl.click()
574+
575+
const titleOption = fieldSelect.locator('.rs__option:has-text("Title")').first()
576+
await titleOption.click()
577+
578+
await editDrawer.locator('input#field-title').fill('test')
579+
580+
await editDrawer.locator('button[type="submit"]:has-text("Publish changes")').click()
581+
582+
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
583+
`Updated ${postCount} Posts successfully.`,
584+
)
585+
586+
// eslint-disable-next-line jest-dom/prefer-checked
587+
await expect(page.locator('input#select-all')).not.toHaveAttribute('checked', '')
588+
589+
for (let i = 0; i < postCount; i++) {
590+
// eslint-disable-next-line jest-dom/prefer-checked
591+
await expect(findTableCell(page, '_select', `Post ${i + 1}`)).not.toHaveAttribute(
592+
'checked',
593+
'',
594+
)
595+
}
596+
})
491597
})
492598

493599
async function selectFieldToEdit(

0 commit comments

Comments
 (0)