Skip to content
Merged
3 changes: 2 additions & 1 deletion packages/components/src/angle-picker-control/angle-circle.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ function AngleCircle( { value, onChange, ...props } ) {
// Prevent (drag) mouse events from selecting and accidentally
// triggering actions from other elements.
event.preventDefault();

// Ensure the input isn't focused as preventDefault would leave it
document.activeElement.blur();
onChange( getAngle( centerX, centerY, event.clientX, event.clientY ) );
};

Expand Down
16 changes: 13 additions & 3 deletions packages/components/src/box-control/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe( 'BoxControl', () => {
const input = container.querySelector( 'input' );
const unitSelect = container.querySelector( 'select' );

input.focus();
fireEvent.change( input, { target: { value: '100%' } } );
fireEvent.keyDown( input, { keyCode: ENTER } );

Expand All @@ -41,14 +42,17 @@ describe( 'BoxControl', () => {
const { container, getByText } = render( <BoxControl /> );
const input = container.querySelector( 'input' );
const unitSelect = container.querySelector( 'select' );
const reset = getByText( /Reset/ );

input.focus();
fireEvent.change( input, { target: { value: '100px' } } );
fireEvent.keyDown( input, { keyCode: ENTER } );

expect( input.value ).toBe( '100' );
expect( unitSelect.value ).toBe( 'px' );

fireEvent.click( getByText( /Reset/ ) );
reset.focus();
fireEvent.click( reset );

expect( input.value ).toBe( '' );
expect( unitSelect.value ).toBe( 'px' );
Expand All @@ -68,14 +72,17 @@ describe( 'BoxControl', () => {
const { container, getByText } = render( <Example /> );
const input = container.querySelector( 'input' );
const unitSelect = container.querySelector( 'select' );
const reset = getByText( /Reset/ );

input.focus();
fireEvent.change( input, { target: { value: '100px' } } );
fireEvent.keyDown( input, { keyCode: ENTER } );

expect( input.value ).toBe( '100' );
expect( unitSelect.value ).toBe( 'px' );

fireEvent.click( getByText( /Reset/ ) );
reset.focus();
fireEvent.click( reset );

expect( input.value ).toBe( '' );
expect( unitSelect.value ).toBe( 'px' );
Expand All @@ -102,14 +109,17 @@ describe( 'BoxControl', () => {
const { container, getByText } = render( <Example /> );
const input = container.querySelector( 'input' );
const unitSelect = container.querySelector( 'select' );
const reset = getByText( /Reset/ );

input.focus();
fireEvent.change( input, { target: { value: '100px' } } );
fireEvent.keyDown( input, { keyCode: ENTER } );

expect( input.value ).toBe( '100' );
expect( unitSelect.value ).toBe( 'px' );

fireEvent.click( getByText( /Reset/ ) );
reset.focus();
fireEvent.click( reset );

expect( input.value ).toBe( '' );
expect( unitSelect.value ).toBe( 'px' );
Expand Down
15 changes: 1 addition & 14 deletions packages/components/src/input-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ export function InputControl(
isPressEnterToChange = false,
label,
labelPosition = 'top',
onBlur = noop,
onChange = noop,
onFocus = noop,
onValidate = noop,
onKeyDown = noop,
prefix,
Expand All @@ -51,16 +49,6 @@ export function InputControl(
const id = useUniqueId( idProp );
const classes = classNames( 'components-input-control', className );

const handleOnBlur = ( event ) => {
onBlur( event );
setIsFocused( false );
};

const handleOnFocus = ( event ) => {
onFocus( event );
setIsFocused( true );
};

return (
<InputBase
className={ classes }
Expand All @@ -81,10 +69,9 @@ export function InputControl(
className="components-input-control__input"
disabled={ disabled }
id={ id }
isFocused={ isFocused }
isPressEnterToChange={ isPressEnterToChange }
onBlur={ handleOnBlur }
onChange={ onChange }
onFocus={ handleOnFocus }
onKeyDown={ onKeyDown }
onValidate={ onValidate }
ref={ ref }
Expand Down
38 changes: 17 additions & 21 deletions packages/components/src/input-control/input-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useDrag } from 'react-use-gesture';
/**
* WordPress dependencies
*/
import { useEffect, useRef, forwardRef } from '@wordpress/element';
import { forwardRef } from '@wordpress/element';
import { UP, DOWN, ENTER } from '@wordpress/keycodes';
/**
* Internal dependencies
Expand All @@ -16,6 +16,7 @@ import { useDragCursor } from './utils';
import { Input } from './styles/input-control-styles';
import { useInputControlStateReducer } from './state';
import { isValueEmpty } from '../utils/values';
import { useUpdateEffect } from '../utils';

function InputField(
{
Expand All @@ -24,6 +25,7 @@ function InputField(
dragThreshold = 10,
id,
isDragEnabled = false,
isFocused,
isPressEnterToChange = false,
onBlur = noop,
onChange = noop,
Expand All @@ -34,6 +36,7 @@ function InputField(
onKeyDown = noop,
onValidate = noop,
size = 'default',
setIsFocused,
stateReducer = ( state ) => state,
value: valueProp,
...props
Expand Down Expand Up @@ -63,35 +66,27 @@ function InputField(

const { _event, value, isDragging, isDirty } = state;

const valueRef = useRef( value );
const dragCursor = useDragCursor( isDragging, dragDirection );

useEffect( () => {
/**
* Handles syncing incoming value changes with internal state.
* This effectively enables a "controlled" state.
* https://reactjs.org/docs/forms.html#controlled-components
*/
if ( valueProp !== valueRef.current ) {
update( valueProp );
valueRef.current = valueProp;

// Quick return to avoid firing the onChange callback
/*
* Syncs value state using the focus state to determine the direction.
* Without focus it updates the value from the props. With focus it
* propagates the value and event through onChange.
*/
useUpdateEffect( () => {
if ( valueProp === value ) {
return;
}

/**
* Fires the onChange callback when internal state value changes.
*/
if ( value !== valueRef.current && ! isDirty ) {
if ( ! isFocused ) {
update( valueProp );
} else if ( ! isDirty ) {
onChange( value, { event: _event } );

valueRef.current = value;
}
}, [ value, isDirty, valueProp ] );
}, [ value, isDirty, isFocused, valueProp ] );

const handleOnBlur = ( event ) => {
onBlur( event );
setIsFocused( false );

/**
* If isPressEnterToChange is set, this commits the value to
Expand All @@ -108,6 +103,7 @@ function InputField(

const handleOnFocus = ( event ) => {
onFocus( event );
setIsFocused( true );
};

const handleOnChange = ( event ) => {
Expand Down
18 changes: 10 additions & 8 deletions packages/components/src/input-control/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe( 'InputControl', () => {
render( <InputControl value="Hello" onChange={ spy } /> );

const input = getInput();

input.focus();
fireEvent.change( input, { target: { value: 'There' } } );

expect( input.value ).toBe( 'There' );
Expand All @@ -59,21 +59,23 @@ describe( 'InputControl', () => {
it( 'should work as a controlled component', () => {
const spy = jest.fn();
const { rerender } = render(
<InputControl value="Original" onChange={ spy } />
<InputControl value="one" onChange={ spy } />
);

const input = getInput();

fireEvent.change( input, { target: { value: 'State' } } );
input.focus();
fireEvent.change( input, { target: { value: 'two' } } );

// Assuming <InputControl /> is controlled...
// Ensuring <InputControl /> is controlled
fireEvent.blur( input );

// Updating the value
rerender( <InputControl value="New" onChange={ spy } /> );
rerender( <InputControl value="three" onChange={ spy } /> );

expect( input.value ).toBe( 'New' );
expect( input.value ).toBe( 'three' );

/**
/*
* onChange called only once. onChange is not called when a
* parent component explicitly passed a (new value) change down to
* the <InputControl />.
Expand All @@ -89,7 +91,7 @@ describe( 'InputControl', () => {

const input = getInput();

// Assuming <InputControl /> is controlled...
// Assuming <InputControl /> is controlled (not focused)

// Updating the value
rerender( <InputControl value="New" onChange={ spy } /> );
Expand Down
Loading