Repository: microsoft/fluent-ui-react (ts)

Issue:
Scrollbar inside a Dialog doesn't get Provider styles
## Bug Report
A scroll bar inside a `Dialog` component doesn't get `Provider`'s theme. The issue arises as the dialog is rendered as sibling to root element, it ends up without a provider wrapper.

![sibling](https://user-images.githubusercontent.com/43226021/70012314-f31f2080-1528-11ea-9db4-3f160d0b53a9.png)

### Steps
Render a `List` of items inside a `Dialog` component with styles override of `overflow: scroll`, you will see that the scroll bar styles is not in par with Provider theme. See the codesandbox link below to replicate the issue.

### Expected Result
![scroll-bar-with-provider](https://user-images.githubusercontent.com/43226021/70007611-d33a2d80-1525-11ea-9a62-cd0921604ec0.png)


### Actual Result
![scroll-bar-without-provider](https://user-images.githubusercontent.com/43226021/70007613-d6cdb480-1525-11ea-92fa-6c28b7df6c3a.png)

### Version
0.40.4

### Testcase
<a href="https://codesandbox.io/s/stardust-ui-example-tmvgh" target="_blank">
  <img alt="Edit stardust-ui-example" src="https://codesandbox.io/static/img/play-codesandbox.svg">
</a>

Pull request description:
fix(Provider): render portals inside styled elements
Fixes #2138.

# BREAKING CHANGES

- `ProviderBox` component was removed
- styles from `ProviderBox` are moved to `Provider`

---

`ProviderBox` was previously used to set styles for DOM tree. It was impossible to do it within `Provider` previously due technical limitations of `UIComponent`.

This PR also solves #2138 as creates elements where components that use `createPortal()` will be rendered.

### Before

```html
<body>
  <div class="ui popup" />
</body>
```

### After

```html
<body>
  <div class="ui provider">
    <div class="ui popup" />
  </div>
</body>
```


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4967d48af..3e40ddf9c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - Styles for the `Animation` component are removed from Teams theme @layershifter ([#2258](https://github.com/microsoft/fluent-ui-react/pull/2258))
 - Styles from `Popup` were moved `PopupContent` component @layershifter ([#2333](https://github.com/microsoft/fluent-ui-react/pull/2333))
 - Styles from `Tooltip` were moved `TooltipContent` component @layershifter ([#2333](https://github.com/microsoft/fluent-ui-react/pull/2333))
+- `ProviderBox` component was removed @layershifter ([#2192](https://github.com/microsoft/fluent-ui-react/pull/2192))
+- Styles and variables from `ProviderBox` are moved to `Provider` @layershifter ([#2192](https://github.com/microsoft/fluent-ui-react/pull/2192))
 
 ### Fixes
 - Remove dependency on Lodash in TypeScript typings @layershifter ([#2323](https://github.com/microsoft/fluent-ui-react/pull/2323))
@@ -33,6 +35,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - Use referentially equal objects for `actions` in `useStateManager` @layershifter ([#2347](https://github.com/microsoft/fluent-ui-react/pull/2347))
 - Fix `Animation` component not to throw when `children` is not provided @mnajdova ([#2345](https://github.com/microsoft/fluent-ui-react/pull/2345))
 - Fix `loader` - adding labeling when loader get focus @kolaps33 ([#2352](https://github.com/microsoft/fluent-ui-react/pull/2352))
+- Styles from `Provider` are applied to components that rendered out of it in DOM @layershifter ([#2192](https://github.com/microsoft/fluent-ui-react/pull/2192))
 
 ### Features
 - Added sourcemaps to the dist output to simplify debugging @miroslavstastny ([#2329](https://github.com/microsoft/fluent-ui-react/pull/2329))
diff --git a/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx b/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx
index 40de177c1..0c340ce3c 100644
--- a/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx
+++ b/docs/src/components/ComponentDoc/ComponentExample/ComponentExample.tsx
@@ -461,7 +461,7 @@ class ComponentExample extends React.Component<ComponentExampleProps, ComponentE
     const newTheme: ThemeInput = {
       componentVariables: {
         ...componentVariables,
-        ProviderBox: { background: showTransparent ? 'initial' : undefined },
+        Provider: { background: showTransparent ? 'initial' : undefined },
       },
     }
     const exampleStyles = {
diff --git a/docs/src/examples/components/Dialog/Variations/DialogExampleScroll.shorthand.steps.ts b/docs/src/examples/components/Dialog/Variations/DialogExampleScroll.shorthand.steps.ts
new file mode 100644
index 000000000..526218835
--- /dev/null
+++ b/docs/src/examples/components/Dialog/Variations/DialogExampleScroll.shorthand.steps.ts
@@ -0,0 +1,8 @@
+import getScreenerSteps from '../commonScreenerSteps'
+
+const config: ScreenerTestsConfig = {
+  themes: ['teams', 'teamsDark', 'teamsHighContrast'],
+  steps: getScreenerSteps(),
+}
+
+export default config
diff --git a/docs/src/examples/components/Dialog/Variations/DialogExampleScroll.shorthand.tsx b/docs/src/examples/components/Dialog/Variations/DialogExampleScroll.shorthand.tsx
new file mode 100644
index 000000000..004ef5c38
--- /dev/null
+++ b/docs/src/examples/components/Dialog/Variations/DialogExampleScroll.shorthand.tsx
@@ -0,0 +1,32 @@
+import { Button, Dialog } from '@fluentui/react'
+import * as React from 'react'
+import * as _ from 'lodash'
+
+const DialogExampleScroll = () => (
+  <Dialog
+    content={{
+      content: (
+        <>
+          {_.times(10, i => (
+            <p key={i}>
+              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent ornare, neque eget
+              egestas fermentum, massa risus mollis orci, et ullamcorper purus turpis at risus.
+              Fusce pharetra mollis sapien nec commodo. Quisque ut congue sem, vel aliquam augue.
+              Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia
+              Curae; Duis porttitor, nunc quis dapibus imperdiet, ligula sem egestas orci, sed
+              volutpat ipsum felis vitae velit. Sed maximus egestas dui elementum aliquam. In hac
+              habitasse platea dictumst. Proin maximus nibh velit, ut ornare dui mollis viverra.
+            </p>
+          ))}
+        </>
+      ),
+      styles: {
+        height: '500px',
+        overflow: 'scroll',
+      },
+    }}
+    trigger={<Button content="Open a dialog" />}
+  />
+)
+
+export default DialogExampleScroll
diff --git a/docs/src/examples/components/Dialog/index.tsx b/docs/src/examples/components/Dialog/index.tsx
index 4e8c1eb86..e088897d1 100644
--- a/docs/src/examples/components/Dialog/index.tsx
+++ b/docs/src/examples/components/Dialog/index.tsx
@@ -6,6 +6,9 @@ import Rtl from './Rtl'
 import Variations from './Variations'
 import Usage from './Usage'
 
+import NonPublicSection from 'docs/src/components/ComponentDoc/NonPublicSection'
+import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample/ComponentExample'
+
 const DialogExamples = () => (
   <>
     <Types />
@@ -13,6 +16,9 @@ const DialogExamples = () => (
     <Content />
     <Usage />
     <Rtl />
+    <NonPublicSection title="Visual tests">
+      <ComponentExample examplePath="components/Dialog/Variations/DialogExampleScroll" />
+    </NonPublicSection>
   </>
 )
 
diff --git a/docs/src/examples/components/Provider/Types/ProviderExampleStyles.tsx b/docs/src/examples/components/Provider/Types/ProviderExampleStyles.tsx
new file mode 100644
index 000000000..8dfd3567e
--- /dev/null
+++ b/docs/src/examples/components/Provider/Types/ProviderExampleStyles.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react'
+import { mergeThemes, Provider, themes } from '@fluentui/react'
+
+const ProviderExampleShorthand = () => (
+  <Provider
+    variables={{ background: 'red' }}
+    design={{ padding: '20px' }}
+    styles={{ borderBottom: '3px solid blue' }}
+    theme={mergeThemes(themes.teamsDark, {
+      siteVariables: {
+        bodyBackground: 'salmon',
+      },
+      componentVariables: {
+        Provider: {
+          color: 'yellow',
+        },
+      },
+      componentStyles: {
+        Provider: {
+          root: {
+            borderTop: '3px solid green',
+          },
+        },
+      },
+    })}
+  >
+    <p>with custom overrides</p>
+  </Provider>
+)
+
+export default ProviderExampleShorthand
diff --git a/docs/src/examples/components/Provider/Types/index.tsx b/docs/src/examples/components/Provider/Types/index.tsx
index a26553e56..576c53a32 100644
--- a/docs/src/examples/components/Provider/Types/index.tsx
+++ b/docs/src/examples/components/Provider/Types/index.tsx
@@ -25,8 +25,7 @@ const Types = () => (
     </ExampleSection>
     <NonPublicSection title="Types for visual tests">
       <ComponentExample examplePath="components/Provider/Types/ProviderExampleScrollbar" />
-    </NonPublicSection>
-    <NonPublicSection title="Types for visual tests">
+      <ComponentExample examplePath="components/Provider/Types/ProviderExampleStyles" />
       <ComponentExample examplePath="components/Provider/Types/ProviderExampleRendererFelaPluginFallbackValue" />
     </NonPublicSection>
   </>
diff --git a/packages/react-bindings/src/index.ts b/packages/react-bindings/src/index.ts
index 502312b07..f8426645f 100644
--- a/packages/react-bindings/src/index.ts
+++ b/packages/react-bindings/src/index.ts
@@ -11,10 +11,10 @@ export * from './FocusZone/focusUtilities'
 
 export { default as useAccessibility } from './hooks/useAccessibility'
 export { default as useAutoControlled } from './hooks/useAutoControlled'
-export { default as useStyles } from './hooks/useStyles'
 export { default as unstable_useDispatchEffect } from './hooks/useDispatchEffect'
 export { default as useIsomorphicLayoutEffect } from './hooks/useIsomorphicLayoutEffect'
 export { default as useStateManager } from './hooks/useStateManager'
+export { default as useStyles } from './hooks/useStyles'
 
 export { default as unstable_createAnimationStyles } from './styles/createAnimationStyles'
 export { default as unstable_getStyles } from './styles/getStyles'
diff --git a/packages/react/src/components/Dialog/Dialog.tsx b/packages/react/src/components/Dialog/Dialog.tsx
index 6485a452a..2c3f57c04 100644
--- a/packages/react/src/components/Dialog/Dialog.tsx
+++ b/packages/react/src/components/Dialog/Dialog.tsx
@@ -287,8 +287,6 @@ class Dialog extends AutoControlledComponent<WithAsProp<DialogProps>, DialogStat
       <Ref innerRef={this.contentRef}>
         <ElementType
           className={classes.root}
-          // it's required to have an `rtl` attribute there as Dialog is rendered outside the main DOM tree
-          dir={rtl ? 'rtl' : undefined}
           {...accessibility.attributes.popup}
           {...unhandledProps}
           {...applyAccessibilityKeyHandlers(accessibility.keyHandlers.popup, unhandledProps)}
diff --git a/packages/react/src/components/Popup/Popup.tsx b/packages/react/src/components/Popup/Popup.tsx
index 076a923d7..b8933c62e 100644
--- a/packages/react/src/components/Popup/Popup.tsx
+++ b/packages/react/src/components/Popup/Popup.tsx
@@ -470,12 +470,12 @@ export default class Popup extends AutoControlledComponent<PopupProps, PopupStat
         rtl={rtl}
         unstable_pinned={unstable_pinned}
         targetRef={this.rightClickReferenceObject || target || this.triggerRef}
-        children={this.renderPopperChildren(rtl, accessibility)}
+        children={this.renderPopperChildren(accessibility)}
       />
     )
   }
 
-  renderPopperChildren = (rtl: boolean, accessibility: ReactAccessibilityBehavior) => ({
+  renderPopperChildren = (accessibility: ReactAccessibilityBehavior) => ({
     placement,
     scheduleUpdate,
   }: PopperChildrenProps) => {
@@ -491,7 +491,6 @@ export default class Popup extends AutoControlledComponent<PopupProps, PopupStat
     const content = renderContent ? renderContent(scheduleUpdate) : propsContent
     const popupContent = Popup.Content.create(content || {}, {
       defaultProps: () => ({
-        ...(rtl && { dir: 'rtl' }),
         ...accessibility.attributes.popup,
         ...accessibility.keyHandlers.popup,
         ...this.getContentProps(),
diff --git a/packages/react/src/components/Portal/PortalInner.tsx b/packages/react/src/components/Portal/PortalInner.tsx
index 901963b79..2a01fec37 100644
--- a/packages/react/src/components/Portal/PortalInner.tsx
+++ b/packages/react/src/components/Portal/PortalInner.tsx
@@ -1,11 +1,10 @@
-import * as PropTypes from 'prop-types'
 import * as _ from 'lodash'
+import * as PropTypes from 'prop-types'
 import * as React from 'react'
 import * as ReactDOM from 'react-dom'
-// @ts-ignore
-import { ThemeContext } from 'react-fela'
 
 import { isBrowser, ChildrenComponentProps, commonPropTypes } from '../../utils'
+import { PortalBoxContext } from '../Provider/usePortalBox'
 
 export interface PortalInnerProps extends ChildrenComponentProps {
   /** Existing element the portal should be bound to. */
@@ -30,7 +29,7 @@ export interface PortalInnerProps extends ChildrenComponentProps {
  * A PortalInner is a container for Portal's content.
  */
 class PortalInner extends React.Component<PortalInnerProps> {
-  static contextType = ThemeContext
+  static contextType = PortalBoxContext
 
   static propTypes = {
     ...commonPropTypes.createCommon({
@@ -56,7 +55,9 @@ class PortalInner extends React.Component<PortalInnerProps> {
   render() {
     const { children, mountNode } = this.props
 
-    const target: HTMLElement | null = isBrowser() ? this.context.target.body : null
+    // PortalInner should render elements even without a context
+    // eslint-disable-next-line
+    const target: HTMLDivElement | null = isBrowser() ? this.context || document.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 de02656fb..d37648b65 100644
--- a/packages/react/src/components/Provider/Provider.tsx
+++ b/packages/react/src/components/Provider/Provider.tsx
@@ -1,10 +1,12 @@
 import { IStyle } from 'fela'
 import * as _ from 'lodash'
 import {
+  getElementType,
   getUnhandledProps,
   Renderer,
   StylesContextPerformance,
   Telemetry,
+  unstable_getStyles,
   useIsomorphicLayoutEffect,
 } from '@fluentui/react-bindings'
 import {
@@ -13,7 +15,6 @@ import {
   StaticStyle,
   StaticStyleFunction,
   FontFace,
-  ComponentVariablesInput,
   ThemeInput,
   SiteVariablesPrepared,
 } from '@fluentui/styles'
@@ -22,10 +23,13 @@ import * as React from 'react'
 // @ts-ignore
 import { RendererProvider, ThemeProvider, ThemeContext } from 'react-fela'
 
-import { ChildrenComponentProps, setUpWhatInput, tryCleanupWhatInput } from '../../utils'
+import {
+  ChildrenComponentProps,
+  setUpWhatInput,
+  tryCleanupWhatInput,
+  UIComponentProps,
+} from '../../utils'
 
-import ProviderConsumer from './ProviderConsumer'
-import ProviderBox, { ProviderBoxProps } from './ProviderBox'
 import {
   WithAsProp,
   ProviderContextInput,
@@ -33,8 +37,10 @@ import {
   withSafeTypeForAs,
 } from '../../types'
 import mergeContexts from '../../utils/mergeProviderContexts'
+import ProviderConsumer from './ProviderConsumer'
+import usePortalBox, { PortalBoxContext } from './usePortalBox'
 
-export interface ProviderProps extends ChildrenComponentProps {
+export interface ProviderProps extends ChildrenComponentProps, UIComponentProps {
   renderer?: Renderer
   rtl?: boolean
   disableAnimations?: boolean
@@ -42,7 +48,6 @@ export interface ProviderProps extends ChildrenComponentProps {
   overwrite?: boolean
   target?: Document
   theme?: ThemeInput
-  variables?: ComponentVariablesInput
   telemetryRef?: React.MutableRefObject<Telemetry>
 }
 
@@ -99,11 +104,13 @@ const renderStaticStyles = (
  * The Provider passes the CSS-in-JS renderer, theme styles and other settings to Fluent UI components.
  */
 const Provider: React.FC<WithAsProp<ProviderProps>> & {
-  Box: typeof ProviderBox
+  className: string
   Consumer: typeof ProviderConsumer
   handledProps: (keyof ProviderProps)[]
 } = props => {
-  const { as, children, overwrite, variables, telemetryRef } = props
+  const { children, className, design, overwrite, styles, variables, telemetryRef } = props
+
+  const ElementType = getElementType(props)
   const unhandledProps = getUnhandledProps(Provider.handledProps, props)
 
   const telemetry = React.useMemo<Telemetry | undefined>(() => {
@@ -143,6 +150,30 @@ const Provider: React.FC<WithAsProp<ProviderProps>> & {
     rtlProps.dir = outgoingContext.rtl ? 'rtl' : 'ltr'
   }
 
+  const { classes } = unstable_getStyles({
+    className: Provider.className,
+    displayName: Provider.displayName,
+    props: {
+      className,
+      design,
+      styles,
+      variables,
+    },
+
+    disableAnimations: outgoingContext.disableAnimations,
+    performance: outgoingContext.performance,
+    renderer: outgoingContext.renderer,
+    rtl: outgoingContext.rtl,
+    theme: outgoingContext.theme,
+    saveDebug: _.noop,
+  })
+
+  const element = usePortalBox({
+    className: classes.root,
+    target: outgoingContext.target,
+    rtl: outgoingContext.rtl,
+  })
+
   useIsomorphicLayoutEffect(() => {
     renderFontFaces(outgoingContext.renderer, props.theme)
     renderStaticStyles(outgoingContext.renderer, props.theme, outgoingContext.theme.siteVariables)
@@ -158,20 +189,31 @@ const Provider: React.FC<WithAsProp<ProviderProps>> & {
     }
   }, [])
 
+  // do not spread anything - React.Fragment can only have `key` and `children` props
+  const elementProps =
+    ElementType === React.Fragment
+      ? {}
+      : {
+          className: classes.root,
+          ...rtlProps,
+          ...unhandledProps,
+        }
+
   return (
     <RendererProvider
       renderer={outgoingContext.renderer}
       {...{ rehydrate: false, targetDocument: outgoingContext.target }}
     >
       <ThemeProvider theme={outgoingContext} overwrite>
-        <ProviderBox as={as} variables={variables} {...unhandledProps} {...rtlProps}>
-          {children}
-        </ProviderBox>
+        <PortalBoxContext.Provider value={element}>
+          <ElementType {...elementProps}>{children}</ElementType>
+        </PortalBoxContext.Provider>
       </ThemeProvider>
     </RendererProvider>
   )
 }
 
+Provider.className = 'ui-provider'
 Provider.displayName = 'Provider'
 
 Provider.defaultProps = {
@@ -179,7 +221,9 @@ Provider.defaultProps = {
 }
 Provider.propTypes = {
   as: PropTypes.elementType,
+  design: PropTypes.object,
   variables: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
+  styles: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
   theme: PropTypes.shape({
     siteVariables: PropTypes.object,
     componentVariables: PropTypes.object,
@@ -220,6 +264,5 @@ Provider.propTypes = {
 Provider.handledProps = Object.keys(Provider.propTypes) as any
 
 Provider.Consumer = ProviderConsumer
-Provider.Box = ProviderBox
 
-export default withSafeTypeForAs<typeof Provider, ProviderProps & ProviderBoxProps>(Provider)
+export default withSafeTypeForAs<typeof Provider, ProviderProps>(Provider)
diff --git a/packages/react/src/components/Provider/ProviderBox.tsx b/packages/react/src/components/Provider/ProviderBox.tsx
deleted file mode 100644
index d92b0dea6..000000000
--- a/packages/react/src/components/Provider/ProviderBox.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import * as React from 'react'
-import {
-  commonPropTypes,
-  ContentComponentProps,
-  ChildrenComponentProps,
-  UIComponentProps,
-} from '../../utils'
-import createComponentInternal from '../../utils/createComponentInternal'
-import { WithAsProp, withSafeTypeForAs } from '../../types'
-
-export interface ProviderBoxProps
-  extends UIComponentProps<ProviderBoxProps>,
-    ContentComponentProps,
-    ChildrenComponentProps {}
-
-const ProviderBox = createComponentInternal<WithAsProp<ProviderBoxProps>>({
-  displayName: 'ProviderBox',
-
-  className: 'ui-provider__box',
-
-  propTypes: {
-    ...commonPropTypes.createCommon(),
-  },
-
-  render(config, props) {
-    const { ElementType, classes, unhandledProps } = config
-    const { children } = props
-    if (ElementType === React.Fragment) {
-      // do not spread anything - React.Fragment can only have `key` and `children` props.
-      return <>{children}</>
-    }
-
-    return (
-      <ElementType className={classes.root} {...unhandledProps}>
-        {children}
-      </ElementType>
-    )
-  },
-})
-
-/**
- * The ProviderBox passes the CSS-in-JS renderer, theme styles and other settings to Fluent UI components.
- * Also, being comapred to Provider, it additionally renders an element to the DOM (`div` by default).
- */
-export default withSafeTypeForAs<typeof ProviderBox, ProviderBoxProps>(ProviderBox)
diff --git a/packages/react/src/components/Provider/usePortalBox.ts b/packages/react/src/components/Provider/usePortalBox.ts
new file mode 100644
index 000000000..fc7bc445d
--- /dev/null
+++ b/packages/react/src/components/Provider/usePortalBox.ts
@@ -0,0 +1,48 @@
+import { useIsomorphicLayoutEffect } from '@fluentui/react-bindings'
+import * as React from 'react'
+
+import isBrowser from '../../utils/isBrowser'
+
+type UsePortalBoxOptions = {
+  className: string
+  rtl: boolean
+  target: Document
+}
+
+export const PortalBoxContext = React.createContext<HTMLDivElement>(null)
+
+const usePortalBox = (options: UsePortalBoxOptions): HTMLDivElement => {
+  const { className, rtl, target } = options
+
+  const element: HTMLDivElement | null = React.useMemo(
+    () => (isBrowser() ? target.createElement('div') : null),
+    [target],
+  )
+
+  useIsomorphicLayoutEffect(() => {
+    if (element) {
+      target.body.appendChild(element)
+    }
+
+    return () => {
+      if (element) {
+        target.body.removeChild(element)
+      }
+    }
+  }, [])
+  useIsomorphicLayoutEffect(() => {
+    if (element) {
+      element.setAttribute('class', className)
+
+      if (rtl) {
+        element.setAttribute('dir', 'rtl')
+      } else {
+        element.removeAttribute('dir')
+      }
+    }
+  }, [className, rtl])
+
+  return element
+}
+
+export default usePortalBox
diff --git a/packages/react/src/components/Tooltip/Tooltip.tsx b/packages/react/src/components/Tooltip/Tooltip.tsx
index 913392aa3..875a2b645 100644
--- a/packages/react/src/components/Tooltip/Tooltip.tsx
+++ b/packages/react/src/components/Tooltip/Tooltip.tsx
@@ -252,18 +252,17 @@ export default class Tooltip extends AutoControlledComponent<TooltipProps, Toolt
         enabled={open}
         rtl={rtl}
         targetRef={target || this.triggerRef}
-        children={this.renderPopperChildren(rtl, accessibility)}
+        children={this.renderPopperChildren(accessibility)}
       />
     )
   }
 
-  renderPopperChildren = (rtl: boolean, accessibility: ReactAccessibilityBehavior) => ({
+  renderPopperChildren = (accessibility: ReactAccessibilityBehavior) => ({
     placement,
   }: PopperChildrenProps) => {
     const { content, pointing } = this.props
 
     const tooltipContentAttributes = {
-      ...(rtl && { dir: 'rtl' }),
       ...accessibility.attributes.tooltip,
       ...accessibility.keyHandlers.tooltip,
       ...this.getContentProps(),
diff --git a/packages/react/src/themes/teams-dark/componentVariables.ts b/packages/react/src/themes/teams-dark/componentVariables.ts
index 2d8cf3386..9efb95edf 100644
--- a/packages/react/src/themes/teams-dark/componentVariables.ts
+++ b/packages/react/src/themes/teams-dark/componentVariables.ts
@@ -14,6 +14,6 @@ export { default as Menu } from './components/Menu/menuVariables'
 export { default as Icon } from './components/Icon/iconVariables'
 export { default as Reaction } from './components/Reaction/reactionVariables'
 export { default as Alert } from './components/Alert/alertVariables'
-export { default as ProviderBox } from './components/Provider/providerBoxVariables'
+export { default as Provider } from './components/Provider/providerVariables'
 export { default as Dropdown } from './components/Dropdown/dropdownVariables'
 export { default as Label } from './components/Label/labelVariables'
diff --git a/packages/react/src/themes/teams-dark/components/Provider/providerBoxVariables.ts b/packages/react/src/themes/teams-dark/components/Provider/providerBoxVariables.ts
deleted file mode 100644
index 5294222da..000000000
--- a/packages/react/src/themes/teams-dark/components/Provider/providerBoxVariables.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { ProviderBoxVariables } from '../../../teams/components/Provider/providerBoxVariables'
-
-export default (siteVariables): Partial<ProviderBoxVariables> => ({
-  scrollbarThumbBackgroundColor: siteVariables.colors.grey[450],
-  scrollbarThumbHoverBackgroundColor: siteVariables.colors.grey[400],
-})
diff --git a/packages/react/src/themes/teams-dark/components/Provider/providerVariables.ts b/packages/react/src/themes/teams-dark/components/Provider/providerVariables.ts
new file mode 100644
index 000000000..6474d9b9e
--- /dev/null
+++ b/packages/react/src/themes/teams-dark/components/Provider/providerVariables.ts
@@ -0,0 +1,6 @@
+import { ProviderVariables } from '../../../teams/components/Provider/providerVariables'
+
+export default (siteVariables): Partial<ProviderVariables> => ({
+  scrollbarThumbBackgroundColor: siteVariables.colors.grey[450],
+  scrollbarThumbHoverBackgroundColor: siteVariables.colors.grey[400],
+})
diff --git a/packages/react/src/themes/teams-high-contrast/componentVariables.ts b/packages/react/src/themes/teams-high-contrast/componentVariables.ts
index 5a5a4b385..4f53420e2 100644
--- a/packages/react/src/themes/teams-high-contrast/componentVariables.ts
+++ b/packages/react/src/themes/teams-high-contrast/componentVariables.ts
@@ -19,7 +19,7 @@ export { default as HierarchicalTreeTitle } from './components/HierarchicalTree/
 export { default as Status } from './components/Status/statusVariables'
 export { default as Reaction } from './components/Reaction/reactionVariables'
 export { default as Alert } from './components/Alert/alertVariables'
-export { default as ProviderBox } from './components/Provider/providerBoxVariables'
+export { default as Provider } from './components/Provider/providerVariables'
 export { default as Dropdown } from './components/Dropdown/dropdownVariables'
 export { default as Label } from './components/Label/labelVariables'
 export { default as TooltipContent } from './components/Tooltip/tooltipContentVariables'
diff --git a/packages/react/src/themes/teams-high-contrast/components/Provider/providerBoxVariables.ts b/packages/react/src/themes/teams-high-contrast/components/Provider/providerBoxVariables.ts
deleted file mode 100644
index 238cfdb99..000000000
--- a/packages/react/src/themes/teams-high-contrast/components/Provider/providerBoxVariables.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { ProviderBoxVariables } from '../../../teams/components/Provider/providerBoxVariables'
-
-export default (siteVariables): Partial<ProviderBoxVariables> => ({
-  scrollbarThumbBackgroundColor: siteVariables.colors.white,
-  scrollbarThumbHoverBackgroundColor: siteVariables.colors.white,
-})
diff --git a/packages/react/src/themes/teams-high-contrast/components/Provider/providerVariables.ts b/packages/react/src/themes/teams-high-contrast/components/Provider/providerVariables.ts
new file mode 100644
index 000000000..95a9e7534
--- /dev/null
+++ b/packages/react/src/themes/teams-high-contrast/components/Provider/providerVariables.ts
@@ -0,0 +1,6 @@
+import { ProviderVariables } from '../../../teams/components/Provider/providerVariables'
+
+export default (siteVariables): Partial<ProviderVariables> => ({
+  scrollbarThumbBackgroundColor: siteVariables.colors.white,
+  scrollbarThumbHoverBackgroundColor: siteVariables.colors.white,
+})
diff --git a/packages/react/src/themes/teams/componentStyles.ts b/packages/react/src/themes/teams/componentStyles.ts
index 93f53bde6..d30b024a1 100644
--- a/packages/react/src/themes/teams/componentStyles.ts
+++ b/packages/react/src/themes/teams/componentStyles.ts
@@ -69,7 +69,7 @@ export { default as MenuButton } from './components/MenuButton/menuButtonStyles'
 
 export { default as PopupContent } from './components/Popup/popupContentStyles'
 
-export { default as ProviderBox } from './components/Provider/providerBoxStyles'
+export { default as Provider } from './components/Provider/providerStyles'
 
 export { default as RadioGroupItem } from './components/RadioGroup/radioGroupItemStyles'
 
diff --git a/packages/react/src/themes/teams/componentVariables.ts b/packages/react/src/themes/teams/componentVariables.ts
index ece358e8a..60276e2ae 100644
--- a/packages/react/src/themes/teams/componentVariables.ts
+++ b/packages/react/src/themes/teams/componentVariables.ts
@@ -56,7 +56,7 @@ export { default as MenuDivider } from './components/Menu/menuDividerVariables'
 
 export { default as PopupContent } from './components/Popup/popupContentVariables'
 
-export { default as ProviderBox } from './components/Provider/providerBoxVariables'
+export { default as Provider } from './components/Provider/providerVariables'
 
 export { default as RadioGroupItem } from './components/RadioGroup/radioGroupItemVariables'
 
diff --git a/packages/react/src/themes/teams/components/Provider/providerBoxStyles.ts b/packages/react/src/themes/teams/components/Provider/providerStyles.ts
similarity index 85%
rename from packages/react/src/themes/teams/components/Provider/providerBoxStyles.ts
rename to packages/react/src/themes/teams/components/Provider/providerStyles.ts
index 58ce4fad9..ffe336bb7 100644
--- a/packages/react/src/themes/teams/components/Provider/providerBoxStyles.ts
+++ b/packages/react/src/themes/teams/components/Provider/providerStyles.ts
@@ -1,10 +1,10 @@
 import { ComponentStyleFunctionParam, ICSSInJSStyle } from '@fluentui/styles'
-import { ProviderBoxVariables } from './providerBoxVariables'
+import { ProviderVariables } from './providerVariables'
 
 export default {
   root: ({
     variables: v,
-  }: ComponentStyleFunctionParam<never, ProviderBoxVariables>): ICSSInJSStyle => ({
+  }: ComponentStyleFunctionParam<never, ProviderVariables>): ICSSInJSStyle => ({
     background: v.background,
     color: v.color,
     textAlign: 'left',
diff --git a/packages/react/src/themes/teams/components/Provider/providerBoxVariables.ts b/packages/react/src/themes/teams/components/Provider/providerVariables.ts
similarity index 87%
rename from packages/react/src/themes/teams/components/Provider/providerBoxVariables.ts
rename to packages/react/src/themes/teams/components/Provider/providerVariables.ts
index 2c2ff4c68..0e0669bdc 100644
--- a/packages/react/src/themes/teams/components/Provider/providerBoxVariables.ts
+++ b/packages/react/src/themes/teams/components/Provider/providerVariables.ts
@@ -1,6 +1,6 @@
 import { pxToRem } from '../../../../utils'
 
-export interface ProviderBoxVariables {
+export interface ProviderVariables {
   background: string
   color: string
 
@@ -15,7 +15,7 @@ export interface ProviderBoxVariables {
   scrollbarThumbHoverBorderSize: string
 }
 
-export default (siteVariables): Partial<ProviderBoxVariables> => ({
+export default (siteVariables): Partial<ProviderVariables> => ({
   background: siteVariables.bodyBackground,
   color: siteVariables.bodyColor,
 


Test patch:
diff --git a/packages/react/test/specs/components/Provider/Provider-test.tsx b/packages/react/test/specs/components/Provider/Provider-test.tsx
index f6a381791..9d115e849 100644
--- a/packages/react/test/specs/components/Provider/Provider-test.tsx
+++ b/packages/react/test/specs/components/Provider/Provider-test.tsx
@@ -6,6 +6,27 @@ import * as React from 'react'
 import Provider from 'src/components/Provider/Provider'
 import ProviderConsumer from 'src/components/Provider/ProviderConsumer'
 
+const createDocumentMock = (): Document => {
+  const externalDocument = document.implementation.createDocument(
+    'http://www.w3.org/1999/xhtml',
+    'html',
+    null,
+  )
+  const externalWindow: Partial<Window> = {
+    ontouchstart: () => {}, // whatInput asserts for this method
+
+    addEventListener: () => {},
+    removeEventListener: () => {},
+  }
+
+  externalDocument.documentElement.appendChild(externalDocument.createElement('body'))
+  // `defaultView` is read-only by spec, getter is used as workaround
+  // https://github.com/facebook/jest/issues/2227#issuecomment-430435133
+  jest.spyOn(externalDocument, 'defaultView', 'get').mockReturnValue(externalWindow as any)
+
+  return externalDocument
+}
+
 describe('Provider', () => {
   test('is exported', () => {
     expect(require('src/index.ts').Provider).toEqual(Provider)
@@ -235,18 +256,10 @@ describe('Provider', () => {
 
   describe('target', () => {
     test('performs whatinput init on first Provider mount', () => {
-      const addEventListener = jest.fn()
-      const setAttribute = jest.fn()
-      const externalDocument: any = {
-        defaultView: {
-          addEventListener,
-          removeEventListener: jest.fn(),
-          ontouchstart: jest.fn(),
-        },
-        documentElement: {
-          setAttribute,
-        },
-      }
+      const externalDocument = createDocumentMock()
+
+      const addEventListener = jest.spyOn(externalDocument.defaultView, 'addEventListener')
+      const setAttribute = jest.spyOn(externalDocument.documentElement, 'setAttribute')
 
       mount(
         <Provider id="first-provider" target={externalDocument}>
@@ -262,18 +275,8 @@ describe('Provider', () => {
     })
 
     test('performs whatinput cleanup on last Provider unmount', () => {
-      const removeEventListener = jest.fn()
-      const setAttribute = jest.fn()
-      const externalDocument: any = {
-        defaultView: {
-          addEventListener: jest.fn(),
-          removeEventListener,
-          ontouchstart: jest.fn(),
-        },
-        documentElement: {
-          setAttribute,
-        },
-      }
+      const externalDocument = createDocumentMock()
+      const removeEventListener = jest.spyOn(externalDocument.defaultView, 'removeEventListener')
 
       const wrapper = mount(
         <Provider id="first-provider" target={externalDocument}>
diff --git a/packages/react/test/specs/utils/__snapshots__/felaRenderer-test.tsx.snap b/packages/react/test/specs/utils/__snapshots__/felaRenderer-test.tsx.snap
index 8979e2352..0868088a7 100644
--- a/packages/react/test/specs/utils/__snapshots__/felaRenderer-test.tsx.snap
+++ b/packages/react/test/specs/utils/__snapshots__/felaRenderer-test.tsx.snap
@@ -14,7 +14,7 @@ exports[`felaRenderer CSS fallback values are rendered 1`] = `
 exports[`felaRenderer animations are not applied if animations are disabled 1`] = `
 "
 
-<div className=ui-provider__box dir=ltr>
+<div className=ui-provider dir=ltr>
   <div className=ui-box ui-animation />
 </div>;
 "
@@ -62,7 +62,7 @@ exports[`felaRenderer array returned by keyframe results in CSS fallback values
 }
 
 
-<div className=ui-provider__box dir=ltr>
+<div className=ui-provider dir=ltr>
   <div className=ui-box ui-animation a />
 </div>;
 "
@@ -111,7 +111,7 @@ exports[`felaRenderer keyframe colors are rendered 1`] = `
 }
 
 
-<div className=ui-provider__box dir=ltr>
+<div className=ui-provider dir=ltr>
   <div className=ui-box ui-animation a b />
 </div>;
 "
@@ -123,7 +123,7 @@ exports[`felaRenderer marginLeft is rendered into marginLeft due to RTL with \`n
 }
 
 
-<div className=ui-provider__box dir=rtl>
+<div className=ui-provider dir=rtl>
   <span className=ui-text a dir=auto>
     Hello
   </span>
@@ -137,7 +137,7 @@ exports[`felaRenderer marginLeft is rendered into marginRight due to RTL 1`] = `
 }
 
 
-<div className=ui-provider__box dir=rtl>
+<div className=ui-provider dir=rtl>
   <span className=ui-text a dir=auto>
     Hello
   </span>