feat!: move from react-toastify to sonner (#6682)

**BREAKING:** We now export toast from `sonner` instead of
`react-toastify`. If you send out toasts from your own projects, make
sure to use our `toast` export, or install `sonner`. React-toastify
toasts will no longer work anymore. The Toast APIs are mostly similar,
but there are some differences if you provide options to your toast

CSS styles have been changed from Toastify

```css
/* before */
.Toastify


/* current */
.payload-toast-container
.payload-toast-item
.payload-toast-close-button

/* individual toast items will also have these classes depending on the state */
.toast-info
.toast-warning
.toast-success
.toast-error
```


https://github.com/payloadcms/payload/assets/70709113/da3e732e-aafc-4008-9469-b10f4eb06b35

---------

Co-authored-by: Paul Popus <paul@nouance.io>
This commit is contained in:
Alessio Gravili
2024-06-11 14:12:59 -04:00
committed by GitHub
parent 10c6ffafc3
commit cb3355b30f
87 changed files with 747 additions and 353 deletions

View File

@@ -250,7 +250,7 @@ describe('access control', () => {
await expect(page.locator('#field-name')).toHaveValue('name')
await expect(page.locator('#action-save')).toBeVisible()
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('#action-save')).toBeHidden()
await expect(page.locator('#field-name')).toBeDisabled()
})
@@ -277,7 +277,7 @@ describe('access control', () => {
await documentDrawer.locator('#field-name').fill('name')
await expect(documentDrawer.locator('#field-name')).toHaveValue('name')
await documentDrawer.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(documentDrawer.locator('#action-save')).toBeHidden()
await expect(documentDrawer.locator('#field-name')).toBeDisabled()
})
@@ -301,7 +301,7 @@ describe('access control', () => {
await expect(page.locator('#field-name')).toBeVisible()
await page.locator('#field-name').fill('anonymous@email.com')
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('#field-name')).toBeDisabled()
await expect(page.locator('#action-save')).toBeHidden()
@@ -309,7 +309,7 @@ describe('access control', () => {
await expect(page.locator('#field-name')).toBeVisible()
await page.locator('#field-name').fill(devUser.email)
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(page.locator('#field-name')).toBeEnabled()
await expect(page.locator('#action-save')).toBeVisible()
})
@@ -332,7 +332,7 @@ describe('access control', () => {
await expect(documentDrawer).toBeVisible()
await documentDrawer.locator('#field-name').fill('anonymous@email.com')
await documentDrawer.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(documentDrawer.locator('#field-name')).toBeDisabled()
await documentDrawer.locator('button.doc-drawer__header-close').click()
await expect(documentDrawer).toBeHidden()
@@ -341,7 +341,7 @@ describe('access control', () => {
await expect(documentDrawer2).toBeVisible()
await documentDrawer2.locator('#field-name').fill('dev@payloadcms.com')
await documentDrawer2.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(documentDrawer2.locator('#field-name')).toBeEnabled()
})
})
@@ -355,7 +355,7 @@ describe('access control', () => {
await expect(page.locator('#field-name')).toBeEnabled()
await page.locator('#field-name').fill('anonymous@email.com')
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText(
await expect(page.locator('.payload-toast-container')).toContainText(
'You are not allowed to perform this action',
)

View File

@@ -769,7 +769,7 @@ describe('admin1', () => {
await page.goto(postsUrl.list)
await selectAndDeleteAll()
await expect(page.locator('.Toastify__toast--success')).toHaveText(
await expect(page.locator('.payload-toast-container .toast-success')).toHaveText(
'Deleted 3 Posts successfully.',
)
await expect(page.locator('.collection-list__no-results')).toBeVisible()
@@ -803,7 +803,7 @@ describe('admin1', () => {
await titleInput.fill(bulkTitle)
await page.locator('.form-submit button[type="submit"].edit-many__publish').click()
await expect(page.locator('.Toastify__toast--success')).toContainText(
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
'Updated 3 Posts successfully.',
)
await expect(page.locator('.row-1 .cell-title')).toContainText(bulkTitle)

View File

@@ -233,7 +233,7 @@ describe('fields - relationship', () => {
await openDocControls(page)
await page.locator('#action-duplicate').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
const field = page.locator('#field-relationship .relationship--single-value__text')
await expect(field).toHaveText(relationOneDoc.id)
@@ -259,7 +259,7 @@ describe('fields - relationship', () => {
await expect(field).toContainText(anotherRelationOneDoc.id)
await wait(2000) // Need to wait form state to come back before clicking save
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText(`is invalid: ${fieldName}`)
await expect(page.locator('.payload-toast-container')).toContainText(`is invalid: ${fieldName}`)
filteredField = page.locator(`#field-${fieldName} .react-select`)
await filteredField.click({ delay: 100 })
filteredOptions = filteredField.locator('.rs__option')
@@ -406,13 +406,13 @@ describe('fields - relationship', () => {
await drawerField.fill('Newly created document')
const saveButton = documentDrawer.locator('#action-save')
await saveButton.click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect(
page.locator('#field-relationshipHasMany .value-container .rs__multi-value'),
).toHaveCount(1)
await drawerField.fill('Updated document')
await saveButton.click()
await expect(page.locator('.Toastify')).toContainText('Updated successfully')
await expect(page.locator('.payload-toast-container')).toContainText('Updated successfully')
await page.locator('.doc-drawer__header-close').click()
await expect(
page.locator('#field-relationshipHasMany .value-container .rs__multi-value'),

View File

@@ -107,7 +107,7 @@ describe('Array', () => {
await page.locator('#field-arrayWithMinRows >> .array-field__add-row').click()
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.Toastify')).toContainText(
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: arrayWithMinRows',
)
})
@@ -290,7 +290,7 @@ describe('Array', () => {
await targetInput.fill(bulkText)
await page.locator('#edit-array-fields .form-submit .edit-many__save').click()
await expect(page.locator('.Toastify__toast--success')).toContainText(
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
'Updated 3 Array Fields successfully.',
)
})

View File

@@ -182,7 +182,7 @@ describe('Block fields', () => {
test('should bypass min rows validation when no rows present and field is not required', async () => {
await page.goto(url.create)
await saveDocAndAssert(page)
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
})
test('should fail min rows validation when rows are present', async () => {
@@ -208,7 +208,7 @@ describe('Block fields', () => {
await expect(firstRow).toHaveValue('first row')
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.Toastify')).toContainText(
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: blocksWithMinRows',
)
})

View File

@@ -835,7 +835,9 @@ describe('lexicalBlocks', () => {
await saveDocAndAssert(page)
await expect(page.locator('.Toastify')).not.toContainText('Please correct invalid fields.')
await expect(page.locator('.payload-toast-container')).not.toContainText(
'Please correct invalid fields.',
)
}
// eslint-disable-next-line playwright/expect-expect
@@ -920,7 +922,9 @@ describe('lexicalBlocks', () => {
await page.click('#action-save', { delay: 100 })
await wait(300)
await expect(page.locator('.Toastify')).toContainText('The following field is invalid')
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid',
)
await wait(300)
const requiredTooltip = conditionalArrayBlock

View File

@@ -138,7 +138,7 @@ describe('Number', () => {
test('should bypass min rows validation when no rows present and field is not required', async () => {
await page.goto(url.create)
await saveDocAndAssert(page)
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
})
test('should fail min rows validation when rows are present', async () => {
@@ -151,7 +151,7 @@ describe('Number', () => {
await page.keyboard.press('Enter')
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.Toastify')).toContainText(
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: withMinRows',
)
})

View File

@@ -86,13 +86,13 @@ describe('relationship', () => {
const textValue = 'hello'
await textField.fill(textValue)
await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click()
await expect(
page.locator('#field-relationship .relationship--single-value__text'),
).toContainText(textValue)
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
})
test('should create nested inline relationships', async () => {
@@ -151,7 +151,7 @@ describe('relationship', () => {
await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('create')
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
})
test('should hide relationship add new button', async () => {
@@ -250,10 +250,10 @@ describe('relationship', () => {
await page.locator('.drawer__content #field-text').fill('something')
await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click()
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
// Create a new doc for the `relationshipHasMany` field
await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).not.toContain('create')
@@ -263,7 +263,7 @@ describe('relationship', () => {
// Save and close the drawer
await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click()
// Now open the drawer again to edit the `text` field _using the keyboard_
@@ -286,12 +286,12 @@ describe('relationship', () => {
// save drawer
await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
// close drawer
await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click()
// save document and reload
await page.locator('#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await page.reload()
// check if the value is saved
@@ -352,7 +352,7 @@ describe('relationship', () => {
await page.getByText('Seeded text document', { exact: true }).click()
await saveDocAndAssert(page)
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
})
test('should fail min rows validation when rows are present', async () => {
@@ -373,7 +373,7 @@ describe('relationship', () => {
.click()
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.Toastify')).toContainText(
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: relationshipWithMinRows',
)
})

View File

@@ -109,7 +109,7 @@ describe('Upload', () => {
page.locator('[id^=doc-drawer_uploads_1_] .file-field__upload .file-field__filename'),
).toHaveValue('payload.png')
await page.locator('[id^=doc-drawer_uploads_1_] #action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
// Assert that the media field has the png upload
await expect(
@@ -140,7 +140,7 @@ describe('Upload', () => {
page.locator('[id^=doc-drawer_uploads_1_] .file-field__upload .file-field__filename'),
).toHaveValue('payload.png')
await page.locator('[id^=doc-drawer_uploads_1_] #action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await page.locator('.field-type.upload .file-details__remove').click()
})

View File

@@ -105,7 +105,7 @@ describe('fields', () => {
await page.click('#action-save', { delay: 200 })
// toast error
await expect(page.locator('.Toastify')).toContainText(
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: uniqueText',
)
@@ -126,7 +126,7 @@ describe('fields', () => {
await page.locator('#action-save').click()
// toast error
await expect(page.locator('.Toastify')).toContainText(
await expect(page.locator('.payload-toast-container')).toContainText(
'The following field is invalid: group.unique',
)
@@ -312,7 +312,7 @@ describe('fields', () => {
await titleInput.fill('Row 123')
await page.locator('#action-save').click()
await wait(200)
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
// ensure the 'title' field is visible in the table header
await page.goto(url.list)
@@ -334,7 +334,7 @@ describe('fields', () => {
await titleInput.fill('Row 456')
await page.locator('#action-save').click()
await wait(200)
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
// ensure there are not two ID fields in the table header
await page.goto(url.list)

View File

@@ -177,7 +177,7 @@ export async function saveDocHotkeyAndAssert(page: Page): Promise<void> {
await page.keyboard.down('Control')
}
await page.keyboard.down('s')
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
}
export async function saveDocAndAssert(
@@ -189,10 +189,10 @@ export async function saveDocAndAssert(
await page.click(selector, { delay: 100 })
if (expectation === 'success') {
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).not.toContain('create')
} else {
await expect(page.locator('.Toastify .Toastify__toast--error')).toBeVisible()
await expect(page.locator('.payload-toast-container .toast-error')).toBeVisible()
}
}

View File

@@ -164,7 +164,7 @@ describe('Localization', () => {
await page.waitForURL(`**${url.edit(id)}`)
await openDocControls(page)
await page.locator('#action-duplicate').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).not.toContain(id)
await expect(page.locator('#field-title')).toHaveValue(englishTitle)
await changeLocale(page, spanishLocale)
@@ -215,7 +215,9 @@ describe('Localization', () => {
await page.locator('#action-duplicate').click()
await expect(page.locator('.id-label')).not.toContainText(originalID)
await expect(page.locator('#field-title')).toHaveValue(englishTitle)
await expect(page.locator('.Toastify')).toContainText('successfully duplicated')
await expect(page.locator('.payload-toast-container')).toContainText(
'successfully duplicated',
)
await expect(page.locator('.id-label')).not.toContainText(originalID)
})
})

View File

@@ -233,12 +233,16 @@ describe('uploads', () => {
.locator('[id^=doc-drawer_media_2_] .file-field__upload input[type="file"]')
.setInputFiles(path.resolve(dirname, './image.png'))
await page.locator('[id^=doc-drawer_media_2_] button#action-save').click()
await expect(page.locator('.Toastify .Toastify__toast--success')).toContainText('successfully')
await page.locator('.Toastify .Toastify__toast--success .Toastify__close-button').click()
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
'successfully',
)
await page
.locator('.payload-toast-container .toast-success .payload-toast-close-button')
.click()
// save the document and expect an error
await page.locator('button#action-save').click()
await expect(page.locator('.Toastify .Toastify__toast--error')).toContainText(
await expect(page.locator('.payload-toast-container .toast-error')).toContainText(
'The following field is invalid: audio',
)
})
@@ -249,7 +253,7 @@ describe('uploads', () => {
await expect(page.locator('.file-field__filename')).toHaveValue('2mb.jpg')
await page.click('#action-save', { delay: 100 })
await expect(page.locator('.Toastify .Toastify__toast--error')).toContainText(
await expect(page.locator('.payload-toast-container .toast-error')).toContainText(
'File size limit has been reached',
)
})
@@ -347,7 +351,7 @@ describe('uploads', () => {
await page.locator('button:has-text("Apply Changes")').click()
await page.waitForSelector('button#action-save')
await page.locator('button#action-save').click()
await expect(page.locator('.Toastify')).toContainText('successfully')
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
await wait(1000) // Wait for the save
}

View File

@@ -137,7 +137,7 @@ describe('versions', () => {
await page.locator('.delete-documents__toggle').click()
await page.locator('#confirm-delete').click()
await expect(page.locator('.Toastify__toast--success')).toContainText(
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
'Deleted 1 Draft Post successfully.',
)
@@ -214,7 +214,7 @@ describe('versions', () => {
await page.locator('#field-description').fill(description)
await page.locator('.form-submit .edit-many__publish').click()
await expect(page.locator('.Toastify__toast--success')).toContainText(
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
'Draft Posts successfully.',
)
@@ -241,7 +241,7 @@ describe('versions', () => {
await page.locator('#field-description').fill(description)
await page.locator('.form-submit .edit-many__draft').click()
await expect(page.locator('.Toastify__toast--success')).toContainText(
await expect(page.locator('.payload-toast-container .toast-success')).toContainText(
'Draft Posts successfully.',
)