Skip to content

Commit 51c0293

Browse files
authored
Allow to configure the Toast portail container root (#7907)
1 parent d21bb3b commit 51c0293

File tree

4 files changed

+53
-3
lines changed

4 files changed

+53
-3
lines changed

packages/react-aria-components/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@react-aria/focus": "^3.20.1",
4646
"@react-aria/interactions": "^3.24.1",
4747
"@react-aria/live-announcer": "^3.4.1",
48+
"@react-aria/ssr": "^3.9.7",
4849
"@react-aria/toolbar": "3.0.0-beta.14",
4950
"@react-aria/utils": "^3.28.1",
5051
"@react-aria/virtualizer": "^4.1.3",

packages/react-aria-components/src/Toast.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {forwardRefType} from '@react-types/shared';
1818
import {QueuedToast, ToastQueue, ToastState, useToastQueue} from 'react-stately';
1919
import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, JSX, ReactElement, useContext} from 'react';
2020
import {TextContext} from './Text';
21+
import {useIsSSR} from '@react-aria/ssr';
2122
import {useObjectRef} from '@react-aria/utils';
2223

2324
const ToastStateContext = createContext<ToastState<any> | null>(null);
@@ -41,13 +42,19 @@ export interface ToastRegionProps<T> extends AriaToastRegionProps, StyleRenderPr
4142
/** The queue of toasts to display. */
4243
queue: ToastQueue<T>,
4344
/** A function to render each toast. */
44-
children: (renderProps: {toast: QueuedToast<T>}) => ReactElement
45+
children: (renderProps: {toast: QueuedToast<T>}) => ReactElement,
46+
/**
47+
* The container element in which the toast region portal will be placed.
48+
* @default document.body
49+
*/
50+
portalContainer?: Element
4551
}
4652

4753
/**
4854
* A ToastRegion displays one or more toast notifications.
4955
*/
5056
export const ToastRegion = /*#__PURE__*/ (forwardRef as forwardRefType)(function ToastRegion<T>(props: ToastRegionProps<T>, ref: ForwardedRef<HTMLDivElement>): JSX.Element | null {
57+
let isSSR = useIsSSR();
5158
let state = useToastQueue(props.queue);
5259
let objectRef = useObjectRef(ref);
5360
let {regionProps} = useToastRegion(props, state, objectRef);
@@ -64,6 +71,8 @@ export const ToastRegion = /*#__PURE__*/ (forwardRef as forwardRefType)(function
6471
}
6572
});
6673

74+
let {portalContainer = isSSR ? null : document.body} = props;
75+
6776
let region = (
6877
<ToastStateContext.Provider value={state}>
6978
<div
@@ -77,8 +86,8 @@ export const ToastRegion = /*#__PURE__*/ (forwardRef as forwardRefType)(function
7786
</ToastStateContext.Provider>
7887
);
7988

80-
return state.visibleToasts.length > 0 && typeof document !== 'undefined'
81-
? createPortal(region, document.body)
89+
return state.visibleToasts.length > 0 && portalContainer
90+
? createPortal(region, portalContainer)
8291
: null;
8392
});
8493

packages/react-aria-components/test/Toast.test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,43 @@ describe('Toast', () => {
322322
let region = getByRole('region');
323323
expect(region).toHaveAttribute('aria-label', 'Toasts');
324324
});
325+
326+
it('should render the toast region in the portal container', async () => {
327+
let queue = new ToastQueue();
328+
329+
function LocalToast(props) {
330+
return (
331+
<>
332+
<ToastRegion queue={queue} portalContainer={props.container}>
333+
{({toast}) => (
334+
<Toast toast={toast}>
335+
<ToastContent>
336+
<Text slot="title">{toast.content}</Text>
337+
</ToastContent>
338+
<Button slot="close">x</Button>
339+
</Toast>
340+
)}
341+
</ToastRegion>
342+
343+
<Button onPress={() => queue.add('Toast')}>Add toast</Button>
344+
</>
345+
);
346+
}
347+
function App() {
348+
let [container, setContainer] = React.useState();
349+
return (
350+
<>
351+
<LocalToast container={container} />
352+
<div ref={setContainer} data-testid="custom-container" />
353+
</>
354+
);
355+
}
356+
357+
let {getByRole, getByTestId} = render(<App />);
358+
359+
let button = getByRole('button');
360+
await user.click(button);
361+
362+
expect(within(getByTestId('custom-container')).getByRole('alertdialog')).toBeInTheDocument();
363+
});
325364
});

yarn.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28997,6 +28997,7 @@ __metadata:
2899728997
"@react-aria/focus": "npm:^3.20.1"
2899828998
"@react-aria/interactions": "npm:^3.24.1"
2899928999
"@react-aria/live-announcer": "npm:^3.4.1"
29000+
"@react-aria/ssr": "npm:^3.9.7"
2900029001
"@react-aria/toolbar": "npm:3.0.0-beta.14"
2900129002
"@react-aria/utils": "npm:^3.28.1"
2900229003
"@react-aria/virtualizer": "npm:^4.1.3"

0 commit comments

Comments
 (0)