-
Notifications
You must be signed in to change notification settings - Fork 1.3k
chore: Deprecate UNSTABLE_portalContainer in favor for PortalProvider #7976
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
Changes from 6 commits
15db448
e29a277
72cec38
b9b0995
26a5f2f
bf0a29f
580dbbb
0dd2ec9
f7b427c
8baed7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,127 @@ | ||||||
{/* Copyright 2025 Adobe. All rights reserved. | ||||||
This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||||||
you may not use this file except in compliance with the License. You may obtain a copy | ||||||
of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||||||
Unless required by applicable law or agreed to in writing, software distributed under | ||||||
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||||||
OF ANY KIND, either express or implied. See the License for the specific language | ||||||
governing permissions and limitations under the License. */} | ||||||
|
||||||
import {Layout} from '@react-spectrum/docs'; | ||||||
export default Layout; | ||||||
|
||||||
import docs from 'docs:@react-aria/overlays'; | ||||||
import {HeaderInfo, PropTable, FunctionAPI, PageDescription} from '@react-spectrum/docs'; | ||||||
import packageData from '@react-aria/overlays/package.json'; | ||||||
|
||||||
--- | ||||||
category: Utilities | ||||||
keywords: [overlays, portals] | ||||||
--- | ||||||
|
||||||
# PortalProvider | ||||||
|
||||||
<PageDescription>{docs.exports.UNSTABLE_PortalProvider.description}</PageDescription> | ||||||
|
||||||
<HeaderInfo | ||||||
packageData={packageData} | ||||||
componentNames={['UNSTABLE_PortalProvider', 'useUNSTABLE_PortalContext']} /> | ||||||
|
||||||
## Introduction | ||||||
|
||||||
`UNSTABLE_PortalProvider` is a utility wrapper component that can be used to set where components like | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know in our other docs pages that have UNSTABLE components/utilities we often don't refer to them as UNSTABLE. However, I've opted to keep that here to further highlight that this is truly UNSTABLE as in there could be unforeseen repercussions. I've however opted to NOT highlight the concerns outright (I think we had uncertainty about ariaHideOutside/focus scoping if I remember correctly) open to opinions There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my opinion #7976 (comment) :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks! I like the copy, my only other thought here would be whether UNSTABLE is the right marker since it may never move out of that sate, maybe it should be UNSAFE? We might have talked about this already actually, don't quite remember... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we talked about it, UNSAFE would be more accurate, we were worried about changing it, though I guess it was marked as UNSTABLE... so maybe this is the "release" of it where we decided to change the API to unsafe. I'm ok with doing that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i kinda like marking it as UNSAFE since it does seem more accurate. if it was UNSTABLE before and we change it now, then it lives up to its name as UNSTABLE i guess? |
||||||
Modals, Popovers, Toasts, and Tooltips will portal their overlay element to. This is typically used when | ||||||
your app is already portalling other elements to a location other than the `document.body` and thus requires | ||||||
your React Aria components to send their overlays to the same container. | ||||||
snowystinger marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
Please note that `UNSTABLE_PortalProvider` is considered `UNSTABLE` because it is an escape hatch, and there are | ||||||
many places that an application could portal to. Not all of them will work, either with styling, accessibility, | ||||||
or for a variety of other reasons. Typically, it is best to portal to the root of the entire application, typically the `body` element, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
outside of any possible overflow or stacking contexts. We envision `UNSTABLE_PortalProvider` being used to group all of the portalled | ||||||
elements into a single container at the root of the app or to control the order of children of the `body` element, but you may have use cases | ||||||
snowystinger marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
that need to do otherwise. | ||||||
|
||||||
## Props | ||||||
|
||||||
<PropTable links={docs.links} component={docs.exports.UNSTABLE_PortalProvider} /> | ||||||
|
||||||
## Example | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bit of a contrived simple example, open to suggestions on how complex I should make it. Also I wasn't able to get the styles from the RAC Toast pages to work here via the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should work: @import '../../../react-aria-components/docs/Button.mdx' layer(button);
@import "@react-aria/example-theme"; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah derp, thanks. I didn't realize that the util pages were in a different path of the site |
||||||
|
||||||
The example below shows how you can use `UNSTABLE_PortalProvider` to portal your Toasts to an arbitrary container. Note that | ||||||
the Toast in this example is taken directly from the [React Aria Components Toast documentation](Toast.html#example), please visit that page for | ||||||
a detailed explanation of its implementation. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add something like "This is a contrived example, we do not recommend portalling toasts to a container in the middle of an application." There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really want/need to point that out? IMHO, I think it should be fairly clear that this is a contrived example for the sake of illustrating the API, I figure people who come to this page will already have a defined use case in mind for the PortalProvider. Happy to add it if others also feel like it should be added, just trying to be as conservative as possible with the docs length as we've discussed in the past There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's fine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mainly opted not to use tooltips because it felt like a less likely use case (at least it felt like the PortalProvider came up more in a Toast context both from external and internal users haha) |
||||||
|
||||||
```tsx example | ||||||
import {UNSTABLE_PortalProvider} from '@react-aria/overlays'; | ||||||
///- begin collapse -/// | ||||||
import {UNSTABLE_ToastRegion as ToastRegion, UNSTABLE_Toast as Toast, UNSTABLE_ToastQueue as ToastQueue, UNSTABLE_ToastContent as ToastContent, Button, Text} from 'react-aria-components'; | ||||||
|
||||||
|
||||||
// Define the type for your toast content. | ||||||
interface MyToastContent { | ||||||
title: string, | ||||||
description?: string | ||||||
} | ||||||
|
||||||
// Create a global ToastQueue. | ||||||
const queue = new ToastQueue<MyToastContent>(); | ||||||
|
||||||
function MyToastRegion() { | ||||||
return ( | ||||||
<ToastRegion queue={queue}> | ||||||
{({toast}) => ( | ||||||
<Toast toast={toast}> | ||||||
<ToastContent> | ||||||
<Text slot="title">{toast.content.title}</Text> | ||||||
<Text slot="description">{toast.content.description}</Text> | ||||||
</ToastContent> | ||||||
<Button slot="close">x</Button> | ||||||
</Toast> | ||||||
)} | ||||||
</ToastRegion> | ||||||
|
||||||
); | ||||||
} | ||||||
///- end collapse -/// | ||||||
|
||||||
// See the above Toast docs link for the ToastRegion implementation | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment didn't appear. Maybe split this into two blocks: |
||||||
function App() { | ||||||
let container = React.useRef(null); | ||||||
return ( | ||||||
<> | ||||||
<UNSTABLE_PortalProvider getContainer={() => container.current}> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be valuable to highlight that you can use multiple of this PortalProvider if you wanted to change up where you were portalling things on a case to case basis? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't, would make the example too long and messy and it should be easy to figure out, also, the more you use the more likely you are to see odd behaviours if you're doing non-standard things |
||||||
<MyToastRegion /> | ||||||
<Button | ||||||
onPress={() => queue.add({ | ||||||
title: 'Toast complete!', | ||||||
description: 'Great success.' | ||||||
})}> | ||||||
Open Toast | ||||||
</Button> | ||||||
</UNSTABLE_PortalProvider> | ||||||
<div ref={container} style={{height: '70px', width: '200px', overflow: 'auto'}}> | ||||||
Toasts are portalled here! | ||||||
</div> | ||||||
</> | ||||||
); | ||||||
} | ||||||
|
||||||
<App /> | ||||||
``` | ||||||
LFDanLu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
## Contexts | ||||||
|
||||||
The `getContainer` set by the nearest PortalProvider can be accessed by calling `useUNSTABLE_PortalContext`. This can be | ||||||
used by custom overlay components to ensure that they are also being consistently portalled throughout your app. | ||||||
|
||||||
<FunctionAPI links={docs.links} function={docs.exports.useUNSTABLE_PortalContext} /> | ||||||
|
||||||
```tsx | ||||||
import {useUNSTABLE_PortalContext} from '@react-aria/overlays'; | ||||||
|
||||||
function MyOverlay(props) { | ||||||
let {children} = props; | ||||||
let {getContainer} = useUNSTABLE_PortalContext(); | ||||||
return ReactDOM.createPortal(children, getContainer()); | ||||||
} | ||||||
``` |
Uh oh!
There was an error while loading. Please reload this page.