-
Notifications
You must be signed in to change notification settings - Fork 0
Document the toast framework and mount the region in cmsui/publicui #118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
InteraktivPreuss
wants to merge
2
commits into
main
Choose a base branch
from
document-toast-framework
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+497
−32
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,4 +24,5 @@ add-on-driven-configuration | |
| cookieplone-frontend-add-on | ||
| routing | ||
| slots | ||
| toasts | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| --- | ||
| myst: | ||
| html_meta: | ||
| "description": "An explanation of the toast notification framework in Plone Aurora, including its architecture, visual variants, and the countdown bar." | ||
| "property=og:description": "An explanation of the toast notification framework in Plone Aurora, including its architecture, visual variants, and the countdown bar." | ||
| "property=og:title": "Toast notifications" | ||
| "keywords": "Plone Aurora, Plone, toast, notification, React Aria, @plone/layout" | ||
| --- | ||
|
|
||
| # Toast notifications | ||
|
|
||
| Plone Aurora ships a global toast framework based on the `UNSTABLE_Toast` primitives from React Aria Components. | ||
| It is registered by `@plone/layout` and reachable from anywhere in the app via `@plone/registry`, so add-ons can confirm successful actions, surface route errors, or report background task progress without each one having to mount its own region. | ||
|
|
||
| See a [demo of toasts](https://plone-storybook.readthedocs.io/?path=/docs/layout_toast--docs). | ||
|
|
||
| For task-oriented guidance, see {doc}`../how-to-guides/show-toasts`. | ||
|
|
||
|
|
||
| ## Architecture | ||
|
|
||
| The framework has three layers: a single shared queue, three registry utilities that wrap it, and a visual region that subscribes to the queue and renders each toast. | ||
|
|
||
|
|
||
| ### The queue and registry utilities | ||
|
|
||
| In the package `@plone/layout`, its file {file}`packages/layout/config/toast.ts` exports a module-level `ToastQueue<ToastItem>` and an `install()` function. | ||
|
|
||
| The queue wraps state updates with the function `document.startViewTransition` when the browser supports it, falling back to a plain update otherwise. | ||
| Enter and exit animations stay in sync with React. | ||
|
|
||
| The `install()` function registers three utilities in `@plone/registry` for working with toasts: | ||
|
|
||
| `{ type: 'toast', name: 'queue' }` | ||
| : Returns the queue itself. | ||
| This is useful to subscribe or render a custom region. | ||
|
|
||
| `{ type: 'toast', name: 'show' }` | ||
| : Adds a `ToastItem` to the queue and returns its key. | ||
|
|
||
| `{ type: 'toast', name: 'dismiss' }` | ||
| : Closes the toast identified by a key returned from `show`. | ||
|
|
||
|
|
||
| ### The region and CSS | ||
|
|
||
| The visual `<Toast>` region is defined in {file}`packages/layout/components/Toast/Toast.tsx`. | ||
| It's mounted once per layout, so toasts appear regardless of which route the user is on. | ||
|
|
||
| The shared CSS lives in {file}`components/src/styles/basic/Toast.css`. | ||
| It defines: | ||
|
|
||
| - The region anchor (`.react-aria-ToastRegion`) and base toast container (`.react-aria-Toast`). | ||
| - Four visual variants picked up from the toast's `className`: the default (Quanta `denim`), `.success` (`turtle`), `.info` (`royal`), and `.error` (`wine`). | ||
| All colors reference Quanta tokens with hexadecimal fallbacks, so the styles render correctly outside a Quanta theme. | ||
| - The countdown bar (`.react-aria-Toast-progress`) rendered along the bottom edge of every timed toast. | ||
|
|
||
|
|
||
| ## Countdown bar | ||
|
|
||
| Every timed toast renders a thin progress bar pinned to its bottom edge. | ||
| The bar shrinks from full width to empty over the toast's `timeout` duration, giving the user a visual cue for how long the toast will remain on screen. | ||
|
|
||
| A toast appears and behaves as described below. | ||
|
|
||
| - Drawn as a single absolutely-positioned `<div>` inside the toast, clipped to the toast's rounded corners. | ||
| - Background color is a lightening overlay, implemented with (`rgba(255, 255, 255, 0.35)`). | ||
| It works against every variant background without per-variant styling. | ||
| - Pauses on hover or focus, mirroring React Aria's own pause-timers behavior. | ||
| - Never renders for sticky toasts, either declared with `timeout: null` or when the caller sets `showProgress: false` on the item. | ||
| - Marked `aria-hidden`, because it's decorative, not announced. | ||
|
|
||
|
|
||
| ## Where toasts appear today | ||
|
|
||
| The region is mounted in the following files. | ||
|
|
||
| {file}`packages/cmsui/routes/layout.tsx` | ||
| : every editor route | ||
|
|
||
| {file}`packages/publicui/routes/index.tsx` | ||
| : every visitor-facing page | ||
|
|
||
| {file}`packages/contents/routes/layout.tsx` | ||
| : the `/contents` UI | ||
|
|
||
| `@plone/layout` is installed by `@plone/aurora`, so the queue and utilities are available in any package that depends on it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| --- | ||
| myst: | ||
| html_meta: | ||
| "description": "How to show toast notifications in Plone Aurora using the @plone/layout toast framework." | ||
| "property=og:description": "How to show toast notifications in Plone Aurora using the @plone/layout toast framework." | ||
| "property=og:title": "Show toast notifications" | ||
| "keywords": "Plone Aurora, Plone, toast, notification, React Aria, @plone/layout" | ||
| --- | ||
|
|
||
| # Show toast notifications | ||
|
|
||
| Plone Aurora ships a global toast framework based on the `UNSTABLE_Toast` primitives from React Aria Components. | ||
| It is registered by `@plone/layout` and reachable from anywhere in the app via `@plone/registry`. | ||
| Use it to confirm successful actions, surface route errors, or report background task progress. | ||
|
|
||
| See a [demo of toasts](https://plone-storybook.readthedocs.io/?path=/docs/layout_toast--docs). | ||
|
|
||
| For an explanation of how the framework is wired and where the region is mounted, see {doc}`../conceptual-guides/toasts`. | ||
|
|
||
|
|
||
| ## Show a toast | ||
|
|
||
| ```ts | ||
| import config from '@plone/registry'; | ||
|
|
||
| config.getUtility({ type: 'toast', name: 'show' }).method({ | ||
| title: 'Saved', | ||
| description: 'Your changes have been saved.', | ||
| }); | ||
| ``` | ||
|
|
||
| The fields on `ToastItem` are: | ||
|
|
||
| `title` | ||
| : Required. | ||
| Announced first by screen readers. | ||
|
|
||
| `description` | ||
| : Optional supporting copy rendered below the title. | ||
|
|
||
| `icon` | ||
| : Optional leading `ReactNode` rendered before the title. | ||
|
|
||
| `className` | ||
| : Optional modifier class. | ||
| Pass `'success'`, `'info'`, or `'error'` to use one of the variants that ship in {file}`Toast.css`. | ||
| Project add-ons can register their own classes for additional toast alert styles. | ||
|
|
||
| `showProgress` | ||
| : Whether to render the auto-dismiss countdown bar at the bottom of the toast. | ||
| Defaults to `true`. | ||
| Pass `false` for short confirmations where the bar adds visual noise. | ||
| Ignored for sticky toasts (`timeout: null`), which never render a bar because there is no countdown to draw. | ||
|
|
||
|
|
||
| ## Customize the timeout | ||
|
|
||
| `show` accepts an options object as a second argument: | ||
|
|
||
| ```ts | ||
| config.getUtility({ type: 'toast', name: 'show' }).method( | ||
| { title: 'Uploading…', description: 'Hold on.' }, | ||
| { timeout: 8000 }, | ||
| ); | ||
| ``` | ||
|
|
||
| `timeout` | ||
| : An integer representing the duration in milliseconds after which a toast will be auto-dismissed. | ||
| Defaults to the value set by `DEFAULT_TOAST_TIMEOUT_MS`, currently 6000 milliseconds, exported from {file}`layout/config/toast.ts`. | ||
| Pass `null` to require manual dismissal, which is useful for long-running operations that resolve via {ref}`dismiss <show-toasts-dismiss>`. | ||
| React Aria recommends a minimum of 5 seconds, so people who use a screen reader have time to read the announcement. | ||
|
|
||
| `onClose` | ||
| : Fires when the toast is removed for any reason, including auto-dismiss, close button, and programmatic dismiss. | ||
|
|
||
|
|
||
| (show-toasts-dismiss)= | ||
|
|
||
| ## Dismiss a toast programmatically | ||
|
|
||
| `show` returns an opaque `ToastKey`. | ||
| Pass it to the `dismiss` utility to remove the toast from code: | ||
|
|
||
| ```ts | ||
| import config from '@plone/registry'; | ||
|
|
||
| const show = config.getUtility({ type: 'toast', name: 'show' }).method; | ||
| const dismiss = config.getUtility({ type: 'toast', name: 'dismiss' }).method; | ||
|
|
||
| const key = show( | ||
| { title: 'Uploading…' }, | ||
| { timeout: null }, | ||
| ); | ||
|
|
||
| // later, when the upload finishes: | ||
| dismiss(key); | ||
| ``` | ||
|
|
||
|
|
||
| ## Surface route errors | ||
|
|
||
| `@plone/layout` exposes an `ErrorToast` helper for React Router error boundaries. | ||
| It reads the route error and pushes a styled toast. | ||
|
|
||
| ```tsx | ||
| import config from '@plone/registry'; | ||
| import ErrorToast from '@plone/layout/components/Toast/ErrorToast'; | ||
|
|
||
| export function ErrorBoundary() { | ||
| const queue = config.getUtility({ type: 'toast', name: 'queue' }).method(); | ||
| return ErrorToast(queue); | ||
| } | ||
| ``` | ||
|
|
||
|
|
||
| ## Render your own region | ||
|
|
||
| The default `<Toast>` region covers the common case, including bottom-center, dismissable, and view-transition animations. | ||
| For a different layout—for example, a side-panel region or a region scoped to a specific route—render `UNSTABLE_ToastRegion` directly, and pass the shared queue: | ||
|
|
||
| ```tsx | ||
| import { | ||
| UNSTABLE_Toast as Toast, | ||
| UNSTABLE_ToastContent as ToastContent, | ||
| UNSTABLE_ToastRegion as ToastRegion, | ||
| } from 'react-aria-components'; | ||
| import config from '@plone/registry'; | ||
|
|
||
| function MyCustomRegion() { | ||
| const queue = config.getUtility({ type: 'toast', name: 'queue' }).method(); | ||
| return ( | ||
| <ToastRegion queue={queue} aria-label="Background tasks"> | ||
| {({ toast }) => ( | ||
| <Toast toast={toast}> | ||
| <ToastContent>{/* … */}</ToastContent> | ||
| </Toast> | ||
| )} | ||
| </ToastRegion> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| Multiple regions can share the same queue. | ||
| Each rendered region receives every queued toast, so use them carefully. | ||
| Typically, one global region per layout is enough. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Mounted the global toast region in the `cmsui` layout, so toasts triggered from editor routes render. @InteraktivPreuss |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Added `success`, `info`, and `error` variants and an auto-dismiss countdown bar to `Toast.css`, and switched its hardcoded hex values to Quanta tokens with fallbacks. @InteraktivPreuss |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.