Skip to content

Commit

Permalink
Disabled: preserve input values when toggling the isDisabled prop (
Browse files Browse the repository at this point in the history
…#43508)

* Fix `isDisabled` so that it keeps its value even if it is changed.

* add line to CHANGELOG.md

* Update packages/components/CHANGELOG.md

Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com>

* add unit test

* use user.type and toHaveTextContent / toHaveValue

* add contents Wrapper

Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com>
  • Loading branch information
torounit and ciampo authored Sep 8, 2022
1 parent f884657 commit ef6ba1a
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
- `Popover`: make sure offset middleware always applies the latest frame offset values ([#43329](https://github.com/WordPress/gutenberg/pull/43329/)).
- `Dropdown`: anchor popover to the dropdown wrapper (instead of the toggle) ([#43377](https://github.com/WordPress/gutenberg/pull/43377/)).
- `Guide`: Fix error when rendering with no pages ([#43380](https://github.com/WordPress/gutenberg/pull/43380/)).
- `Disabled`: preserve input values when toggling the `isDisabled` prop ([#43508](https://github.com/WordPress/gutenberg/pull/43508/))

### Enhancements

Expand Down
34 changes: 26 additions & 8 deletions packages/components/src/disabled/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import type { HTMLProps } from 'react';

/**
* WordPress dependencies
*/
import { useDisabled } from '@wordpress/compose';
import { createContext } from '@wordpress/element';
import { createContext, forwardRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import { StyledWrapper } from './styles/disabled-styles';
import { disabledStyles } from './styles/disabled-styles';
import type { DisabledProps } from './types';
import type { WordPressComponentProps } from '../ui/context';
import { useCx } from '../utils';

const Context = createContext< boolean >( false );
const { Consumer, Provider } = Context;

// Extracting this ContentWrapper component in order to make it more explicit
// the same 'ContentWrapper' component is needed so that React can reconcile
// the dom correctly when switching between disabled/non-disabled (instead
// of thrashing the previous DOM and therefore losing the form fields values).
const ContentWrapper = forwardRef<
HTMLDivElement,
HTMLProps< HTMLDivElement >
>( ( props, ref ) => <div { ...props } ref={ ref } /> );

/**
* `Disabled` is a component which disables descendant tabbable elements and prevents pointer interaction.
*
Expand Down Expand Up @@ -56,20 +66,28 @@ function Disabled( {
...props
}: WordPressComponentProps< DisabledProps, 'div' > ) {
const ref = useDisabled();

const cx = useCx();
if ( ! isDisabled ) {
return <Provider value={ false }>{ children }</Provider>;
return (
<Provider value={ false }>
<ContentWrapper>{ children }</ContentWrapper>
</Provider>
);
}

return (
<Provider value={ true }>
<StyledWrapper
<ContentWrapper
ref={ ref }
className={ classnames( className, 'components-disabled' ) }
className={ cx(
disabledStyles,
className,
'components-disabled'
) }
{ ...props }
>
{ children }
</StyledWrapper>
</ContentWrapper>
</Provider>
);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/components/src/disabled/styles/disabled-styles.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* External dependencies
*/
import styled from '@emotion/styled';
import { css } from '@emotion/react';

export const StyledWrapper = styled.div`
export const disabledStyles = css`
position: relative;
pointer-events: none;
Expand Down
38 changes: 38 additions & 0 deletions packages/components/src/disabled/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { render, screen, waitFor } from '@testing-library/react';
* Internal dependencies
*/
import Disabled from '../';
import userEvent from '@testing-library/user-event';

jest.mock( '@wordpress/dom', () => {
const focus = jest.requireActual( '../../../../dom/src' ).focus;
Expand Down Expand Up @@ -134,6 +135,43 @@ describe( 'Disabled', () => {
);
} );

it( 'should preserve input values when toggling the isDisabled prop', async () => {
const user = userEvent.setup( {
advanceTimers: jest.advanceTimersByTime,
} );

const MaybeDisable = ( { isDisabled = true } ) => (
<Disabled isDisabled={ isDisabled }>
<Form />
</Disabled>
);

const getInput = () => screen.getByRole( 'textbox' );
const getContentEditable = () => screen.getByTitle( 'edit my content' );

const { rerender } = render( <MaybeDisable isDisabled={ false } /> );

await user.type( getInput(), 'This is input.' );
expect( getInput() ).toHaveValue( 'This is input.' );

await user.type( getContentEditable(), 'This is contentEditable.' );
expect( getContentEditable() ).toHaveTextContent(
'This is contentEditable.'
);

rerender( <MaybeDisable isDisabled={ true } /> );
expect( getInput() ).toHaveValue( 'This is input.' );
expect( getContentEditable() ).toHaveTextContent(
'This is contentEditable.'
);

rerender( <MaybeDisable isDisabled={ false } /> );
expect( getInput() ).toHaveValue( 'This is input.' );
expect( getContentEditable() ).toHaveTextContent(
'This is contentEditable.'
);
} );

describe( 'Consumer', () => {
function DisabledStatus() {
return (
Expand Down

0 comments on commit ef6ba1a

Please sign in to comment.