Skip to content

Commit

Permalink
Refactored Disabled component to use React Hooks (#23917)
Browse files Browse the repository at this point in the history
  • Loading branch information
javidalkaruzi authored Jul 14, 2020
1 parent d7af685 commit 90920ac
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 53 deletions.
94 changes: 45 additions & 49 deletions packages/components/src/disabled/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { createContext, Component } from '@wordpress/element';
import {
createContext,
useCallback,
useLayoutEffect,
useRef,
} from '@wordpress/element';
import { focus } from '@wordpress/dom';

const { Consumer, Provider } = createContext( false );
Expand All @@ -31,40 +36,11 @@ const DISABLED_ELIGIBLE_NODE_NAMES = [
'TEXTAREA',
];

class Disabled extends Component {
constructor() {
super( ...arguments );
function Disabled( { className, children, ...props } ) {
const node = useRef();

this.bindNode = this.bindNode.bind( this );
this.disable = this.disable.bind( this );

// Debounce re-disable since disabling process itself will incur
// additional mutations which should be ignored.
this.debouncedDisable = debounce( this.disable, { leading: true } );
}

componentDidMount() {
this.disable();

this.observer = new window.MutationObserver( this.debouncedDisable );
this.observer.observe( this.node, {
childList: true,
attributes: true,
subtree: true,
} );
}

componentWillUnmount() {
this.observer.disconnect();
this.debouncedDisable.cancel();
}

bindNode( node ) {
this.node = node;
}

disable() {
focus.focusable.find( this.node ).forEach( ( focusable ) => {
const disable = () => {
focus.focusable.find( node.current ).forEach( ( focusable ) => {
if (
includes( DISABLED_ELIGIBLE_NODE_NAMES, focusable.nodeName )
) {
Expand All @@ -79,22 +55,42 @@ class Disabled extends Component {
focusable.setAttribute( 'contenteditable', 'false' );
}
} );
}
};

// Debounce re-disable since disabling process itself will incur
// additional mutations which should be ignored.
const debouncedDisable = useCallback(
debounce( disable, { leading: true } ),
[]
);

useLayoutEffect( () => {
disable();

const observer = new window.MutationObserver( debouncedDisable );
observer.observe( node.current, {
childList: true,
attributes: true,
subtree: true,
} );

render() {
const { className, ...props } = this.props;
return (
<Provider value={ true }>
<div
ref={ this.bindNode }
className={ classnames( className, 'components-disabled' ) }
{ ...props }
>
{ this.props.children }
</div>
</Provider>
);
}
return () => {
observer.disconnect();
debouncedDisable.cancel();
};
}, [] );

return (
<Provider value={ true }>
<div
ref={ node }
className={ classnames( className, 'components-disabled' ) }
{ ...props }
>
{ children }
</div>
</Provider>
);
}

Disabled.Consumer = Consumer;
Expand Down
17 changes: 13 additions & 4 deletions packages/components/src/disabled/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,20 @@ describe( 'Disabled', () => {
</form>
);

// this is needed because TestUtils does not accept a stateless component.
class DisabledComponent extends Component {
render() {
const { children } = this.props;

return <Disabled>{ children }</Disabled>;
}
}

it( 'will disable all fields', () => {
const wrapper = TestUtils.renderIntoDocument(
<Disabled>
<DisabledComponent>
<Form />
</Disabled>
</DisabledComponent>
);

const input = TestUtils.findRenderedDOMComponentWithTag(
Expand Down Expand Up @@ -150,9 +159,9 @@ describe( 'Disabled', () => {

test( "lets components know that they're disabled via context", () => {
const wrapper = TestUtils.renderIntoDocument(
<Disabled>
<DisabledComponent>
<DisabledStatus />
</Disabled>
</DisabledComponent>
);
const wrapperElement = TestUtils.findRenderedDOMComponentWithTag(
wrapper,
Expand Down

0 comments on commit 90920ac

Please sign in to comment.