diff --git a/CHANGELOG.md b/CHANGELOG.md index 08be060165..a941333fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix `Checkbox` style bug in background color [redlines] @bcalvery ([#1796](https://github.com/stardust-ui/react/pull/1796)) - Fix bidirectional `FocusZone` to land focus correctly on DOWN key press after series of UP arrow keys @sophieH29 ([#1794](https://github.com/stardust-ui/react/pull/1794)) - Fix `hand` icon in Teams theme @lucivpav ([#1782](https://github.com/stardust-ui/react/pull/1782)) +- Use `target` from `Provider` in `ReactDOM.createPortal()` calls @layershifter ([#1810](https://github.com/stardust-ui/react/pull/1810)) ### Features - Add `overwrite` prop to `Provider` @layershifter ([#1780](https://github.com/stardust-ui/react/pull/1780)) diff --git a/packages/react/src/components/Dialog/Dialog.tsx b/packages/react/src/components/Dialog/Dialog.tsx index bd5599736f..9710081dc5 100644 --- a/packages/react/src/components/Dialog/Dialog.tsx +++ b/packages/react/src/components/Dialog/Dialog.tsx @@ -1,6 +1,6 @@ import { Unstable_NestingAuto } from '@stardust-ui/react-component-nesting-registry' -import { documentRef, EventListener } from '@stardust-ui/react-component-event-listener' -import { Ref } from '@stardust-ui/react-component-ref' +import { EventListener } from '@stardust-ui/react-component-event-listener' +import { Ref, toRefObject } from '@stardust-ui/react-component-ref' import * as customPropTypes from '@stardust-ui/react-proptypes' import * as _ from 'lodash' import * as PropTypes from 'prop-types' @@ -273,6 +273,8 @@ class Dialog extends AutoControlledComponent, DialogStat ) + + const targetRef = toRefObject(this.context.target) const triggerAccessibility: TriggerAccessibility = { attributes: accessibility.attributes.trigger, keyHandlers: accessibility.keyHandlers.trigger, @@ -306,7 +308,7 @@ class Dialog extends AutoControlledComponent, DialogStat diff --git a/packages/react/src/components/Menu/MenuItem.tsx b/packages/react/src/components/Menu/MenuItem.tsx index 54cdb849ae..5c72ea72ae 100644 --- a/packages/react/src/components/Menu/MenuItem.tsx +++ b/packages/react/src/components/Menu/MenuItem.tsx @@ -1,5 +1,5 @@ -import { documentRef, EventListener } from '@stardust-ui/react-component-event-listener' -import { Ref } from '@stardust-ui/react-component-ref' +import { EventListener } from '@stardust-ui/react-component-event-listener' +import { Ref, toRefObject } from '@stardust-ui/react-component-ref' import * as customPropTypes from '@stardust-ui/react-proptypes' import * as _ from 'lodash' import cx from 'classnames' @@ -212,10 +212,11 @@ class MenuItem extends AutoControlledComponent, MenuIt indicator, disabled, } = this.props - const indicatorWithDefaults = indicator === undefined ? {} : indicator - const { menuOpen } = this.state + const indicatorWithDefaults = indicator === undefined ? {} : indicator + const targetRef = toRefObject(this.context.target) + const menuItemInner = childrenExist(children) ? ( children ) : ( @@ -272,7 +273,7 @@ class MenuItem extends AutoControlledComponent, MenuIt })} - + ) : null diff --git a/packages/react/src/components/Popup/Popup.tsx b/packages/react/src/components/Popup/Popup.tsx index 1a64d9b06c..4bd46e78c6 100644 --- a/packages/react/src/components/Popup/Popup.tsx +++ b/packages/react/src/components/Popup/Popup.tsx @@ -3,7 +3,6 @@ import { NodeRef, Unstable_NestingAuto } from '@stardust-ui/react-component-nest import { handleRef, toRefObject, Ref } from '@stardust-ui/react-component-ref' import * as customPropTypes from '@stardust-ui/react-proptypes' import * as React from 'react' -import * as ReactDOM from 'react-dom' import * as PropTypes from 'prop-types' import * as keyboardKey from 'keyboard-key' import * as _ from 'lodash' @@ -13,7 +12,6 @@ import { childrenExist, AutoControlledComponent, RenderResultConfig, - isBrowser, ChildrenComponentProps, ContentComponentProps, StyledComponentProps, @@ -39,6 +37,7 @@ import { ReactAccessibilityBehavior } from '../../lib/accessibility/reactTypes' import { createShorthandFactory } from '../../lib/factories' import createReferenceFromContextClick from './createReferenceFromContextClick' import isRightClick from '../../lib/isRightClick' +import PortalInner from '../Portal/PortalInner' export type PopupEvents = 'click' | 'hover' | 'focus' | 'context' export type RestrictedClickEvents = 'click' | 'focus' @@ -178,8 +177,6 @@ export default class Popup extends AutoControlledComponent {this.renderTrigger(accessibility)} {open && - (inline ? popupContent : mountNode && ReactDOM.createPortal(popupContent, mountNode))} + (inline ? popupContent : {popupContent})} ) } @@ -479,8 +476,9 @@ export default class Popup extends AutoControlledComponent @@ -535,13 +533,13 @@ export default class Popup extends AutoControlledComponent @@ -596,7 +594,9 @@ export default class Popup extends AutoControlledComponent { renderPortal(): JSX.Element | undefined { const { children, content, trapFocus } = this.props const { open } = this.state + const contentToRender = childrenExist(children) ? children : content const focusTrapZoneProps = (_.keys(trapFocus).length && trapFocus) || {} + const targetRef = toRefObject(this.context.target) return ( open && ( @@ -137,11 +139,7 @@ class Portal extends AutoControlledComponent { ) : ( contentToRender )} - + ) diff --git a/packages/react/src/components/Portal/PortalInner.tsx b/packages/react/src/components/Portal/PortalInner.tsx index 5832587513..0eabeebe0c 100644 --- a/packages/react/src/components/Portal/PortalInner.tsx +++ b/packages/react/src/components/Portal/PortalInner.tsx @@ -2,6 +2,9 @@ import * as PropTypes from 'prop-types' import * as _ from 'lodash' import * as React from 'react' import * as ReactDOM from 'react-dom' +// @ts-ignore +import { ThemeContext } from '@stardust-ui/react-fela' + import { isBrowser, ChildrenComponentProps, commonPropTypes } from '../../lib' export interface PortalInnerProps extends ChildrenComponentProps { @@ -27,6 +30,8 @@ export interface PortalInnerProps extends ChildrenComponentProps { * A PortalInner is a container for Portal's content. */ class PortalInner extends React.Component { + static contextType = ThemeContext + static propTypes = { ...commonPropTypes.createCommon({ accessibility: false, @@ -41,10 +46,6 @@ class PortalInner extends React.Component { onUnmount: PropTypes.func, } - static defaultProps = { - mountNode: isBrowser() ? document.body : null, - } - componentDidMount() { _.invoke(this.props, 'onMount', this.props) } @@ -56,7 +57,10 @@ class PortalInner extends React.Component { render() { const { children, mountNode } = this.props - return mountNode && ReactDOM.createPortal(children, mountNode) + const target: HTMLElement | null = isBrowser() ? this.context.target.body : null + const container: HTMLElement | null = mountNode || target + + return container && ReactDOM.createPortal(children, container) } } diff --git a/packages/react/src/components/Provider/Provider.tsx b/packages/react/src/components/Provider/Provider.tsx index 59e7c8948b..dcc2e706a5 100644 --- a/packages/react/src/components/Provider/Provider.tsx +++ b/packages/react/src/components/Provider/Provider.tsx @@ -156,6 +156,7 @@ class Provider extends React.Component> { rtl, disableAnimations, renderer, + target, } const incomingContext: ProviderContextPrepared = overwrite ? {} : this.context diff --git a/packages/react/src/components/Toolbar/ToolbarItem.tsx b/packages/react/src/components/Toolbar/ToolbarItem.tsx index dbcc79630c..4c31b7eb51 100644 --- a/packages/react/src/components/Toolbar/ToolbarItem.tsx +++ b/packages/react/src/components/Toolbar/ToolbarItem.tsx @@ -3,8 +3,8 @@ import * as _ from 'lodash' import * as PropTypes from 'prop-types' import * as customPropTypes from '@stardust-ui/react-proptypes' import cx from 'classnames' -import { Ref } from '@stardust-ui/react-component-ref' -import { documentRef, EventListener } from '@stardust-ui/react-component-event-listener' +import { Ref, toRefObject } from '@stardust-ui/react-component-ref' +import { EventListener } from '@stardust-ui/react-component-event-listener' import { UIComponent, @@ -173,6 +173,8 @@ class ToolbarItem extends UIComponent, ToolbarItemS menuRef = React.createRef() renderSubmenu(menu, variables) { + const targetRef = toRefObject(this.context.target) + return ( <> @@ -194,7 +196,7 @@ class ToolbarItem extends UIComponent, ToolbarItemS diff --git a/packages/react/src/components/Tooltip/Tooltip.tsx b/packages/react/src/components/Tooltip/Tooltip.tsx index b47432a8a0..2456ced049 100644 --- a/packages/react/src/components/Tooltip/Tooltip.tsx +++ b/packages/react/src/components/Tooltip/Tooltip.tsx @@ -1,7 +1,6 @@ import { toRefObject, Ref } from '@stardust-ui/react-component-ref' import * as customPropTypes from '@stardust-ui/react-proptypes' import * as React from 'react' -import * as ReactDOM from 'react-dom' import * as PropTypes from 'prop-types' import * as _ from 'lodash' @@ -10,7 +9,6 @@ import { childrenExist, AutoControlledComponent, RenderResultConfig, - isBrowser, ChildrenComponentProps, ContentComponentProps, StyledComponentProps, @@ -31,6 +29,7 @@ import TooltipContent, { TooltipContentProps } from './TooltipContent' import { tooltipBehavior } from '../../lib/accessibility' import { Accessibility } from '../../lib/accessibility/types' import { ReactAccessibilityBehavior } from '../../lib/accessibility/reactTypes' +import PortalInner from '../Portal/PortalInner' export interface TooltipSlotClassNames { content: string @@ -120,7 +119,6 @@ export default class Tooltip extends AutoControlledComponent )} - {mountNode && ReactDOM.createPortal(tooltipContent, mountNode)} + {tooltipContent} ) } diff --git a/packages/react/src/lib/mergeProviderContexts.ts b/packages/react/src/lib/mergeProviderContexts.ts index 899c502f92..8bd3b91cd1 100644 --- a/packages/react/src/lib/mergeProviderContexts.ts +++ b/packages/react/src/lib/mergeProviderContexts.ts @@ -25,6 +25,7 @@ const mergeProviderContexts = ( rtl: false, disableAnimations: false, originalThemes: [], + target: document, } as ProviderContextPrepared return contexts.reduce( @@ -39,6 +40,9 @@ const mergeProviderContexts = ( acc.rtl = mergedRTL } + // Use provided renderer if it is defined + acc.target = next.target || acc.target + // Use provided renderer if it is defined acc.renderer = next.renderer || acc.renderer diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 47940fb8eb..d81551c7cb 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -157,6 +157,7 @@ export interface ProviderContextInput { renderer?: Renderer rtl?: boolean disableAnimations?: boolean + target?: Document theme?: ThemeInput } @@ -164,6 +165,7 @@ export interface ProviderContextPrepared { renderer: Renderer rtl: boolean disableAnimations: boolean + target: Document theme: ThemePrepared originalThemes: (ThemeInput | undefined)[] } diff --git a/packages/react/test/specs/components/PortalInner/PortalInner-test.tsx b/packages/react/test/specs/components/PortalInner/PortalInner-test.tsx index 1c90299272..4a07e57831 100644 --- a/packages/react/test/specs/components/PortalInner/PortalInner-test.tsx +++ b/packages/react/test/specs/components/PortalInner/PortalInner-test.tsx @@ -1,10 +1,10 @@ import * as React from 'react' -import { mount } from 'enzyme' import PortalInner, { PortalInnerProps } from 'src/components/Portal/PortalInner' +import { mountWithProvider } from 'test/utils' const mountPortalInner = (props: PortalInnerProps) => - mount( + mountWithProvider(

, diff --git a/packages/react/test/utils/withProvider.tsx b/packages/react/test/utils/withProvider.tsx index 7b47426d4d..94bb7e5119 100644 --- a/packages/react/test/utils/withProvider.tsx +++ b/packages/react/test/utils/withProvider.tsx @@ -5,7 +5,7 @@ import { felaRenderer } from 'src/lib' import { ThemeInput } from 'src/themes/types' export const EmptyThemeProvider: React.FunctionComponent = ({ children }) => ( - {children} + {children} ) export const mountWithProvider = (node, options?, theme?: ThemeInput) => {