From cb3355b30f0d26993c17dfecb7e264845deeb8b3 Mon Sep 17 00:00:00 2001 From: Alessio Gravili Date: Tue, 11 Jun 2024 14:12:59 -0400 Subject: [PATCH] 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 --- packages/next/package.json | 2 +- packages/next/src/layouts/Root/index.tsx | 1 - packages/next/src/scss/app.scss | 2 +- packages/next/src/scss/toastify.scss | 58 --------- packages/next/src/scss/toasts.scss | 111 ++++++++++++++++++ packages/next/src/views/API/index.client.tsx | 2 +- .../src/views/Edit/Default/Auth/index.tsx | 4 +- .../ForgotPasswordForm/index.tsx | 2 +- .../src/views/ResetPassword/index.client.tsx | 4 +- .../next/src/views/Version/Restore/index.tsx | 2 +- packages/richtext-lexical/src/scss/app.scss | 2 +- .../richtext-lexical/src/scss/toastify.scss | 58 --------- .../richtext-lexical/src/scss/toasts.scss | 111 ++++++++++++++++++ packages/richtext-slate/src/scss/app.scss | 2 +- .../richtext-slate/src/scss/toastify.scss | 58 --------- packages/richtext-slate/src/scss/toasts.scss | 111 ++++++++++++++++++ packages/translations/src/clientKeys.ts | 1 + packages/translations/src/languages/ar.ts | 1 + packages/translations/src/languages/az.ts | 1 + packages/translations/src/languages/bg.ts | 1 + packages/translations/src/languages/cs.ts | 1 + packages/translations/src/languages/de.ts | 15 +-- packages/translations/src/languages/en.ts | 1 + packages/translations/src/languages/es.ts | 1 + packages/translations/src/languages/fa.ts | 1 + packages/translations/src/languages/fr.ts | 1 + packages/translations/src/languages/he.ts | 1 + packages/translations/src/languages/hr.ts | 1 + packages/translations/src/languages/hu.ts | 1 + packages/translations/src/languages/it.ts | 1 + packages/translations/src/languages/ja.ts | 1 + packages/translations/src/languages/ko.ts | 1 + packages/translations/src/languages/my.ts | 1 + packages/translations/src/languages/nb.ts | 1 + packages/translations/src/languages/nl.ts | 1 + packages/translations/src/languages/pl.ts | 1 + packages/translations/src/languages/pt.ts | 1 + packages/translations/src/languages/ro.ts | 1 + packages/translations/src/languages/rs.ts | 1 + .../translations/src/languages/rsLatin.ts | 1 + packages/translations/src/languages/ru.ts | 1 + packages/translations/src/languages/sk.ts | 1 + packages/translations/src/languages/sv.ts | 1 + packages/translations/src/languages/th.ts | 1 + packages/translations/src/languages/tr.ts | 1 + packages/translations/src/languages/uk.ts | 1 + packages/translations/src/languages/vi.ts | 1 + packages/translations/src/languages/zh.ts | 1 + packages/translations/src/languages/zhTw.ts | 1 + packages/ui/package.json | 2 +- packages/ui/src/elements/Autosave/index.tsx | 2 +- .../ui/src/elements/DeleteDocument/index.tsx | 2 +- packages/ui/src/elements/DeleteMany/index.tsx | 4 +- .../elements/DocumentDrawer/DrawerContent.tsx | 2 +- .../src/elements/DuplicateDocument/index.tsx | 6 +- .../elements/GenerateConfirmation/index.tsx | 4 +- .../elements/PreviewButton/usePreviewURL.tsx | 2 +- .../ui/src/elements/PublishMany/index.tsx | 2 +- packages/ui/src/elements/Status/index.tsx | 2 +- .../ui/src/elements/UnpublishMany/index.tsx | 3 +- packages/ui/src/exports/elements.ts | 8 +- packages/ui/src/forms/Form/index.tsx | 52 ++++++-- packages/ui/src/providers/Auth/index.tsx | 2 +- packages/ui/src/providers/Root/index.tsx | 4 +- .../providers/ToastContainer/icons/Error.tsx | 18 +++ .../providers/ToastContainer/icons/Info.tsx | 18 +++ .../ToastContainer/icons/Success.tsx | 18 +++ .../ToastContainer/icons/Warning.tsx | 18 +++ .../ui/src/providers/ToastContainer/index.tsx | 40 +++++++ packages/ui/src/scss/app.scss | 2 +- packages/ui/src/scss/toastify.scss | 58 --------- packages/ui/src/scss/toasts.scss | 111 ++++++++++++++++++ pnpm-lock.yaml | 33 +++--- test/access-control/e2e.spec.ts | 14 +-- test/admin/e2e/1/e2e.spec.ts | 4 +- test/fields-relationship/e2e.spec.ts | 8 +- test/fields/collections/Array/e2e.spec.ts | 4 +- test/fields/collections/Blocks/e2e.spec.ts | 4 +- .../Lexical/e2e/blocks/e2e.spec.ts | 8 +- test/fields/collections/Number/e2e.spec.ts | 4 +- .../collections/Relationship/e2e.spec.ts | 20 ++-- test/fields/collections/Upload/e2e.spec.ts | 4 +- test/fields/e2e.spec.ts | 8 +- test/helpers.ts | 6 +- test/localization/e2e.spec.ts | 6 +- test/uploads/e2e.spec.ts | 14 ++- test/versions/e2e.spec.ts | 6 +- 87 files changed, 747 insertions(+), 353 deletions(-) delete mode 100644 packages/next/src/scss/toastify.scss create mode 100644 packages/next/src/scss/toasts.scss delete mode 100644 packages/richtext-lexical/src/scss/toastify.scss create mode 100644 packages/richtext-lexical/src/scss/toasts.scss delete mode 100644 packages/richtext-slate/src/scss/toastify.scss create mode 100644 packages/richtext-slate/src/scss/toasts.scss create mode 100644 packages/ui/src/providers/ToastContainer/icons/Error.tsx create mode 100644 packages/ui/src/providers/ToastContainer/icons/Info.tsx create mode 100644 packages/ui/src/providers/ToastContainer/icons/Success.tsx create mode 100644 packages/ui/src/providers/ToastContainer/icons/Warning.tsx create mode 100644 packages/ui/src/providers/ToastContainer/index.tsx delete mode 100644 packages/ui/src/scss/toastify.scss create mode 100644 packages/ui/src/scss/toasts.scss diff --git a/packages/next/package.json b/packages/next/package.json index 239d65d91..04a358b02 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -54,8 +54,8 @@ "path-to-regexp": "^6.2.1", "qs": "6.11.2", "react-diff-viewer-continued": "3.2.6", - "react-toastify": "10.0.5", "sass": "1.77.4", + "sonner": "^1.5.0", "ws": "^8.16.0" }, "devDependencies": { diff --git a/packages/next/src/layouts/Root/index.tsx b/packages/next/src/layouts/Root/index.tsx index 118adb89e..1eaabe8b6 100644 --- a/packages/next/src/layouts/Root/index.tsx +++ b/packages/next/src/layouts/Root/index.tsx @@ -11,7 +11,6 @@ import { headers as getHeaders, cookies as nextCookies } from 'next/headers.js' import { parseCookies } from 'payload/auth' import { createClientConfig } from 'payload/config' import React from 'react' -import 'react-toastify/dist/ReactToastify.css' import { getPayloadHMR } from '../../utilities/getPayloadHMR.js' import { getRequestLanguage } from '../../utilities/getRequestLanguage.js' diff --git a/packages/next/src/scss/app.scss b/packages/next/src/scss/app.scss index c87413e4f..ee80f80fd 100644 --- a/packages/next/src/scss/app.scss +++ b/packages/next/src/scss/app.scss @@ -1,5 +1,5 @@ @import './styles.scss'; -@import './toastify.scss'; +@import './toasts.scss'; @import './colors.scss'; :root { diff --git a/packages/next/src/scss/toastify.scss b/packages/next/src/scss/toastify.scss deleted file mode 100644 index b387cb63d..000000000 --- a/packages/next/src/scss/toastify.scss +++ /dev/null @@ -1,58 +0,0 @@ -@import 'vars'; - -.Toastify { - .Toastify__toast-container { - left: base(5); - transform: none; - right: base(5); - width: auto; - } - - .Toastify__toast { - padding: base(0.5); - border-radius: $style-radius-m; - font-weight: 600; - } - - .Toastify__close-button { - align-self: center; - opacity: 0.7; - - &:hover { - opacity: 1; - } - } - - .Toastify__toast--success { - color: var(--color-success-900); - background: var(--color-success-500); - - .Toastify__progress-bar { - background-color: var(--color-success-900); - } - } - - .Toastify__close-button--success { - color: var(--color-success-900); - } - - .Toastify__toast--error { - background: var(--theme-error-500); - color: #fff; - - .Toastify__progress-bar { - background-color: #fff; - } - } - - .Toastify__close-button--light { - color: inherit; - } - - @include mid-break { - .Toastify__toast-container { - left: $baseline; - right: $baseline; - } - } -} diff --git a/packages/next/src/scss/toasts.scss b/packages/next/src/scss/toasts.scss new file mode 100644 index 000000000..15bc94721 --- /dev/null +++ b/packages/next/src/scss/toasts.scss @@ -0,0 +1,111 @@ +.payload-toast-container { + .payload-toast-close-button { + left: unset; + right: 0.5rem; + top: 1.5rem; + color: var(--theme-elevation-400); + background: unset; + border: none; + display: flex; + width: 1.25rem; + height: 1.25rem; + justify-content: center; + align-items: center; + + &:hover { + background: none; + } + + svg { + width: 2rem; + height: 2rem; + } + + [dir='RTL'] & { + right: unset; + left: 0.5rem; + } + } + + .payload-toast-item { + padding: 1rem; + color: var(--theme-text); + font-style: normal; + font-weight: 600; + display: flex; + gap: 1rem; + align-items: center; + width: 100%; + border-radius: 0.15rem; + border: 1px solid var(--theme-border-color); + background: var(--theme-input-bg); + box-shadow: + 0px 10px 4px -8px rgba(0, 2, 4, 0.02), + 0px 2px 3px 0px rgba(0, 2, 4, 0.05); + + .toast-content { + transition: opacity 100ms cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + &[data-front='false'] { + .toast-content { + opacity: 0; + } + } + + &[data-expanded='true'] { + .toast-content { + opacity: 1; + } + } + + .toast-icon { + svg { + width: 2.4rem; + height: 2.4rem; + } + } + + &.toast-warning { + border-color: var(--theme-warning-200); + background-color: var(--theme-warning-100); + } + + &.toast-error { + border-color: var(--theme-error-300); + background-color: var(--theme-error-150); + } + + &.toast-success { + border-color: var(--theme-success-200); + background-color: var(--theme-success-100); + } + + &.toast-info { + border-color: var(--theme-elevation-250); + background-color: var(--theme-elevation-100); + } + + [data-theme='light'] & { + &.toast-warning { + border-color: var(--theme-warning-550); + background-color: var(--theme-warning-100); + } + + &.toast-error { + border-color: var(--theme-error-200); + background-color: var(--theme-error-50); + } + + &.toast-success { + border-color: var(--theme-success-550); + background-color: var(--theme-success-50); + } + + &.toast-info { + border-color: var(--theme-border-color); + background-color: var(--theme-elevation-50); + } + } + } +} diff --git a/packages/next/src/views/API/index.client.tsx b/packages/next/src/views/API/index.client.tsx index 862081a1e..f08c61369 100644 --- a/packages/next/src/views/API/index.client.tsx +++ b/packages/next/src/views/API/index.client.tsx @@ -15,7 +15,7 @@ import { useTranslation } from '@payloadcms/ui/providers/Translation' import { useSearchParams } from 'next/navigation.js' import qs from 'qs' import * as React from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { SetDocumentStepNav } from '../Edit/Default/SetDocumentStepNav/index.js' import { LocaleSelector } from './LocaleSelector/index.js' diff --git a/packages/next/src/views/Edit/Default/Auth/index.tsx b/packages/next/src/views/Edit/Default/Auth/index.tsx index 71fbaa9d2..4de8eb743 100644 --- a/packages/next/src/views/Edit/Default/Auth/index.tsx +++ b/packages/next/src/views/Edit/Default/Auth/index.tsx @@ -10,7 +10,7 @@ import { useConfig } from '@payloadcms/ui/providers/Config' import { useDocumentInfo } from '@payloadcms/ui/providers/DocumentInfo' import { useTranslation } from '@payloadcms/ui/providers/Translation' import React, { useCallback, useEffect, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import type { Props } from './types.js' @@ -71,7 +71,7 @@ export const Auth: React.FC = (props) => { }) if (response.status === 200) { - toast.success(t('authentication:successfullyUnlocked'), { autoClose: 3000 }) + toast.success(t('authentication:successfullyUnlocked')) } else { toast.error(t('authentication:failedToUnlock')) } diff --git a/packages/next/src/views/ForgotPassword/ForgotPasswordForm/index.tsx b/packages/next/src/views/ForgotPassword/ForgotPasswordForm/index.tsx index 7345d6fad..85fb4fd64 100644 --- a/packages/next/src/views/ForgotPassword/ForgotPasswordForm/index.tsx +++ b/packages/next/src/views/ForgotPassword/ForgotPasswordForm/index.tsx @@ -9,7 +9,7 @@ import { useConfig } from '@payloadcms/ui/providers/Config' import { useTranslation } from '@payloadcms/ui/providers/Translation' import { email } from 'payload/fields/validations' import React, { Fragment, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' export const ForgotPasswordForm: React.FC = () => { const config = useConfig() diff --git a/packages/next/src/views/ResetPassword/index.client.tsx b/packages/next/src/views/ResetPassword/index.client.tsx index f538e3eab..abce3d64a 100644 --- a/packages/next/src/views/ResetPassword/index.client.tsx +++ b/packages/next/src/views/ResetPassword/index.client.tsx @@ -11,7 +11,7 @@ import { useConfig } from '@payloadcms/ui/providers/Config' import { useTranslation } from '@payloadcms/ui/providers/Translation' import { useRouter } from 'next/navigation.js' import React from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' type Args = { token: string @@ -49,7 +49,7 @@ export const ResetPasswordClient: React.FC = ({ token }) => { history.push(`${admin}`) } else { history.push(`${admin}/login`) - toast.success(i18n.t('general:updatedSuccessfully'), { autoClose: 3000 }) + toast.success(i18n.t('general:updatedSuccessfully')) } }, [fetchFullUser, history, admin, i18n], diff --git a/packages/next/src/views/Version/Restore/index.tsx b/packages/next/src/views/Version/Restore/index.tsx index 7c0d547f8..3b94e2d2a 100644 --- a/packages/next/src/views/Version/Restore/index.tsx +++ b/packages/next/src/views/Version/Restore/index.tsx @@ -9,7 +9,7 @@ import { MinimalTemplate } from '@payloadcms/ui/templates/Minimal' import { requests } from '@payloadcms/ui/utilities/api' import { useRouter } from 'next/navigation.js' import React, { Fragment, useCallback, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import type { Props } from './types.js' diff --git a/packages/richtext-lexical/src/scss/app.scss b/packages/richtext-lexical/src/scss/app.scss index d2424f66e..e32b856c5 100644 --- a/packages/richtext-lexical/src/scss/app.scss +++ b/packages/richtext-lexical/src/scss/app.scss @@ -1,5 +1,5 @@ @import 'styles'; -@import './toastify.scss'; +@import './toasts.scss'; @import './colors.scss'; :root { diff --git a/packages/richtext-lexical/src/scss/toastify.scss b/packages/richtext-lexical/src/scss/toastify.scss deleted file mode 100644 index b387cb63d..000000000 --- a/packages/richtext-lexical/src/scss/toastify.scss +++ /dev/null @@ -1,58 +0,0 @@ -@import 'vars'; - -.Toastify { - .Toastify__toast-container { - left: base(5); - transform: none; - right: base(5); - width: auto; - } - - .Toastify__toast { - padding: base(0.5); - border-radius: $style-radius-m; - font-weight: 600; - } - - .Toastify__close-button { - align-self: center; - opacity: 0.7; - - &:hover { - opacity: 1; - } - } - - .Toastify__toast--success { - color: var(--color-success-900); - background: var(--color-success-500); - - .Toastify__progress-bar { - background-color: var(--color-success-900); - } - } - - .Toastify__close-button--success { - color: var(--color-success-900); - } - - .Toastify__toast--error { - background: var(--theme-error-500); - color: #fff; - - .Toastify__progress-bar { - background-color: #fff; - } - } - - .Toastify__close-button--light { - color: inherit; - } - - @include mid-break { - .Toastify__toast-container { - left: $baseline; - right: $baseline; - } - } -} diff --git a/packages/richtext-lexical/src/scss/toasts.scss b/packages/richtext-lexical/src/scss/toasts.scss new file mode 100644 index 000000000..15bc94721 --- /dev/null +++ b/packages/richtext-lexical/src/scss/toasts.scss @@ -0,0 +1,111 @@ +.payload-toast-container { + .payload-toast-close-button { + left: unset; + right: 0.5rem; + top: 1.5rem; + color: var(--theme-elevation-400); + background: unset; + border: none; + display: flex; + width: 1.25rem; + height: 1.25rem; + justify-content: center; + align-items: center; + + &:hover { + background: none; + } + + svg { + width: 2rem; + height: 2rem; + } + + [dir='RTL'] & { + right: unset; + left: 0.5rem; + } + } + + .payload-toast-item { + padding: 1rem; + color: var(--theme-text); + font-style: normal; + font-weight: 600; + display: flex; + gap: 1rem; + align-items: center; + width: 100%; + border-radius: 0.15rem; + border: 1px solid var(--theme-border-color); + background: var(--theme-input-bg); + box-shadow: + 0px 10px 4px -8px rgba(0, 2, 4, 0.02), + 0px 2px 3px 0px rgba(0, 2, 4, 0.05); + + .toast-content { + transition: opacity 100ms cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + &[data-front='false'] { + .toast-content { + opacity: 0; + } + } + + &[data-expanded='true'] { + .toast-content { + opacity: 1; + } + } + + .toast-icon { + svg { + width: 2.4rem; + height: 2.4rem; + } + } + + &.toast-warning { + border-color: var(--theme-warning-200); + background-color: var(--theme-warning-100); + } + + &.toast-error { + border-color: var(--theme-error-300); + background-color: var(--theme-error-150); + } + + &.toast-success { + border-color: var(--theme-success-200); + background-color: var(--theme-success-100); + } + + &.toast-info { + border-color: var(--theme-elevation-250); + background-color: var(--theme-elevation-100); + } + + [data-theme='light'] & { + &.toast-warning { + border-color: var(--theme-warning-550); + background-color: var(--theme-warning-100); + } + + &.toast-error { + border-color: var(--theme-error-200); + background-color: var(--theme-error-50); + } + + &.toast-success { + border-color: var(--theme-success-550); + background-color: var(--theme-success-50); + } + + &.toast-info { + border-color: var(--theme-border-color); + background-color: var(--theme-elevation-50); + } + } + } +} diff --git a/packages/richtext-slate/src/scss/app.scss b/packages/richtext-slate/src/scss/app.scss index d2424f66e..e32b856c5 100644 --- a/packages/richtext-slate/src/scss/app.scss +++ b/packages/richtext-slate/src/scss/app.scss @@ -1,5 +1,5 @@ @import 'styles'; -@import './toastify.scss'; +@import './toasts.scss'; @import './colors.scss'; :root { diff --git a/packages/richtext-slate/src/scss/toastify.scss b/packages/richtext-slate/src/scss/toastify.scss deleted file mode 100644 index b387cb63d..000000000 --- a/packages/richtext-slate/src/scss/toastify.scss +++ /dev/null @@ -1,58 +0,0 @@ -@import 'vars'; - -.Toastify { - .Toastify__toast-container { - left: base(5); - transform: none; - right: base(5); - width: auto; - } - - .Toastify__toast { - padding: base(0.5); - border-radius: $style-radius-m; - font-weight: 600; - } - - .Toastify__close-button { - align-self: center; - opacity: 0.7; - - &:hover { - opacity: 1; - } - } - - .Toastify__toast--success { - color: var(--color-success-900); - background: var(--color-success-500); - - .Toastify__progress-bar { - background-color: var(--color-success-900); - } - } - - .Toastify__close-button--success { - color: var(--color-success-900); - } - - .Toastify__toast--error { - background: var(--theme-error-500); - color: #fff; - - .Toastify__progress-bar { - background-color: #fff; - } - } - - .Toastify__close-button--light { - color: inherit; - } - - @include mid-break { - .Toastify__toast-container { - left: $baseline; - right: $baseline; - } - } -} diff --git a/packages/richtext-slate/src/scss/toasts.scss b/packages/richtext-slate/src/scss/toasts.scss new file mode 100644 index 000000000..15bc94721 --- /dev/null +++ b/packages/richtext-slate/src/scss/toasts.scss @@ -0,0 +1,111 @@ +.payload-toast-container { + .payload-toast-close-button { + left: unset; + right: 0.5rem; + top: 1.5rem; + color: var(--theme-elevation-400); + background: unset; + border: none; + display: flex; + width: 1.25rem; + height: 1.25rem; + justify-content: center; + align-items: center; + + &:hover { + background: none; + } + + svg { + width: 2rem; + height: 2rem; + } + + [dir='RTL'] & { + right: unset; + left: 0.5rem; + } + } + + .payload-toast-item { + padding: 1rem; + color: var(--theme-text); + font-style: normal; + font-weight: 600; + display: flex; + gap: 1rem; + align-items: center; + width: 100%; + border-radius: 0.15rem; + border: 1px solid var(--theme-border-color); + background: var(--theme-input-bg); + box-shadow: + 0px 10px 4px -8px rgba(0, 2, 4, 0.02), + 0px 2px 3px 0px rgba(0, 2, 4, 0.05); + + .toast-content { + transition: opacity 100ms cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + &[data-front='false'] { + .toast-content { + opacity: 0; + } + } + + &[data-expanded='true'] { + .toast-content { + opacity: 1; + } + } + + .toast-icon { + svg { + width: 2.4rem; + height: 2.4rem; + } + } + + &.toast-warning { + border-color: var(--theme-warning-200); + background-color: var(--theme-warning-100); + } + + &.toast-error { + border-color: var(--theme-error-300); + background-color: var(--theme-error-150); + } + + &.toast-success { + border-color: var(--theme-success-200); + background-color: var(--theme-success-100); + } + + &.toast-info { + border-color: var(--theme-elevation-250); + background-color: var(--theme-elevation-100); + } + + [data-theme='light'] & { + &.toast-warning { + border-color: var(--theme-warning-550); + background-color: var(--theme-warning-100); + } + + &.toast-error { + border-color: var(--theme-error-200); + background-color: var(--theme-error-50); + } + + &.toast-success { + border-color: var(--theme-success-550); + background-color: var(--theme-success-50); + } + + &.toast-info { + border-color: var(--theme-border-color); + background-color: var(--theme-elevation-50); + } + } + } +} diff --git a/packages/translations/src/clientKeys.ts b/packages/translations/src/clientKeys.ts index b1c973552..797f814f8 100644 --- a/packages/translations/src/clientKeys.ts +++ b/packages/translations/src/clientKeys.ts @@ -213,6 +213,7 @@ export const clientTranslationKeys = createClientTranslationKeys([ 'general:stayOnThisPage', 'general:submissionSuccessful', 'general:submit', + 'general:submitting', 'general:success', 'general:successfullyCreated', 'general:successfullyDuplicated', diff --git a/packages/translations/src/languages/ar.ts b/packages/translations/src/languages/ar.ts index 6135e8d61..801a767b0 100644 --- a/packages/translations/src/languages/ar.ts +++ b/packages/translations/src/languages/ar.ts @@ -271,6 +271,7 @@ export const arTranslations: DefaultTranslationsObject = { stayOnThisPage: 'البقاء على هذه الصفحة', submissionSuccessful: 'تمت الإرسال بنجاح.', submit: 'إرسال', + submitting: 'جاري التقديم...', success: 'النجاح', successfullyCreated: '{{label}} تم إنشاؤها بنجاح.', successfullyDuplicated: '{{label}} تم استنساخها بنجاح.', diff --git a/packages/translations/src/languages/az.ts b/packages/translations/src/languages/az.ts index 3d009132a..03fc95981 100644 --- a/packages/translations/src/languages/az.ts +++ b/packages/translations/src/languages/az.ts @@ -273,6 +273,7 @@ export const azTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Bu səhifədə qal', submissionSuccessful: 'Təqdimat uğurlu oldu.', submit: 'Təqdim et', + submitting: 'Təqdim olunur...', success: 'Uğur', successfullyCreated: '{{label}} uğurla yaradıldı.', successfullyDuplicated: '{{label}} uğurla dublikatlandı.', diff --git a/packages/translations/src/languages/bg.ts b/packages/translations/src/languages/bg.ts index b3f73a552..f4fea1c5c 100644 --- a/packages/translations/src/languages/bg.ts +++ b/packages/translations/src/languages/bg.ts @@ -272,6 +272,7 @@ export const bgTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Остани на тази страница', submissionSuccessful: 'Успешно подаване.', submit: 'Подай', + submitting: 'Подаване...', success: 'Успех', successfullyCreated: '{{label}} успешно създаден.', successfullyDuplicated: '{{label}} успешно дупликиран.', diff --git a/packages/translations/src/languages/cs.ts b/packages/translations/src/languages/cs.ts index 327c4cf87..5eed17a92 100644 --- a/packages/translations/src/languages/cs.ts +++ b/packages/translations/src/languages/cs.ts @@ -272,6 +272,7 @@ export const csTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Zůstat na této stránce', submissionSuccessful: 'Odeslání úspěšné.', submit: 'Odeslat', + submitting: 'Odesílání...', success: 'Úspěch', successfullyCreated: '{{label}} úspěšně vytvořeno.', successfullyDuplicated: '{{label}} úspěšně duplikováno.', diff --git a/packages/translations/src/languages/de.ts b/packages/translations/src/languages/de.ts index a12b3430e..5e49ebd7d 100644 --- a/packages/translations/src/languages/de.ts +++ b/packages/translations/src/languages/de.ts @@ -235,8 +235,8 @@ export const deTranslations: DefaultTranslationsObject = { light: 'Hell', livePreview: 'Vorschau', loading: 'Lädt', - locale: 'Sprachumgebung', - locales: 'Sprachumgebungen', + locale: 'Sprache', + locales: 'Sprachen', menu: 'Menü', moveDown: 'Nach unten bewegen', moveUp: 'Nach oben bewegen', @@ -276,6 +276,7 @@ export const deTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Auf dieser Seite bleiben', submissionSuccessful: 'Einrichung erfolgreich.', submit: 'Senden', + submitting: 'Wird aktualisiert...', success: 'Erfolg', successfullyCreated: '{{label}} erfolgreich erstellt.', successfullyDuplicated: '{{label}} wurde erfolgreich dupliziert.', @@ -389,13 +390,13 @@ export const deTranslations: DefaultTranslationsObject = { publishing: 'Veröffentlichung', restoreThisVersion: 'Diese Version wiederherstellen', restoredSuccessfully: 'Erfolgreich wiederhergestellt.', - restoring: 'wiederherstellen...', - revertToPublished: 'Auf Veröffentlicht zurücksetzen', - reverting: 'zurücksetzen...', + restoring: 'Wiederherstellen...', + revertToPublished: 'Auf veröffentlichte Version zurücksetzen', + reverting: 'Zurücksetzen...', saveDraft: 'Entwurf speichern', - selectLocales: 'Wähle anzuzeigende Sprachumgebungen', + selectLocales: 'Wähle anzuzeigende Sprachen', selectVersionToCompare: 'Wähle Version zum Vergleich', - showLocales: 'Sprachumgebungen anzeigen:', + showLocales: 'Sprachen anzeigen:', showingVersionsFor: 'Versionen anzeigen für:', status: 'Status', unpublish: 'Auf Entwurf setzen', diff --git a/packages/translations/src/languages/en.ts b/packages/translations/src/languages/en.ts index eebacb5af..327ffffd7 100644 --- a/packages/translations/src/languages/en.ts +++ b/packages/translations/src/languages/en.ts @@ -273,6 +273,7 @@ export const enTranslations = { stayOnThisPage: 'Stay on this page', submissionSuccessful: 'Submission Successful.', submit: 'Submit', + submitting: 'Submitting...', success: 'Success', successfullyCreated: '{{label}} successfully created.', successfullyDuplicated: '{{label}} successfully duplicated.', diff --git a/packages/translations/src/languages/es.ts b/packages/translations/src/languages/es.ts index b0fe32a82..a20554216 100644 --- a/packages/translations/src/languages/es.ts +++ b/packages/translations/src/languages/es.ts @@ -275,6 +275,7 @@ export const esTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Permanecer en esta página', submissionSuccessful: 'Envío realizado correctamente.', submit: 'Enviar', + submitting: 'Enviando...', success: 'Éxito', successfullyCreated: '{{label}} creado correctamente.', successfullyDuplicated: '{{label}} duplicado correctamente.', diff --git a/packages/translations/src/languages/fa.ts b/packages/translations/src/languages/fa.ts index fe2c9e952..668b7d35b 100644 --- a/packages/translations/src/languages/fa.ts +++ b/packages/translations/src/languages/fa.ts @@ -272,6 +272,7 @@ export const faTranslations: DefaultTranslationsObject = { stayOnThisPage: 'ماندن در این برگه', submissionSuccessful: 'با موفقیت ثبت شد.', submit: 'فرستادن', + submitting: 'در حال ارسال...', success: 'موفقیت', successfullyCreated: '{{label}} با موفقیت ساخته شد.', successfullyDuplicated: '{{label}} با موفقیت رونوشت شد.', diff --git a/packages/translations/src/languages/fr.ts b/packages/translations/src/languages/fr.ts index 3ca67e07f..fdeded420 100644 --- a/packages/translations/src/languages/fr.ts +++ b/packages/translations/src/languages/fr.ts @@ -279,6 +279,7 @@ export const frTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Rester sur cette page', submissionSuccessful: 'Soumission réussie.', submit: 'Soumettre', + submitting: 'Soumission...', success: 'Succès', successfullyCreated: '{{label}} créé(e) avec succès.', successfullyDuplicated: '{{label}} dupliqué(e) avec succès.', diff --git a/packages/translations/src/languages/he.ts b/packages/translations/src/languages/he.ts index 7ac80695f..4f18cfad6 100644 --- a/packages/translations/src/languages/he.ts +++ b/packages/translations/src/languages/he.ts @@ -267,6 +267,7 @@ export const heTranslations: DefaultTranslationsObject = { stayOnThisPage: 'הישאר בדף זה', submissionSuccessful: 'נשלח בהצלחה.', submit: 'שלח', + submitting: 'מגיש...', success: 'הצלחה', successfullyCreated: '{{label}} נוצר בהצלחה.', successfullyDuplicated: '{{label}} שוכפל בהצלחה.', diff --git a/packages/translations/src/languages/hr.ts b/packages/translations/src/languages/hr.ts index a0435b1eb..8ca69f05a 100644 --- a/packages/translations/src/languages/hr.ts +++ b/packages/translations/src/languages/hr.ts @@ -273,6 +273,7 @@ export const hrTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Ostani na ovoj stranici', submissionSuccessful: 'Uspješno slanje', submit: 'Podnesi', + submitting: 'Podnošenje...', success: 'Uspjeh', successfullyCreated: '{{label}} uspješno kreirano.', successfullyDuplicated: '{{label}} uspješno duplicirano.', diff --git a/packages/translations/src/languages/hu.ts b/packages/translations/src/languages/hu.ts index a817c229c..42fa56993 100644 --- a/packages/translations/src/languages/hu.ts +++ b/packages/translations/src/languages/hu.ts @@ -275,6 +275,7 @@ export const huTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Maradjon ezen az oldalon', submissionSuccessful: 'Beküldés sikeres.', submit: 'Beküldés', + submitting: 'Beküldés...', success: 'Siker', successfullyCreated: '{{label}} sikeresen létrehozva.', successfullyDuplicated: '{{label}} sikeresen duplikálódott.', diff --git a/packages/translations/src/languages/it.ts b/packages/translations/src/languages/it.ts index cf32528bc..0737cd5b0 100644 --- a/packages/translations/src/languages/it.ts +++ b/packages/translations/src/languages/it.ts @@ -275,6 +275,7 @@ export const itTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Rimani su questa pagina', submissionSuccessful: 'Invio riuscito.', submit: 'Invia', + submitting: 'Inviando...', success: 'Successo', successfullyCreated: '{{label}} creato con successo.', successfullyDuplicated: '{{label}} duplicato con successo.', diff --git a/packages/translations/src/languages/ja.ts b/packages/translations/src/languages/ja.ts index b09485f8e..3342a869e 100644 --- a/packages/translations/src/languages/ja.ts +++ b/packages/translations/src/languages/ja.ts @@ -273,6 +273,7 @@ export const jaTranslations: DefaultTranslationsObject = { stayOnThisPage: 'この画面にとどまる', submissionSuccessful: '送信が成功しました。', submit: '送信', + submitting: '提出中...', success: '成功', successfullyCreated: '{{label}} が作成されました。', successfullyDuplicated: '{{label}} が複製されました。', diff --git a/packages/translations/src/languages/ko.ts b/packages/translations/src/languages/ko.ts index dd2d6a408..e8d68f3e0 100644 --- a/packages/translations/src/languages/ko.ts +++ b/packages/translations/src/languages/ko.ts @@ -272,6 +272,7 @@ export const koTranslations: DefaultTranslationsObject = { stayOnThisPage: '이 페이지에 머무르기', submissionSuccessful: '제출이 완료되었습니다.', submit: '제출', + submitting: '제출 중...', success: '성공', successfullyCreated: '{{label}}이(가) 생성되었습니다.', successfullyDuplicated: '{{label}}이(가) 복제되었습니다.', diff --git a/packages/translations/src/languages/my.ts b/packages/translations/src/languages/my.ts index 71b5e55f1..ddfaf07fd 100644 --- a/packages/translations/src/languages/my.ts +++ b/packages/translations/src/languages/my.ts @@ -275,6 +275,7 @@ export const myTranslations: DefaultTranslationsObject = { stayOnThisPage: 'ဒီမှာပဲ ဆက်နေမည်။', submissionSuccessful: 'သိမ်းဆည်းမှု အောင်မြင်ပါသည်။', submit: 'သိမ်းဆည်းမည်။', + submitting: 'Menghantar...', success: 'အောင်မြင်မှု', successfullyCreated: '{{label}} အောင်မြင်စွာဖန်တီးခဲ့သည်။', successfullyDuplicated: '{{label}} အောင်မြင်စွာ ပုံတူပွားခဲ့သည်။', diff --git a/packages/translations/src/languages/nb.ts b/packages/translations/src/languages/nb.ts index a343d1a07..c0dd9d080 100644 --- a/packages/translations/src/languages/nb.ts +++ b/packages/translations/src/languages/nb.ts @@ -273,6 +273,7 @@ export const nbTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Bli på denne siden', submissionSuccessful: 'Innsending vellykket.', submit: 'Send inn', + submitting: 'Innsending...', success: 'Suksess', successfullyCreated: '{{label}} ble opprettet.', successfullyDuplicated: '{{label}} ble duplisert.', diff --git a/packages/translations/src/languages/nl.ts b/packages/translations/src/languages/nl.ts index 7b65a5eaa..aff87dd4b 100644 --- a/packages/translations/src/languages/nl.ts +++ b/packages/translations/src/languages/nl.ts @@ -275,6 +275,7 @@ export const nlTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Blijf op deze pagina', submissionSuccessful: 'Indiening succesvol.', submit: 'Indienen', + submitting: 'Inzenden...', success: 'Succes', successfullyCreated: '{{label}} succesvol aangemaakt.', successfullyDuplicated: '{{label}} succesvol gedupliceerd.', diff --git a/packages/translations/src/languages/pl.ts b/packages/translations/src/languages/pl.ts index 00d8e8793..c8453aac6 100644 --- a/packages/translations/src/languages/pl.ts +++ b/packages/translations/src/languages/pl.ts @@ -273,6 +273,7 @@ export const plTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Pozostań na stronie', submissionSuccessful: 'Zgłoszenie zakończone powodzeniem.', submit: 'Zatwierdź', + submitting: 'Przesyłanie...', success: 'Sukces', successfullyCreated: 'Pomyślnie utworzono {{label}}.', successfullyDuplicated: 'Pomyślnie zduplikowano {{label}}', diff --git a/packages/translations/src/languages/pt.ts b/packages/translations/src/languages/pt.ts index b265d58f0..6b2556670 100644 --- a/packages/translations/src/languages/pt.ts +++ b/packages/translations/src/languages/pt.ts @@ -274,6 +274,7 @@ export const ptTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Permanecer nessa página', submissionSuccessful: 'Envio bem-sucedido.', submit: 'Enviar', + submitting: 'Enviando...', success: 'Sucesso', successfullyCreated: '{{label}} criado com sucesso.', successfullyDuplicated: '{{label}} duplicado com sucesso.', diff --git a/packages/translations/src/languages/ro.ts b/packages/translations/src/languages/ro.ts index 7ca13f8f7..8ab7f9d1d 100644 --- a/packages/translations/src/languages/ro.ts +++ b/packages/translations/src/languages/ro.ts @@ -276,6 +276,7 @@ export const roTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Rămâneți pe această pagină', submissionSuccessful: 'Trimitere cu succes.', submit: 'Trimite', + submitting: 'Se trimite...', success: 'Succes', successfullyCreated: '{{label}} creat(ă) cu succes.', successfullyDuplicated: '{{label}} duplicat(ă) cu succes.', diff --git a/packages/translations/src/languages/rs.ts b/packages/translations/src/languages/rs.ts index f45b37d0b..612534ee5 100644 --- a/packages/translations/src/languages/rs.ts +++ b/packages/translations/src/languages/rs.ts @@ -272,6 +272,7 @@ export const rsTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Остани на овој страници', submissionSuccessful: 'Успешно слање', submit: 'Потврди', + submitting: 'Podnošenje...', success: 'Uspeh', successfullyCreated: '{{label}} успешно креирано.', successfullyDuplicated: '{{label}} успешно дуплицирано.', diff --git a/packages/translations/src/languages/rsLatin.ts b/packages/translations/src/languages/rsLatin.ts index b65fbb963..c2d6a7636 100644 --- a/packages/translations/src/languages/rsLatin.ts +++ b/packages/translations/src/languages/rsLatin.ts @@ -272,6 +272,7 @@ export const rsLatinTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Ostani na ovoj stranici', submissionSuccessful: 'Uspešno slanje', submit: 'Potvrdi', + submitting: 'Podnošenje...', success: 'Uspeh', successfullyCreated: '{{label}} uspešno kreirano.', successfullyDuplicated: '{{label}} uspešno duplicirano.', diff --git a/packages/translations/src/languages/ru.ts b/packages/translations/src/languages/ru.ts index 31d03b960..9a32bbe37 100644 --- a/packages/translations/src/languages/ru.ts +++ b/packages/translations/src/languages/ru.ts @@ -275,6 +275,7 @@ export const ruTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Остаться на этой странице', submissionSuccessful: 'Успешно отправлено.', submit: 'Отправить', + submitting: 'Подача заявления...', success: 'Успех', successfullyCreated: '{{label}} успешно создан.', successfullyDuplicated: '{{label}} успешно продублирован.', diff --git a/packages/translations/src/languages/sk.ts b/packages/translations/src/languages/sk.ts index 8cdfcabc9..5df940d01 100644 --- a/packages/translations/src/languages/sk.ts +++ b/packages/translations/src/languages/sk.ts @@ -274,6 +274,7 @@ export const skTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Zostať na tejto stránke', submissionSuccessful: 'Odoslanie úspešné.', submit: 'Odoslať', + submitting: 'Odosielanie...', success: 'Úspech', successfullyCreated: '{{label}} úspešne vytvorené.', successfullyDuplicated: '{{label}} úspešne duplikované.', diff --git a/packages/translations/src/languages/sv.ts b/packages/translations/src/languages/sv.ts index 74ec2de63..be4ae02f8 100644 --- a/packages/translations/src/languages/sv.ts +++ b/packages/translations/src/languages/sv.ts @@ -273,6 +273,7 @@ export const svTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Stanna på denna sida', submissionSuccessful: 'Inlämningen Lyckades.', submit: 'Lämna in', + submitting: 'Inlämnar...', success: 'Framgång', successfullyCreated: '{{label}} skapades framgångsrikt.', successfullyDuplicated: '{{label}} duplicerades framgångsrikt.', diff --git a/packages/translations/src/languages/th.ts b/packages/translations/src/languages/th.ts index 6c3abcb4b..8f336d849 100644 --- a/packages/translations/src/languages/th.ts +++ b/packages/translations/src/languages/th.ts @@ -269,6 +269,7 @@ export const thTranslations: DefaultTranslationsObject = { stayOnThisPage: 'อยู่หน้านี้ต่อ', submissionSuccessful: 'ส่งสำเร็จ', submit: 'ส่ง', + submitting: 'ส่ง...', success: 'ความสำเร็จ', successfullyCreated: 'สร้าง {{label}} สำเร็จ', successfullyDuplicated: 'สำเนา {{label}} สำเร็จ', diff --git a/packages/translations/src/languages/tr.ts b/packages/translations/src/languages/tr.ts index 57f072266..83f6f7309 100644 --- a/packages/translations/src/languages/tr.ts +++ b/packages/translations/src/languages/tr.ts @@ -276,6 +276,7 @@ export const trTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Bu sayfada kal', submissionSuccessful: 'Gönderme başarılı', submit: 'Gönder', + submitting: 'Gönderiliyor...', success: 'Başarı', successfullyCreated: '{{label}} başarıyla oluşturuldu.', successfullyDuplicated: '{{label}} başarıyla kopyalandı.', diff --git a/packages/translations/src/languages/uk.ts b/packages/translations/src/languages/uk.ts index e745aac26..c19ecd09c 100644 --- a/packages/translations/src/languages/uk.ts +++ b/packages/translations/src/languages/uk.ts @@ -273,6 +273,7 @@ export const ukTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Залишитись на цій сторінці', submissionSuccessful: 'Успішно відправлено.', submit: 'Відправити', + submitting: 'Надсилаємо...', success: 'Успіх', successfullyCreated: '{{label}} успішно створено.', successfullyDuplicated: '{{label}} успішно продубльовано.', diff --git a/packages/translations/src/languages/vi.ts b/packages/translations/src/languages/vi.ts index b77e7dcf8..e57867ba2 100644 --- a/packages/translations/src/languages/vi.ts +++ b/packages/translations/src/languages/vi.ts @@ -271,6 +271,7 @@ export const viTranslations: DefaultTranslationsObject = { stayOnThisPage: 'Ở lại trang này', submissionSuccessful: 'Gửi thành công.', submit: 'Gửi', + submitting: 'Đang gửi...', success: 'Thành công', successfullyCreated: '{{label}} đã được tạo thành công.', successfullyDuplicated: '{{label}} đã được sao chép thành công.', diff --git a/packages/translations/src/languages/zh.ts b/packages/translations/src/languages/zh.ts index 2a2b7e0b7..f59e83b82 100644 --- a/packages/translations/src/languages/zh.ts +++ b/packages/translations/src/languages/zh.ts @@ -265,6 +265,7 @@ export const zhTranslations: DefaultTranslationsObject = { stayOnThisPage: '停留在此页面', submissionSuccessful: '提交成功。', submit: '提交', + submitting: '提交中...', success: '成功', successfullyCreated: '成功创建{{label}}', successfullyDuplicated: '成功复制{{label}}', diff --git a/packages/translations/src/languages/zhTw.ts b/packages/translations/src/languages/zhTw.ts index 0fa3d6d1f..235617a39 100644 --- a/packages/translations/src/languages/zhTw.ts +++ b/packages/translations/src/languages/zhTw.ts @@ -265,6 +265,7 @@ export const zhTwTranslations: DefaultTranslationsObject = { stayOnThisPage: '停留在此頁面', submissionSuccessful: '成功送出。', submit: '送出', + submitting: '提交中...', success: '成功', successfullyCreated: '成功建立{{label}}', successfullyDuplicated: '成功複製{{label}}', diff --git a/packages/ui/package.json b/packages/ui/package.json index 471adc5dc..13a472798 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -113,8 +113,8 @@ "react-datepicker": "6.9.0", "react-image-crop": "10.1.8", "react-select": "5.8.0", - "react-toastify": "10.0.5", "scheduler": "0.25.0-rc-f994737d14-20240522", + "sonner": "^1.5.0", "use-context-selector": "2.0.0", "uuid": "^9.0.1" }, diff --git a/packages/ui/src/elements/Autosave/index.tsx b/packages/ui/src/elements/Autosave/index.tsx index 3913e362b..0c87152da 100644 --- a/packages/ui/src/elements/Autosave/index.tsx +++ b/packages/ui/src/elements/Autosave/index.tsx @@ -4,7 +4,7 @@ import type { ClientCollectionConfig, ClientGlobalConfig } from 'payload/types' import { versionDefaults } from 'payload/versions' import React, { useEffect, useRef, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { useAllFormFields, diff --git a/packages/ui/src/elements/DeleteDocument/index.tsx b/packages/ui/src/elements/DeleteDocument/index.tsx index 5b0ccbb47..c34290b57 100644 --- a/packages/ui/src/elements/DeleteDocument/index.tsx +++ b/packages/ui/src/elements/DeleteDocument/index.tsx @@ -5,7 +5,7 @@ import { Modal, useModal } from '@faceless-ui/modal' import { getTranslation } from '@payloadcms/translations' import { useRouter } from 'next/navigation.js' import React, { useCallback, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { useForm } from '../../forms/Form/context.js' import { useConfig } from '../../providers/Config/index.js' diff --git a/packages/ui/src/elements/DeleteMany/index.tsx b/packages/ui/src/elements/DeleteMany/index.tsx index a986a0dae..6b83c0384 100644 --- a/packages/ui/src/elements/DeleteMany/index.tsx +++ b/packages/ui/src/elements/DeleteMany/index.tsx @@ -5,7 +5,7 @@ import { Modal, useModal } from '@faceless-ui/modal' import { getTranslation } from '@payloadcms/translations' import { useRouter } from 'next/navigation.js' import React, { useCallback, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { useAuth } from '../../providers/Auth/index.js' import { useConfig } from '../../providers/Config/index.js' @@ -65,7 +65,7 @@ export const DeleteMany: React.FC = (props) => { const json = await res.json() toggleModal(modalSlug) if (res.status < 400) { - toast.success(json.message || t('general:deletedSuccessfully'), { autoClose: 3000 }) + toast.success(json.message || t('general:deletedSuccessfully')) toggleAll() router.replace( stringifyParams({ diff --git a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx index 573bd316d..b56ff411c 100644 --- a/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx +++ b/packages/ui/src/elements/DocumentDrawer/DrawerContent.tsx @@ -3,7 +3,7 @@ import { useModal } from '@faceless-ui/modal' import queryString from 'qs' import React, { useCallback, useEffect, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import type { DocumentDrawerProps } from './types.js' diff --git a/packages/ui/src/elements/DuplicateDocument/index.tsx b/packages/ui/src/elements/DuplicateDocument/index.tsx index f3cf79198..a8f2f3c91 100644 --- a/packages/ui/src/elements/DuplicateDocument/index.tsx +++ b/packages/ui/src/elements/DuplicateDocument/index.tsx @@ -6,7 +6,7 @@ import { Modal, useModal } from '@faceless-ui/modal' import { getTranslation } from '@payloadcms/translations' import { useRouter } from 'next/navigation.js' import React, { useCallback, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { useForm, useFormModified } from '../../forms/Form/context.js' import { useConfig } from '../../providers/Config/index.js' @@ -67,9 +67,6 @@ export const DuplicateDocument: React.FC = ({ id, slug, singularLabel }) toast.success( message || t('general:successfullyDuplicated', { label: getTranslation(singularLabel, i18n) }), - { - autoClose: 3000, - }, ) setModified(false) router.push(`${admin}/collections/${slug}/${doc.id}?locale=${locale.code}`) @@ -78,7 +75,6 @@ export const DuplicateDocument: React.FC = ({ id, slug, singularLabel }) errors?.[0].message || message || t('error:unspecific', { label: getTranslation(singularLabel, i18n) }), - { autoClose: 5000 }, ) } }) diff --git a/packages/ui/src/elements/GenerateConfirmation/index.tsx b/packages/ui/src/elements/GenerateConfirmation/index.tsx index 57d495275..9e953e4e5 100644 --- a/packages/ui/src/elements/GenerateConfirmation/index.tsx +++ b/packages/ui/src/elements/GenerateConfirmation/index.tsx @@ -1,7 +1,7 @@ 'use client' import { Modal, useModal } from '@faceless-ui/modal' import React from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { useDocumentInfo } from '../../providers/DocumentInfo/index.js' import { useTranslation } from '../../providers/Translation/index.js' @@ -29,7 +29,7 @@ export const GenerateConfirmation: React.FC = (props) const handleGenerate = () => { setKey() toggleModal(modalSlug) - toast.success(t('authentication:newAPIKeyGenerated'), { autoClose: 3000 }) + toast.success(t('authentication:newAPIKeyGenerated')) highlightField(true) } diff --git a/packages/ui/src/elements/PreviewButton/usePreviewURL.tsx b/packages/ui/src/elements/PreviewButton/usePreviewURL.tsx index 1e683c441..c05cd07af 100644 --- a/packages/ui/src/elements/PreviewButton/usePreviewURL.tsx +++ b/packages/ui/src/elements/PreviewButton/usePreviewURL.tsx @@ -1,6 +1,6 @@ 'use client' import { useCallback, useRef, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { useConfig } from '../../providers/Config/index.js' import { useDocumentInfo } from '../../providers/DocumentInfo/index.js' diff --git a/packages/ui/src/elements/PublishMany/index.tsx b/packages/ui/src/elements/PublishMany/index.tsx index 7993601c6..9568f45a0 100644 --- a/packages/ui/src/elements/PublishMany/index.tsx +++ b/packages/ui/src/elements/PublishMany/index.tsx @@ -5,7 +5,7 @@ import { Modal, useModal } from '@faceless-ui/modal' import { getTranslation } from '@payloadcms/translations' import { useRouter } from 'next/navigation.js' import React, { useCallback, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { useAuth } from '../../providers/Auth/index.js' import { useConfig } from '../../providers/Config/index.js' diff --git a/packages/ui/src/elements/Status/index.tsx b/packages/ui/src/elements/Status/index.tsx index 6d42cb32b..5cc024e59 100644 --- a/packages/ui/src/elements/Status/index.tsx +++ b/packages/ui/src/elements/Status/index.tsx @@ -1,7 +1,7 @@ 'use client' import { Modal, useModal } from '@faceless-ui/modal' import React, { useCallback, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { useForm } from '../../forms/Form/context.js' import { useConfig } from '../../providers/Config/index.js' diff --git a/packages/ui/src/elements/UnpublishMany/index.tsx b/packages/ui/src/elements/UnpublishMany/index.tsx index dd5b07e8c..ed2b4203e 100644 --- a/packages/ui/src/elements/UnpublishMany/index.tsx +++ b/packages/ui/src/elements/UnpublishMany/index.tsx @@ -3,7 +3,6 @@ import { Modal, useModal } from '@faceless-ui/modal' import { getTranslation } from '@payloadcms/translations' import { useRouter } from 'next/navigation.js' import React, { useCallback, useState } from 'react' -import { toast } from 'react-toastify' import { useAuth } from '../../providers/Auth/index.js' import { useConfig } from '../../providers/Config/index.js' @@ -21,6 +20,8 @@ const baseClass = 'unpublish-many' import type { ClientCollectionConfig } from 'payload/types' +import { toast } from 'sonner' + export type UnpublishManyProps = { collection: ClientCollectionConfig } diff --git a/packages/ui/src/exports/elements.ts b/packages/ui/src/exports/elements.ts index 956c2d5bb..c1000af27 100644 --- a/packages/ui/src/exports/elements.ts +++ b/packages/ui/src/exports/elements.ts @@ -46,9 +46,5 @@ export { Tooltip } from '../elements/Tooltip/index.js' export { Translation } from '../elements/Translation/index.js' export { UnpublishMany } from '../elements/UnpublishMany/index.js' export { Upload } from '../elements/Upload/index.js' -import { Modal } from '@faceless-ui/modal' -export { Modal } -import * as reactToastifyImport from 'react-toastify' -const { toast } = - reactToastifyImport && 'toast' in reactToastifyImport ? reactToastifyImport : { toast: undefined } -export { toast } +export { Modal } from '@faceless-ui/modal' +export { toast } from 'sonner' diff --git a/packages/ui/src/forms/Form/index.tsx b/packages/ui/src/forms/Form/index.tsx index bac74ad77..33390d73e 100644 --- a/packages/ui/src/forms/Form/index.tsx +++ b/packages/ui/src/forms/Form/index.tsx @@ -8,7 +8,7 @@ import { serialize } from 'object-to-formdata' import { wait } from 'payload/utilities' import QueryString from 'qs' import React, { useCallback, useEffect, useReducer, useRef, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import type { Context as FormContextType, @@ -180,6 +180,33 @@ export const Form: React.FC = (props) => { return } + // create new toast promise which will resolve manually later + let successToast, errorToast + const promise = new Promise((resolve, reject) => { + successToast = resolve + errorToast = reject + }) + + const hasFormSubmitAction = + actionArg || typeof action === 'string' || typeof action === 'function' + + if (redirect || disableSuccessStatus || !hasFormSubmitAction) { + // Do not show submitting toast, as the promise toast may never disappear under these conditions. + // Instead, make successToast() or errorToast() throw toast.success / toast.error + successToast = (data) => toast.success(data) + errorToast = (data) => toast.error(data) + } else { + toast.promise(promise, { + error: (data) => { + return data as string + }, + loading: t('general:submitting'), + success: (data) => { + return data as string + }, + }) + } + if (e) { e.stopPropagation() e.preventDefault() @@ -219,7 +246,7 @@ export const Form: React.FC = (props) => { // If not valid, prevent submission if (!isValid) { - toast.error(t('error:correctInvalidFields')) + errorToast(t('error:correctInvalidFields')) setProcessing(false) setSubmitted(true) setDisabled(false) @@ -236,6 +263,15 @@ export const Form: React.FC = (props) => { onSubmit(fields, data) } + if (!hasFormSubmitAction) { + // No action provided, so we should return. An example where this happens are lexical link drawers. Upon submitting the drawer, we + // want to close it without submitting the form. Stuff like validation would be handled by lexical before this, through beforeSubmit + setProcessing(false) + setSubmitted(true) + setDisabled(false) + return + } + const formData = contextRef.current.createFormData(overrides) try { @@ -272,7 +308,6 @@ export const Form: React.FC = (props) => { let json: Record = {} if (isJSON) json = await res.json() - if (res.status < 400) { if (typeof onSuccess === 'function') await onSuccess(json) setSubmitted(false) @@ -281,7 +316,7 @@ export const Form: React.FC = (props) => { if (redirect) { router.push(redirect) } else if (!disableSuccessStatus) { - toast.success(json.message || t('general:submissionSuccessful'), { autoClose: 3000 }) + successToast(json.message || t('general:submissionSuccessful')) } } else { setProcessing(false) @@ -289,7 +324,7 @@ export const Form: React.FC = (props) => { contextRef.current = { ...contextRef.current } // triggers rerender of all components that subscribe to form if (json.message) { - toast.error(json.message) + errorToast(json.message) return } @@ -328,7 +363,7 @@ export const Form: React.FC = (props) => { }) nonFieldErrors.forEach((err) => { - toast.error(err.message || t('error:unknown')) + errorToast(err.message || t('error:unknown')) }) return @@ -336,14 +371,15 @@ export const Form: React.FC = (props) => { const message = errorMessages?.[res.status] || res?.statusText || t('error:unknown') - toast.error(message) + errorToast(message) } } catch (err) { + console.error('Error submitting form', err) setProcessing(false) setSubmitted(true) setDisabled(false) - toast.error(err) + errorToast(err.message) } }, [ diff --git a/packages/ui/src/providers/Auth/index.tsx b/packages/ui/src/providers/Auth/index.tsx index 4262c34b4..e1e5a54a8 100644 --- a/packages/ui/src/providers/Auth/index.tsx +++ b/packages/ui/src/providers/Auth/index.tsx @@ -6,7 +6,7 @@ import { useModal } from '@faceless-ui/modal' import { usePathname, useRouter } from 'next/navigation.js' import qs from 'qs' import React, { createContext, useCallback, useContext, useEffect, useState } from 'react' -import { toast } from 'react-toastify' +import { toast } from 'sonner' import { stayLoggedInModalSlug } from '../../elements/StayLoggedIn/index.js' import { useDebounce } from '../../hooks/useDebounce.js' diff --git a/packages/ui/src/providers/Root/index.tsx b/packages/ui/src/providers/Root/index.tsx index 6338a0c7b..97f586140 100644 --- a/packages/ui/src/providers/Root/index.tsx +++ b/packages/ui/src/providers/Root/index.tsx @@ -6,7 +6,6 @@ import { ModalContainer, ModalProvider } from '@faceless-ui/modal' import { ScrollInfoProvider } from '@faceless-ui/scroll-info' import { WindowInfoProvider } from '@faceless-ui/window-info' import React, { Fragment } from 'react' -import { Slide, ToastContainer } from 'react-toastify' import type { ComponentMap } from '../ComponentMap/buildComponentMap/types.js' import type { Theme } from '../Theme/index.js' @@ -28,6 +27,7 @@ import { PreferencesProvider } from '../Preferences/index.js' import { RouteCache } from '../RouteCache/index.js' import { SearchParamsProvider } from '../SearchParams/index.js' import { ThemeProvider } from '../Theme/index.js' +import { ToastContainer } from '../ToastContainer/index.js' import { TranslationProvider } from '../Translation/index.js' type Props = { @@ -112,7 +112,7 @@ export const RootProvider: React.FC = ({ - + ) } diff --git a/packages/ui/src/providers/ToastContainer/icons/Error.tsx b/packages/ui/src/providers/ToastContainer/icons/Error.tsx new file mode 100644 index 000000000..2ecf794d8 --- /dev/null +++ b/packages/ui/src/providers/ToastContainer/icons/Error.tsx @@ -0,0 +1,18 @@ +import React from 'react' + +export const Error: React.FC = () => { + return ( + + + + + ) +} diff --git a/packages/ui/src/providers/ToastContainer/icons/Info.tsx b/packages/ui/src/providers/ToastContainer/icons/Info.tsx new file mode 100644 index 000000000..a372cf9f8 --- /dev/null +++ b/packages/ui/src/providers/ToastContainer/icons/Info.tsx @@ -0,0 +1,18 @@ +import React from 'react' + +export const Info: React.FC = () => { + return ( + + + + + ) +} diff --git a/packages/ui/src/providers/ToastContainer/icons/Success.tsx b/packages/ui/src/providers/ToastContainer/icons/Success.tsx new file mode 100644 index 000000000..657aed716 --- /dev/null +++ b/packages/ui/src/providers/ToastContainer/icons/Success.tsx @@ -0,0 +1,18 @@ +import React from 'react' + +export const Success: React.FC = () => { + return ( + + + + + ) +} diff --git a/packages/ui/src/providers/ToastContainer/icons/Warning.tsx b/packages/ui/src/providers/ToastContainer/icons/Warning.tsx new file mode 100644 index 000000000..a25da91c7 --- /dev/null +++ b/packages/ui/src/providers/ToastContainer/icons/Warning.tsx @@ -0,0 +1,18 @@ +import React from 'react' + +export const Warning: React.FC = () => { + return ( + + + + + ) +} diff --git a/packages/ui/src/providers/ToastContainer/index.tsx b/packages/ui/src/providers/ToastContainer/index.tsx new file mode 100644 index 000000000..ec08c33ee --- /dev/null +++ b/packages/ui/src/providers/ToastContainer/index.tsx @@ -0,0 +1,40 @@ +'use client' +import React from 'react' +import { Toaster } from 'sonner' + +import { Error } from './icons/Error.js' +import { Info } from './icons/Info.js' +import { Success } from './icons/Success.js' +import { Warning } from './icons/Warning.js' + +export const ToastContainer: React.FC = () => { + return ( + , + info: , + success: , + warning: , + }} + offset="36px" + toastOptions={{ + classNames: { + closeButton: 'payload-toast-close-button', + content: 'toast-content', + error: 'toast-error', + icon: 'toast-icon', + info: 'toast-info', + success: 'toast-success', + toast: 'payload-toast-item', + warning: 'toast-warning', + }, + unstyled: true, + }} + visibleToasts={5} + /> + ) +} diff --git a/packages/ui/src/scss/app.scss b/packages/ui/src/scss/app.scss index 7bf7bfbc7..ff5bdcda8 100644 --- a/packages/ui/src/scss/app.scss +++ b/packages/ui/src/scss/app.scss @@ -1,5 +1,5 @@ @import 'styles'; -@import './toastify.scss'; +@import './toasts.scss'; @import './colors.scss'; :root { diff --git a/packages/ui/src/scss/toastify.scss b/packages/ui/src/scss/toastify.scss deleted file mode 100644 index b387cb63d..000000000 --- a/packages/ui/src/scss/toastify.scss +++ /dev/null @@ -1,58 +0,0 @@ -@import 'vars'; - -.Toastify { - .Toastify__toast-container { - left: base(5); - transform: none; - right: base(5); - width: auto; - } - - .Toastify__toast { - padding: base(0.5); - border-radius: $style-radius-m; - font-weight: 600; - } - - .Toastify__close-button { - align-self: center; - opacity: 0.7; - - &:hover { - opacity: 1; - } - } - - .Toastify__toast--success { - color: var(--color-success-900); - background: var(--color-success-500); - - .Toastify__progress-bar { - background-color: var(--color-success-900); - } - } - - .Toastify__close-button--success { - color: var(--color-success-900); - } - - .Toastify__toast--error { - background: var(--theme-error-500); - color: #fff; - - .Toastify__progress-bar { - background-color: #fff; - } - } - - .Toastify__close-button--light { - color: inherit; - } - - @include mid-break { - .Toastify__toast-container { - left: $baseline; - right: $baseline; - } - } -} diff --git a/packages/ui/src/scss/toasts.scss b/packages/ui/src/scss/toasts.scss new file mode 100644 index 000000000..15bc94721 --- /dev/null +++ b/packages/ui/src/scss/toasts.scss @@ -0,0 +1,111 @@ +.payload-toast-container { + .payload-toast-close-button { + left: unset; + right: 0.5rem; + top: 1.5rem; + color: var(--theme-elevation-400); + background: unset; + border: none; + display: flex; + width: 1.25rem; + height: 1.25rem; + justify-content: center; + align-items: center; + + &:hover { + background: none; + } + + svg { + width: 2rem; + height: 2rem; + } + + [dir='RTL'] & { + right: unset; + left: 0.5rem; + } + } + + .payload-toast-item { + padding: 1rem; + color: var(--theme-text); + font-style: normal; + font-weight: 600; + display: flex; + gap: 1rem; + align-items: center; + width: 100%; + border-radius: 0.15rem; + border: 1px solid var(--theme-border-color); + background: var(--theme-input-bg); + box-shadow: + 0px 10px 4px -8px rgba(0, 2, 4, 0.02), + 0px 2px 3px 0px rgba(0, 2, 4, 0.05); + + .toast-content { + transition: opacity 100ms cubic-bezier(0.55, 0.055, 0.675, 0.19); + } + + &[data-front='false'] { + .toast-content { + opacity: 0; + } + } + + &[data-expanded='true'] { + .toast-content { + opacity: 1; + } + } + + .toast-icon { + svg { + width: 2.4rem; + height: 2.4rem; + } + } + + &.toast-warning { + border-color: var(--theme-warning-200); + background-color: var(--theme-warning-100); + } + + &.toast-error { + border-color: var(--theme-error-300); + background-color: var(--theme-error-150); + } + + &.toast-success { + border-color: var(--theme-success-200); + background-color: var(--theme-success-100); + } + + &.toast-info { + border-color: var(--theme-elevation-250); + background-color: var(--theme-elevation-100); + } + + [data-theme='light'] & { + &.toast-warning { + border-color: var(--theme-warning-550); + background-color: var(--theme-warning-100); + } + + &.toast-error { + border-color: var(--theme-error-200); + background-color: var(--theme-error-50); + } + + &.toast-success { + border-color: var(--theme-success-550); + background-color: var(--theme-success-50); + } + + &.toast-info { + border-color: var(--theme-border-color); + background-color: var(--theme-elevation-50); + } + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78d9b5441..a19e0f5c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -603,12 +603,12 @@ importers: react-diff-viewer-continued: specifier: 3.2.6 version: 3.2.6(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522) - react-toastify: - specifier: 10.0.5 - version: 10.0.5(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522) sass: specifier: 1.77.4 version: 1.77.4 + sonner: + specifier: ^1.5.0 + version: 1.5.0(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522) ws: specifier: ^8.16.0 version: 8.16.0 @@ -1464,12 +1464,12 @@ importers: react-select: specifier: 5.8.0 version: 5.8.0(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522)(types-react@19.0.0-beta.2) - react-toastify: - specifier: 10.0.5 - version: 10.0.5(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522) scheduler: specifier: 0.25.0-rc-f994737d14-20240522 version: 0.25.0-rc-f994737d14-20240522 + sonner: + specifier: ^1.5.0 + version: 1.5.0(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522) use-context-selector: specifier: 2.0.0 version: 2.0.0(react@19.0.0-rc-f994737d14-20240522)(scheduler@0.25.0-rc-f994737d14-20240522) @@ -15308,17 +15308,6 @@ packages: - '@types/react' dev: false - /react-toastify@10.0.5(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522): - resolution: {integrity: sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==} - peerDependencies: - react: ^19.0.0-rc-f994737d14-20240522 - react-dom: ^19.0.0-rc-f994737d14-20240522 - dependencies: - clsx: 2.1.0 - react: 19.0.0-rc-f994737d14-20240522 - react-dom: 19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522) - dev: false - /react-transition-group@4.4.5(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522): resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -16096,6 +16085,16 @@ packages: dependencies: atomic-sleep: 1.0.0 + /sonner@1.5.0(react-dom@19.0.0-rc-f994737d14-20240522)(react@19.0.0-rc-f994737d14-20240522): + resolution: {integrity: sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==} + peerDependencies: + react: ^19.0.0-rc-f994737d14-20240522 + react-dom: ^19.0.0-rc-f994737d14-20240522 + dependencies: + react: 19.0.0-rc-f994737d14-20240522 + react-dom: 19.0.0-rc-f994737d14-20240522(react@19.0.0-rc-f994737d14-20240522) + dev: false + /sort-keys-length@1.0.1: resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} engines: {node: '>=0.10.0'} diff --git a/test/access-control/e2e.spec.ts b/test/access-control/e2e.spec.ts index d31ac4cef..dcf2d0e3a 100644 --- a/test/access-control/e2e.spec.ts +++ b/test/access-control/e2e.spec.ts @@ -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', ) diff --git a/test/admin/e2e/1/e2e.spec.ts b/test/admin/e2e/1/e2e.spec.ts index 24e0fb4e4..14820738b 100644 --- a/test/admin/e2e/1/e2e.spec.ts +++ b/test/admin/e2e/1/e2e.spec.ts @@ -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) diff --git a/test/fields-relationship/e2e.spec.ts b/test/fields-relationship/e2e.spec.ts index ee2060445..8fef6cf12 100644 --- a/test/fields-relationship/e2e.spec.ts +++ b/test/fields-relationship/e2e.spec.ts @@ -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'), diff --git a/test/fields/collections/Array/e2e.spec.ts b/test/fields/collections/Array/e2e.spec.ts index d5e2e5b5a..ca4cd4b62 100644 --- a/test/fields/collections/Array/e2e.spec.ts +++ b/test/fields/collections/Array/e2e.spec.ts @@ -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.', ) }) diff --git a/test/fields/collections/Blocks/e2e.spec.ts b/test/fields/collections/Blocks/e2e.spec.ts index b579d604d..ade24b287 100644 --- a/test/fields/collections/Blocks/e2e.spec.ts +++ b/test/fields/collections/Blocks/e2e.spec.ts @@ -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', ) }) diff --git a/test/fields/collections/Lexical/e2e/blocks/e2e.spec.ts b/test/fields/collections/Lexical/e2e/blocks/e2e.spec.ts index 763a4e8a7..cfcb221b7 100644 --- a/test/fields/collections/Lexical/e2e/blocks/e2e.spec.ts +++ b/test/fields/collections/Lexical/e2e/blocks/e2e.spec.ts @@ -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 diff --git a/test/fields/collections/Number/e2e.spec.ts b/test/fields/collections/Number/e2e.spec.ts index 6f6156b1e..109928c19 100644 --- a/test/fields/collections/Number/e2e.spec.ts +++ b/test/fields/collections/Number/e2e.spec.ts @@ -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', ) }) diff --git a/test/fields/collections/Relationship/e2e.spec.ts b/test/fields/collections/Relationship/e2e.spec.ts index be3a19e91..e8518654b 100644 --- a/test/fields/collections/Relationship/e2e.spec.ts +++ b/test/fields/collections/Relationship/e2e.spec.ts @@ -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', ) }) diff --git a/test/fields/collections/Upload/e2e.spec.ts b/test/fields/collections/Upload/e2e.spec.ts index ebd429846..639c7a2b9 100644 --- a/test/fields/collections/Upload/e2e.spec.ts +++ b/test/fields/collections/Upload/e2e.spec.ts @@ -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() }) diff --git a/test/fields/e2e.spec.ts b/test/fields/e2e.spec.ts index 71194dcb6..43c5fd14b 100644 --- a/test/fields/e2e.spec.ts +++ b/test/fields/e2e.spec.ts @@ -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) diff --git a/test/helpers.ts b/test/helpers.ts index a3d05878a..a3673720e 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -177,7 +177,7 @@ export async function saveDocHotkeyAndAssert(page: Page): Promise { 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() } } diff --git a/test/localization/e2e.spec.ts b/test/localization/e2e.spec.ts index e4cc3f69c..871518ff6 100644 --- a/test/localization/e2e.spec.ts +++ b/test/localization/e2e.spec.ts @@ -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) }) }) diff --git a/test/uploads/e2e.spec.ts b/test/uploads/e2e.spec.ts index debec00f8..c06bacb40 100644 --- a/test/uploads/e2e.spec.ts +++ b/test/uploads/e2e.spec.ts @@ -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 } diff --git a/test/versions/e2e.spec.ts b/test/versions/e2e.spec.ts index fae64faab..5c01fc38f 100644 --- a/test/versions/e2e.spec.ts +++ b/test/versions/e2e.spec.ts @@ -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.', )