diff --git a/UNRELEASED-V4.md b/UNRELEASED-V4.md index adee134997a..00c013eb1b0 100644 --- a/UNRELEASED-V4.md +++ b/UNRELEASED-V4.md @@ -45,6 +45,7 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f ### Code quality - Added hooks to storybooks scope ([#1672](https://github.com/Shopify/polaris-react/pull/1672)) +- Removed all uses of `ReactDOM.findDOMNode` ([#1696](https://github.com/Shopify/polaris-react/pull/1696)) - Simplified `WithinContentContainer` context type ([#1602](https://github.com/Shopify/polaris-react/pull/1602)) - Updated `Collapsible` to no longer use `componentWillReceiveProps`([#1670](https://github.com/Shopify/polaris-react/pull/1670)) - Remove `withRef` and `withContext` from `DropZone.FileUpload` ([#1491](https://github.com/Shopify/polaris-react/pull/1491)) diff --git a/src/components/Focus/Focus.tsx b/src/components/Focus/Focus.tsx index eed6b5ad559..547dd877425 100644 --- a/src/components/Focus/Focus.tsx +++ b/src/components/Focus/Focus.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import {findDOMNode} from 'react-dom'; import isEqual from 'lodash/isEqual'; import {focusFirstFocusableNode} from '@shopify/javascript-utilities/focus'; export interface Props { children?: React.ReactNode; disabled?: boolean; + root: HTMLElement | null; } export default class Focus extends React.PureComponent { @@ -24,11 +24,11 @@ export default class Focus extends React.PureComponent { } handleSelfFocus() { - if (this.props.disabled) { + const {disabled, root} = this.props; + if (disabled) { return; } - const root = findDOMNode(this) as HTMLElement | null; if (root) { if (!root.querySelector('[autofocus]')) { focusFirstFocusableNode(root, false); diff --git a/src/components/Focus/tests/Focus.test.tsx b/src/components/Focus/tests/Focus.test.tsx index 8593f103594..f4b14a0c1c9 100644 --- a/src/components/Focus/tests/Focus.test.tsx +++ b/src/components/Focus/tests/Focus.test.tsx @@ -1,6 +1,7 @@ -import React from 'react'; +import React, {useRef, useState, useEffect} from 'react'; import {mountWithAppProvider} from 'test-utilities/legacy'; -import Focus from '../Focus'; +import Focus, {Props} from '../Focus'; +import {Discard} from '../../../types'; describe('', () => { let requestAnimationFrameSpy: jest.SpyInstance; @@ -15,22 +16,16 @@ describe('', () => { }); it('mounts', () => { - const focus = mountWithAppProvider( - -
- , - ); + const focus = mountWithAppProvider(); expect(focus.exists()).toBe(true); }); it('will not focus any element if none are natively focusable', () => { mountWithAppProvider( - -
- -
-
, + + + , ); expect(document.body).toBe(document.activeElement); @@ -38,11 +33,9 @@ describe('', () => { it('will focus first focusable node', () => { const focus = mountWithAppProvider( - -
- -
-
, + + + , ); const input = focus.find('input').getDOMNode(); @@ -51,14 +44,27 @@ describe('', () => { it('will not focus the first focusable node is the `disabled` is true', () => { const focus = mountWithAppProvider( - -
- -
-
, + + + , ); const input = focus.find('input').getDOMNode(); expect(input).not.toBe(document.activeElement); }); }); + +function FocusTestWrapper({children, ...props}: Discard) { + const root = useRef(null); + const [, setMount] = useState(false); + + useEffect(() => { + setMount(true); + }, []); + + return ( + +
{children}
+
+ ); +} diff --git a/src/components/ResourceList/components/BulkActions/components/BulkActionButton/BulkActionButton.tsx b/src/components/ResourceList/components/BulkActions/components/BulkActionButton/BulkActionButton.tsx index 8faa4f618d4..314e12e9584 100644 --- a/src/components/ResourceList/components/BulkActions/components/BulkActionButton/BulkActionButton.tsx +++ b/src/components/ResourceList/components/BulkActions/components/BulkActionButton/BulkActionButton.tsx @@ -1,5 +1,4 @@ -import React from 'react'; -import {findDOMNode} from 'react-dom'; +import React, {createRef} from 'react'; import {CaretDownMinor} from '@shopify/polaris-icons'; import {classNames} from '../../../../../../utilities/css'; @@ -20,14 +19,13 @@ export default class BulkActionButton extends React.PureComponent< Props, never > { + private bulkActionButton = createRef(); + componentDidMount() { const {handleMeasurement} = this.props; if (handleMeasurement) { - const bulkActionButtonNode = findDOMNode(this); - const width = - (bulkActionButtonNode instanceof Element && - bulkActionButtonNode.getBoundingClientRect().width) || - 0; + const width = (this.bulkActionButton + .current as HTMLButtonElement).getBoundingClientRect().width; handleMeasurement(width); } } @@ -82,6 +80,7 @@ export default class BulkActionButton extends React.PureComponent< aria-label={accessibilityLabel} type="button" disabled={disabled} + ref={this.bulkActionButton} > {contentMarkup} diff --git a/src/components/Tabs/components/TabMeasurer/TabMeasurer.tsx b/src/components/Tabs/components/TabMeasurer/TabMeasurer.tsx index 191a4318cae..c2682b2153d 100644 --- a/src/components/Tabs/components/TabMeasurer/TabMeasurer.tsx +++ b/src/components/Tabs/components/TabMeasurer/TabMeasurer.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import {findDOMNode} from 'react-dom'; import {classNames} from '../../../../utilities/css'; import EventListener from '../../../EventListener'; @@ -89,9 +88,9 @@ export default class TabMeasurer extends React.PureComponent { const {handleMeasurement} = this.props; const containerWidth = this.containerNode.offsetWidth; - const tabMeasurerNode = findDOMNode(this); + const hiddenTabNodes = - tabMeasurerNode instanceof Element && tabMeasurerNode.children; + this.containerNode instanceof Element && this.containerNode.children; const hiddenTabNodesArray: HTMLElement[] = [].slice.call(hiddenTabNodes); const hiddenTabWidths = hiddenTabNodesArray.map((node) => { return node.getBoundingClientRect().width; diff --git a/src/components/TrapFocus/TrapFocus.tsx b/src/components/TrapFocus/TrapFocus.tsx index 65786299ab8..f19993989cd 100644 --- a/src/components/TrapFocus/TrapFocus.tsx +++ b/src/components/TrapFocus/TrapFocus.tsx @@ -43,7 +43,7 @@ export default class TrapFocus extends React.PureComponent { const {children} = this.props; return ( - +
{children} diff --git a/src/types.ts b/src/types.ts index 67b7c30ee62..4fd2872420c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -258,3 +258,5 @@ export type DeepPartial = { ? ReadonlyArray> : DeepPartial }; + +export type Discard = Pick>;