feat(ui): confirmation modal (#11271)

There are nearly a dozen independent implementations of the same modal
spread throughout the admin panel and various plugins. These modals are
used to confirm or cancel an action, such as deleting a document, bulk
publishing, etc. Each of these instances is nearly identical, leading to
unnecessary development efforts when creating them, inconsistent UI, and
duplicative stylesheets.

Everything is now standardized behind a new `ConfirmationModal`
component. This modal comes with a standard API that is flexible enough
to replace nearly every instance. This component has also been exported
for reuse.

Here is a basic example of how to use it:

```tsx
'use client'
import { ConfirmationModal, useModal } from '@payloadcms/ui'
import React, { Fragment } from 'react'

const modalSlug = 'my-confirmation-modal'

export function MyComponent() {
  const { openModal } = useModal()

  return (
    <Fragment>
      <button
        onClick={() => {
          openModal(modalSlug)
        }}
        type="button"
      >
        Do something
      </button>
      <ConfirmationModal
        heading="Are you sure?"
        body="Confirm or cancel before proceeding."
        modalSlug={modalSlug}
        onConfirm={({ closeConfirmationModal, setConfirming }) => {
          // do something
          setConfirming(false)
          closeConfirmationModal()
        }}
      />
    </Fragment>
  )
}
```
This commit is contained in:
Jacob Fletcher
2025-02-19 02:27:03 -05:00
committed by GitHub
parent 132852290a
commit bd8ced1b60
41 changed files with 819 additions and 1364 deletions

1
test/versions/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
uploads

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

View File

@@ -134,7 +134,7 @@ describe('Versions', () => {
await rowToDelete.locator('.cell-_select input').check()
await page.locator('.delete-documents__toggle').click()
await page.locator('#confirm-delete').click()
await page.locator('#delete-draft-posts #confirm-action').click()
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
'Deleted 1 Draft Post successfully.',
@@ -152,7 +152,7 @@ describe('Versions', () => {
// Bulk edit the selected rows
await page.locator('.publish-many__toggle').click()
await page.locator('#confirm-publish').click()
await page.locator('#publish-draft-posts #confirm-action').click()
// Check that the statuses for each row has been updated to `published`
await expect(findTableCell(page, '_status', 'Published Title')).toContainText('Published')
@@ -176,7 +176,7 @@ describe('Versions', () => {
await expect(findTableCell(page, '_status', title)).toContainText('Draft')
await selectTableRow(page, title)
await page.locator('.publish-many__toggle').click()
await page.locator('#confirm-publish').click()
await page.locator('#publish-autosave-posts #confirm-action').click()
await expect(findTableCell(page, '_status', title)).toContainText('Published')
})
@@ -189,7 +189,7 @@ describe('Versions', () => {
// Bulk edit the selected rows
await page.locator('.unpublish-many__toggle').click()
await page.locator('#confirm-unpublish').click()
await page.locator('#unpublish-draft-posts #confirm-action').click()
// Check that the statuses for each row has been updated to `draft`
await expect(findTableCell(page, '_status', 'Published Title')).toContainText('Draft')
@@ -565,7 +565,7 @@ describe('Versions', () => {
// revert to last published version
await page.locator('#action-revert-to-published').click()
await saveDocAndAssert(page, '#action-revert-to-published-confirm')
await saveDocAndAssert(page, '[id^=confirm-revert-] #confirm-action')
// verify that spanish content is reverted correctly
await expect(page.locator('#field-title')).toHaveValue(spanishTitle)