Skip to content

Commit 78683ee

Browse files
authored
add placement prop to ToastContainer (#7609)
1 parent 75b74b1 commit 78683ee

File tree

5 files changed

+34
-10
lines changed

5 files changed

+34
-10
lines changed

packages/@react-spectrum/toast/docs/Toast.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@ function Example() {
165165
}
166166
```
167167

168+
## Placement
169+
170+
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'`.
171+
172+
```tsx example render=false hidden
173+
<ToastContainer placement="bottom end" />
174+
```
175+
168176
## API
169177

170178
### ToastQueue

packages/@react-spectrum/toast/src/ToastContainer.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ import {Toaster} from './Toaster';
2121
import {ToastOptions, ToastQueue, useToastQueue} from '@react-stately/toast';
2222
import {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';
2323

24-
export interface SpectrumToastContainerProps extends AriaToastRegionProps {}
24+
export type ToastPlacement = 'top start' | 'top' | 'top end' | 'bottom start' | 'bottom' | 'bottom end';
25+
26+
export interface SpectrumToastContainerProps extends AriaToastRegionProps {
27+
placement?: ToastPlacement
28+
}
2529

2630
export interface SpectrumToastOptions extends Omit<ToastOptions, 'priority'>, DOMProps {
2731
/** A label for the action button within the toast. */

packages/@react-spectrum/toast/src/Toaster.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,17 @@ import {classNames} from '@react-spectrum/utils';
1515
import {FocusScope, useFocusRing} from '@react-aria/focus';
1616
import {mergeProps} from '@react-aria/utils';
1717
import {Provider} from '@react-spectrum/provider';
18-
import React, {createContext, ReactElement, ReactNode, useRef} from 'react';
18+
import React, {createContext, ReactElement, ReactNode, useMemo, useRef} from 'react';
1919
import ReactDOM from 'react-dom';
2020
import toastContainerStyles from './toastContainer.css';
21+
import type {ToastPlacement} from './ToastContainer';
2122
import {ToastState} from '@react-stately/toast';
2223
import {useUNSTABLE_PortalContext} from '@react-aria/overlays';
2324

2425
interface ToastContainerProps extends AriaToastRegionProps {
2526
children: ReactNode,
26-
state: ToastState<unknown>
27+
state: ToastState<unknown>,
28+
placement?: ToastPlacement
2729
}
2830

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

44+
let [position, placement] = useMemo(() => {
45+
let [pos = 'bottom', place = 'center'] = props.placement?.split(' ') || [];
46+
return [pos, place];
47+
}, [props.placement]);
48+
4249
let contents = (
4350
<Provider UNSAFE_style={{background: 'transparent'}}>
4451
<FocusScope>
4552
<ToasterContext.Provider value={isFocusVisible}>
4653
<div
4754
{...mergeProps(regionProps, focusProps)}
4855
ref={ref}
49-
data-position="bottom"
50-
data-placement="center"
56+
data-position={position}
57+
data-placement={placement}
5158
className={classNames(
5259
toastContainerStyles,
5360
'react-spectrum-ToastContainer',

packages/@react-spectrum/toast/src/toastContainer.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
--slide-to: translateY(0);
4949
}
5050

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

65-
&[data-placement=right] {
65+
&[data-placement=end] {
6666
align-items: flex-end;
6767
--slide-from: translateX(100%);
6868
--slide-to: translateX(0);

packages/@react-spectrum/toast/stories/Toast.stories.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,26 @@ import {UNSTABLE_PortalProvider} from '@react-aria/overlays';
2727
export default {
2828
title: 'Toast',
2929
decorators: [
30-
(story, {parameters}) => (
30+
(story, {parameters, args}) => (
3131
<>
32-
{!parameters.disableToastContainer && <ToastContainer />}
32+
{!parameters.disableToastContainer && <ToastContainer placement={args.placement} />}
3333
<MainLandmark>{story()}</MainLandmark>
3434
</>
3535
)
3636
],
3737
args: {
3838
shouldCloseOnAction: false,
39-
timeout: null
39+
timeout: null,
40+
placement: undefined
4041
},
4142
argTypes: {
4243
timeout: {
4344
control: 'radio',
4445
options: [null, 5000]
46+
},
47+
placement: {
48+
control: 'select',
49+
options: [undefined, 'top start', 'top', 'top end', 'bottom start', 'bottom', 'bottom end']
4550
}
4651
}
4752
};

0 commit comments

Comments
 (0)