From d62cff649c1db363efa461b02cf600270394f4e6 Mon Sep 17 00:00:00 2001 From: Dan Rosenthal Date: Thu, 3 Oct 2019 08:05:38 -0400 Subject: [PATCH 1/2] move portal node within theme provider node to enable theming Co-authored-by: Tim Layton --- UNRELEASED.md | 2 ++ src/components/Portal/Portal.tsx | 14 ++++++++++++-- src/components/Portal/tests/Portal.test.tsx | 9 +++++++-- src/components/ThemeProvider/ThemeProvider.tsx | 5 ++++- src/components/shared.ts | 5 +++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/UNRELEASED.md b/UNRELEASED.md index 8d14e8526f7..7e11b37edc0 100644 --- a/UNRELEASED.md +++ b/UNRELEASED.md @@ -12,6 +12,8 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f ### Bug fixes +- Moved rendering of the portal component's node within the node created by the theme provider component to enable theming through CSS Custom Properties ([#2224](https://github.com/Shopify/polaris-react/pull/2224)) + ### Documentation ### Development workflow diff --git a/src/components/Portal/Portal.tsx b/src/components/Portal/Portal.tsx index 9ec79f9948c..b072d2d89c3 100644 --- a/src/components/Portal/Portal.tsx +++ b/src/components/Portal/Portal.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {createPortal} from 'react-dom'; import {createUniqueIDFactory} from '@shopify/javascript-utilities/other'; +import {themeProvider} from '../shared'; export interface PortalProps { children?: React.ReactNode; @@ -20,6 +21,7 @@ export class Portal extends React.PureComponent { state: State = {isMounted: false}; private portalNode: HTMLElement; + private portalContainerNode: HTMLElement | null; private portalId = this.props.idPrefix !== '' @@ -29,7 +31,13 @@ export class Portal extends React.PureComponent { componentDidMount() { this.portalNode = document.createElement('div'); this.portalNode.setAttribute('data-portal-id', this.portalId); - document.body.appendChild(this.portalNode); + this.portalContainerNode = document.querySelector( + `${themeProvider.selector}`, + ); + if (this.portalContainerNode != null) { + this.portalContainerNode.appendChild(this.portalNode); + } + this.setState({isMounted: true}); } @@ -41,7 +49,9 @@ export class Portal extends React.PureComponent { } componentWillUnmount() { - document.body.removeChild(this.portalNode); + if (this.portalContainerNode != null) { + this.portalContainerNode.removeChild(this.portalNode); + } } render() { diff --git a/src/components/Portal/tests/Portal.test.tsx b/src/components/Portal/tests/Portal.test.tsx index 5ae84800401..d303789e0d7 100644 --- a/src/components/Portal/tests/Portal.test.tsx +++ b/src/components/Portal/tests/Portal.test.tsx @@ -46,15 +46,20 @@ describe('', () => { }); describe('DOM node', () => { + const appendChildSpy = jest.fn(); + const removeChildSpy = jest.fn(); + Object.defineProperty(document, 'querySelector', { + value: () => { + return {appendChild: appendChildSpy, removeChild: removeChildSpy}; + }, + }); it('gets added to the DOM on mount', () => { - const appendChildSpy = jest.spyOn(document.body, 'appendChild'); mountWithAppProvider(); expect(appendChildSpy).toHaveBeenCalledWith(expect.any(HTMLDivElement)); appendChildSpy.mockRestore(); }); it('gets removed from the DOM when the component unmounts', () => { - const removeChildSpy = jest.spyOn(document.body, 'removeChild'); const portal = mountWithAppProvider(); portal.unmount(); expect(removeChildSpy).toHaveBeenCalledWith(expect.any(HTMLDivElement)); diff --git a/src/components/ThemeProvider/ThemeProvider.tsx b/src/components/ThemeProvider/ThemeProvider.tsx index 7bf607f1bdc..b8edc5c8a4d 100644 --- a/src/components/ThemeProvider/ThemeProvider.tsx +++ b/src/components/ThemeProvider/ThemeProvider.tsx @@ -3,6 +3,7 @@ import isEqual from 'lodash/isEqual'; import {ThemeContext} from '../../utilities/theme'; import {Theme} from '../../utilities/theme/types'; import {setColors} from '../../utilities/theme/utils'; +import {themeProvider} from '../shared'; interface State { theme: Theme; @@ -55,7 +56,9 @@ export class ThemeProvider extends React.Component { return ( -
{children}
+
+ {children} +
); } diff --git a/src/components/shared.ts b/src/components/shared.ts index 5888a46b90c..b8c0ea8be67 100644 --- a/src/components/shared.ts +++ b/src/components/shared.ts @@ -28,6 +28,11 @@ export const headerCell = { selector: '[data-polaris-header-cell]', }; +export const themeProvider = { + props: {'data-polaris-theme-provider': true}, + selector: '[data-polaris-theme-provider]', +}; + export const DATA_ATTRIBUTE = { overlay, layer, From e456023083f2683efc10cfdb7c108cef63b3b735 Mon Sep 17 00:00:00 2001 From: Dan Rosenthal Date: Thu, 3 Oct 2019 14:13:42 -0400 Subject: [PATCH 2/2] Update UNRELEASED.md Co-Authored-By: Tim Layton --- UNRELEASED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UNRELEASED.md b/UNRELEASED.md index 7e11b37edc0..7168ac76d21 100644 --- a/UNRELEASED.md +++ b/UNRELEASED.md @@ -12,7 +12,7 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f ### Bug fixes -- Moved rendering of the portal component's node within the node created by the theme provider component to enable theming through CSS Custom Properties ([#2224](https://github.com/Shopify/polaris-react/pull/2224)) +- Moved rendering of the portal component’s node within the node created by the theme provider component to enable theming through CSS Custom Properties ([#2224](https://github.com/Shopify/polaris-react/pull/2224)) ### Documentation