Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/components/src/angle-picker-control/angle-circle.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ 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: 3 additions & 13 deletions packages/components/src/box-control/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ 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 @@ -42,17 +41,14 @@ 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' );

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

expect( input.value ).toBe( '' );
expect( unitSelect.value ).toBe( 'px' );
Expand All @@ -72,17 +68,14 @@ 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' );

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

expect( input.value ).toBe( '' );
expect( unitSelect.value ).toBe( 'px' );
Expand All @@ -109,17 +102,14 @@ 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' );

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

expect( input.value ).toBe( '' );
expect( unitSelect.value ).toBe( 'px' );
Expand Down
15 changes: 14 additions & 1 deletion packages/components/src/input-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export function InputControl(
isPressEnterToChange = false,
label,
labelPosition = 'top',
onBlur = noop,
onChange = noop,
onFocus = noop,
onValidate = noop,
onKeyDown = noop,
prefix,
Expand All @@ -50,6 +52,16 @@ 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
__unstableInputWidth={ __unstableInputWidth }
Expand All @@ -71,9 +83,10 @@ 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
56 changes: 22 additions & 34 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 { forwardRef } from '@wordpress/element';
import { useEffect, useRef, forwardRef } from '@wordpress/element';
import { UP, DOWN, ENTER } from '@wordpress/keycodes';
/**
* Internal dependencies
Expand All @@ -16,16 +16,14 @@ 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(
{
disabled = false,
dragDirection = 'n',
dragThreshold = 10,
dragThreshold = 15,
id,
isDragEnabled = false,
isFocused,
isPressEnterToChange = false,
onBlur = noop,
onChange = noop,
Expand All @@ -36,10 +34,8 @@ function InputField(
onKeyDown = noop,
onValidate = noop,
size = 'default',
setIsFocused,
stateReducer = ( state ) => state,
value: valueProp,
type,
...props
},
ref
Expand Down Expand Up @@ -67,27 +63,35 @@ function InputField(

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

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

/*
* 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 ) {
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
return;
}
if ( ! isFocused ) {
update( valueProp );
} else if ( ! isDirty ) {

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

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

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

/**
* If isPressEnterToChange is set, this commits the value to
Expand All @@ -102,22 +106,8 @@ function InputField(
}
};

/*
* Works around the odd UA (e.g. Firefox) that does not focus inputs of
* type=number when their spinner arrows are pressed.
*/
let handleOnMouseDown;
if ( type === 'number' ) {
handleOnMouseDown = ( event ) => {
if ( event.target !== event.target.ownerDocument.activeElement ) {
event.target.focus();
}
};
}

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

const handleOnChange = ( event ) => {
Expand Down Expand Up @@ -205,11 +195,9 @@ function InputField(
onChange={ handleOnChange }
onFocus={ handleOnFocus }
onKeyDown={ handleOnKeyDown }
onMouseDown={ handleOnMouseDown }
ref={ ref }
size={ size }
value={ value }
type={ type }
/>
);
}
Expand Down
18 changes: 8 additions & 10 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,23 +59,21 @@ describe( 'InputControl', () => {
it( 'should work as a controlled component', () => {
const spy = jest.fn();
const { rerender } = render(
<InputControl value="one" onChange={ spy } />
<InputControl value="Original" onChange={ spy } />
);

const input = getInput();

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

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

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

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

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

const input = getInput();

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

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