Skip to content
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

feat(Toast): add placement prop to ToastContainer #7609

Merged
merged 2 commits into from
Jan 15, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/@react-spectrum/toast/docs/Toast.mdx
Original file line number Diff line number Diff line change
@@ -165,6 +165,14 @@ function Example() {
}
```

## Placement

By default, toasts are displayed at the bottom center of the screen. This can be changed by setting the `placement` prop on the `ToastContainer` to `'top start'`, `'top'`, `'top end'`, `'bottom start'`, `'bottom'`, or `'bottom end'`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the team feel about a blurb for mfe's that may have multiple ToastContainers?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! I checked and we currently don't talk about multiple ToastContainers in the docs. Do we need a general section on that or continue not documenting it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to be editing more docs soon, so I'll do this in a follow up!


```tsx example render=false hidden
<ToastContainer placement="bottom end" />
```

## API

### ToastQueue
6 changes: 5 additions & 1 deletion packages/@react-spectrum/toast/src/ToastContainer.tsx
Original file line number Diff line number Diff line change
@@ -21,7 +21,11 @@ import {Toaster} from './Toaster';
import {ToastOptions, ToastQueue, useToastQueue} from '@react-stately/toast';
import {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';

export interface SpectrumToastContainerProps extends AriaToastRegionProps {}
export type ToastPlacement = 'top start' | 'top' | 'top end' | 'bottom start' | 'bottom' | 'bottom end';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that we specified in the ticket that we'd only support this set of placement, but it just occurred to me that we support left and right in additional to start and end in our other placement props. I don't quite remember if there was a use case for that or if it was carryover from v2 or not...


export interface SpectrumToastContainerProps extends AriaToastRegionProps {
placement?: ToastPlacement
}

export interface SpectrumToastOptions extends Omit<ToastOptions, 'priority'>, DOMProps {
/** A label for the action button within the toast. */
15 changes: 11 additions & 4 deletions packages/@react-spectrum/toast/src/Toaster.tsx
Original file line number Diff line number Diff line change
@@ -15,15 +15,17 @@ import {classNames} from '@react-spectrum/utils';
import {FocusScope, useFocusRing} from '@react-aria/focus';
import {mergeProps} from '@react-aria/utils';
import {Provider} from '@react-spectrum/provider';
import React, {createContext, ReactElement, ReactNode, useRef} from 'react';
import React, {createContext, ReactElement, ReactNode, useMemo, useRef} from 'react';
import ReactDOM from 'react-dom';
import toastContainerStyles from './toastContainer.css';
import type {ToastPlacement} from './ToastContainer';
import {ToastState} from '@react-stately/toast';
import {useUNSTABLE_PortalContext} from '@react-aria/overlays';

interface ToastContainerProps extends AriaToastRegionProps {
children: ReactNode,
state: ToastState<unknown>
state: ToastState<unknown>,
placement?: ToastPlacement
}

export const ToasterContext = createContext(false);
@@ -39,15 +41,20 @@ export function Toaster(props: ToastContainerProps): ReactElement {
let {focusProps, isFocusVisible} = useFocusRing();
let {getContainer} = useUNSTABLE_PortalContext();

let [position, placement] = useMemo(() => {
let [pos = 'bottom', place = 'center'] = props.placement?.split(' ') || [];
return [pos, place];
}, [props.placement]);

let contents = (
<Provider UNSAFE_style={{background: 'transparent'}}>
<FocusScope>
<ToasterContext.Provider value={isFocusVisible}>
<div
{...mergeProps(regionProps, focusProps)}
ref={ref}
data-position="bottom"
data-placement="center"
data-position={position}
data-placement={placement}
className={classNames(
toastContainerStyles,
'react-spectrum-ToastContainer',
4 changes: 2 additions & 2 deletions packages/@react-spectrum/toast/src/toastContainer.css
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@
--slide-to: translateY(0);
}

&[data-placement=left] {
&[data-placement=start] {
align-items: flex-start;
--slide-from: translateX(-100%);
--slide-to: translateX(0);
@@ -62,7 +62,7 @@
align-items: center;
}

&[data-placement=right] {
&[data-placement=end] {
align-items: flex-end;
--slide-from: translateX(100%);
--slide-to: translateX(0);
11 changes: 8 additions & 3 deletions packages/@react-spectrum/toast/stories/Toast.stories.tsx
Original file line number Diff line number Diff line change
@@ -27,21 +27,26 @@ import {UNSTABLE_PortalProvider} from '@react-aria/overlays';
export default {
title: 'Toast',
decorators: [
(story, {parameters}) => (
(story, {parameters, args}) => (
<>
{!parameters.disableToastContainer && <ToastContainer />}
{!parameters.disableToastContainer && <ToastContainer placement={args.placement} />}
<MainLandmark>{story()}</MainLandmark>
</>
)
],
args: {
shouldCloseOnAction: false,
timeout: null
timeout: null,
placement: undefined
},
argTypes: {
timeout: {
control: 'radio',
options: [null, 5000]
},
placement: {
control: 'select',
options: [undefined, 'top start', 'top', 'top end', 'bottom start', 'bottom', 'bottom end']
}
}
};