Add reference to number control (#49357)

* Add ref to NumberControl

* Add identifier

# Conflicts:
#	packages/js/product-editor/src/hooks/use-error-handler.ts

* Remove console.log

* Add changelog

* Stop using setTimeout and use mutation observer to check if the tab is visible before focusing field

* Rename prop

* Refactor number-control functions

* Fix lint

* resetIncrement funciton

---------

Co-authored-by: Nathan Schneider <nsschneider1@gmail.com>
This commit is contained in:
Fernando Marichal 2024-07-15 15:49:19 -03:00 committed by GitHub
parent 6bc551c71b
commit 67ecc95633
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 207 additions and 160 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: dev
Add reference to number control #49357

View File

@ -88,12 +88,14 @@ export function Edit( {
};
}
const widthFieldId = `dimensions_width-${ clientId }`;
const {
ref: dimensionsWidthRef,
error: dimensionsWidthValidationError,
validate: validateDimensionsWidth,
} = useValidation< Product >(
`dimensions_width-${ clientId }`,
widthFieldId,
async function dimensionsWidthValidator() {
if ( dimensions?.width && +dimensions.width <= 0 ) {
return {
@ -108,12 +110,14 @@ export function Edit( {
[ dimensions?.width ]
);
const lengthFieldId = `dimensions_length-${ clientId }`;
const {
ref: dimensionsLengthRef,
error: dimensionsLengthValidationError,
validate: validateDimensionsLength,
} = useValidation< Product >(
`dimensions_length-${ clientId }`,
lengthFieldId,
async function dimensionsLengthValidator() {
if ( dimensions?.length && +dimensions.length <= 0 ) {
return {
@ -128,12 +132,14 @@ export function Edit( {
[ dimensions?.length ]
);
const heightFieldId = `dimensions_height-${ clientId }`;
const {
ref: dimensionsHeightRef,
error: dimensionsHeightValidationError,
validate: validateDimensionsHeight,
} = useValidation< Product >(
`dimensions_height-${ clientId }`,
heightFieldId,
async function dimensionsHeightValidator() {
if ( dimensions?.height && +dimensions.height <= 0 ) {
return {
@ -148,12 +154,14 @@ export function Edit( {
[ dimensions?.height ]
);
const weightFieldId = `weight-${ clientId }`;
const {
ref: weightRef,
error: weightValidationError,
validate: validateWeight,
} = useValidation< Product >(
`weight-${ clientId }`,
weightFieldId,
async function weightValidator() {
if ( weight && +weight <= 0 ) {
return {
@ -172,18 +180,22 @@ export function Edit( {
...getDimensionsControlProps( 'width', 'A' ),
ref: dimensionsWidthRef,
onBlur: validateDimensionsWidth,
id: widthFieldId,
};
const dimensionsLengthProps = {
...getDimensionsControlProps( 'length', 'B' ),
ref: dimensionsLengthRef,
onBlur: validateDimensionsLength,
id: lengthFieldId,
};
const dimensionsHeightProps = {
...getDimensionsControlProps( 'height', 'C' ),
ref: dimensionsHeightRef,
onBlur: validateDimensionsHeight,
id: heightFieldId,
};
const weightProps = {
id: weightFieldId,
name: 'weight',
value: weight ?? '',
onChange: setWeight,

View File

@ -3,6 +3,7 @@
*/
import {
createElement,
forwardRef,
Fragment,
isValidElement,
useEffect,
@ -13,6 +14,7 @@ import { useInstanceId } from '@wordpress/compose';
import classNames from 'classnames';
import { plus, reset } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import type { ForwardedRef } from 'react';
import {
BaseControl,
Button,
@ -27,6 +29,7 @@ import { useNumberInputProps } from '../../hooks/use-number-input-props';
import { Label } from '../label/label';
export type NumberProps = {
id?: string;
value: string;
onChange: ( selected: string ) => void;
label: string | JSX.Element;
@ -47,7 +50,10 @@ export type NumberProps = {
const MEDIUM_DELAY = 500;
const SHORT_DELAY = 100;
export const NumberControl: React.FC< NumberProps > = ( {
export const NumberControl: React.FC< NumberProps > = forwardRef(
(
{
id,
value,
onChange,
label,
@ -63,26 +69,35 @@ export const NumberControl: React.FC< NumberProps > = ( {
step = 1,
min = -Infinity,
max = Infinity,
}: NumberProps ) => {
const id = useInstanceId( BaseControl, 'product_number_field' ) as string;
}: NumberProps,
ref: ForwardedRef< HTMLInputElement >
) => {
const instanceId = useInstanceId(
BaseControl,
'product_number_field'
) as string;
const identifier = id ?? instanceId;
const [ isFocused, setIsFocused ] = useState( false );
const unfocusIfOutside = ( event: React.FocusEvent ) => {
if (
! document
.getElementById( id )
.getElementById( identifier )
?.parentElement?.contains( event.relatedTarget )
) {
setIsFocused( false );
onBlur?.();
}
};
function handleOnFocus() {
setIsFocused( true );
onFocus?.();
}
const inputProps = useNumberInputProps( {
value: value || '',
onChange,
onFocus: () => {
setIsFocused( true );
onFocus?.();
},
onFocus: handleOnFocus,
min,
max,
} );
@ -93,11 +108,11 @@ export const NumberControl: React.FC< NumberProps > = ( {
const isInitialClick = useRef< boolean >( false );
const incrementValue = () => {
function incrementValue() {
const newValue = parseFloat( value || '0' ) + increment;
if ( newValue >= min && newValue <= max )
onChange( String( newValue ) );
};
}
useEffect( () => {
if ( increment !== 0 ) {
@ -116,23 +131,25 @@ export const NumberControl: React.FC< NumberProps > = ( {
};
}, [ increment, value ] );
const resetIncrement = () => setIncrement( 0 );
function resetIncrement() {
setIncrement( 0 );
}
const handleIncrement = ( thisStep: number ) => {
function handleIncrement( thisStep: number ) {
const newValue = parseFloat( value || '0' ) + thisStep;
if ( newValue >= min && newValue <= max ) {
onChange( String( parseFloat( value || '0' ) + thisStep ) );
setIncrement( thisStep );
isInitialClick.current = true;
}
};
}
return (
<BaseControl
className={ classNames( {
'has-error': error,
} ) }
id={ id }
id={ identifier }
label={
isValidElement( label ) ? (
label
@ -148,10 +165,11 @@ export const NumberControl: React.FC< NumberProps > = ( {
>
<InputControl
{ ...inputProps }
ref={ ref }
step={ step }
disabled={ disabled }
autoComplete="off"
id={ id }
id={ identifier }
className="woocommerce-number-control"
suffix={
<>
@ -207,4 +225,5 @@ export const NumberControl: React.FC< NumberProps > = ( {
/>
</BaseControl>
);
};
}
);

View File

@ -19,8 +19,20 @@ export function useValidations< T = unknown >() {
async function focusByValidatorId( validatorId: string ) {
const field = await context.getFieldByValidatorId( validatorId );
if ( field ) {
const tab = field.closest(
'.wp-block-woocommerce-product-tab__content'
);
const observer = new MutationObserver( () => {
if ( tab && getComputedStyle( tab ).display !== 'none' ) {
field.focus();
observer.disconnect();
}
} );
if ( tab ) {
observer.observe( tab, {
attributes: true,
} );
}
}