Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions packages/react-aria-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@react-aria/focus": "^3.20.1",
"@react-aria/interactions": "^3.24.1",
"@react-aria/live-announcer": "^3.4.1",
"@react-aria/ssr": "^3.9.7",
"@react-aria/toolbar": "3.0.0-beta.14",
"@react-aria/utils": "^3.28.1",
"@react-aria/virtualizer": "^4.1.3",
Expand Down
15 changes: 12 additions & 3 deletions packages/react-aria-components/src/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {forwardRefType} from '@react-types/shared';
import {QueuedToast, ToastQueue, ToastState, useToastQueue} from 'react-stately';
import React, {createContext, ForwardedRef, forwardRef, HTMLAttributes, JSX, ReactElement, useContext} from 'react';
import {TextContext} from './Text';
import {useIsSSR} from '@react-aria/ssr';
import {useObjectRef} from '@react-aria/utils';

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

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

let {portalContainer = isSSR ? null : document.body} = props;

let region = (
<ToastStateContext.Provider value={state}>
<div
Expand All @@ -77,8 +86,8 @@ export const ToastRegion = /*#__PURE__*/ (forwardRef as forwardRefType)(function
</ToastStateContext.Provider>
);

return state.visibleToasts.length > 0 && typeof document !== 'undefined'
? createPortal(region, document.body)
return state.visibleToasts.length > 0 && portalContainer
? createPortal(region, portalContainer)
: null;
});

Expand Down
39 changes: 39 additions & 0 deletions packages/react-aria-components/test/Toast.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,4 +322,43 @@ describe('Toast', () => {
let region = getByRole('region');
expect(region).toHaveAttribute('aria-label', 'Toasts');
});

it('should render the toast region in the portal container', async () => {
let queue = new ToastQueue();

function LocalToast(props) {
return (
<>
<ToastRegion queue={queue} portalContainer={props.container}>
{({toast}) => (
<Toast toast={toast}>
<ToastContent>
<Text slot="title">{toast.content}</Text>
</ToastContent>
<Button slot="close">x</Button>
</Toast>
)}
</ToastRegion>

<Button onPress={() => queue.add('Toast')}>Add toast</Button>
</>
);
}
function App() {
let [container, setContainer] = React.useState();
return (
<>
<LocalToast container={container} />
<div ref={setContainer} data-testid="custom-container" />
</>
);
}

let {getByRole, getByTestId} = render(<App />);

let button = getByRole('button');
await user.click(button);

expect(within(getByTestId('custom-container')).getByRole('alertdialog')).toBeInTheDocument();
});
});
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -28997,6 +28997,7 @@ __metadata:
"@react-aria/focus": "npm:^3.20.1"
"@react-aria/interactions": "npm:^3.24.1"
"@react-aria/live-announcer": "npm:^3.4.1"
"@react-aria/ssr": "npm:^3.9.7"
"@react-aria/toolbar": "npm:3.0.0-beta.14"
"@react-aria/utils": "npm:^3.28.1"
"@react-aria/virtualizer": "npm:^4.1.3"
Expand Down