Add improvements to NumberControl (#47121)

* Use min and max values if provided when sanitizing values

* Pass min and max attributes to NumberControl

* Use 'min' and 'max' values when incrementing through + and - buttons

* Add changelog

* Handle increment when pressing buttons

* Show + and - buttons disabled when max/min has been reached

* Prevent suffix from being selected
This commit is contained in:
Nathan Silveira 2024-05-10 10:02:00 -03:00 committed by GitHub
parent d0a8477b51
commit 7c7d9837a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 58 additions and 25 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Update number control to not allow invalid values through arrow keys or buttons

View File

@ -95,6 +95,8 @@ export function Edit( {
tooltip={ tooltip }
disabled={ disabled }
step={ step }
min={ min }
max={ max }
/>
</div>
);

View File

@ -38,6 +38,8 @@ export type NumberProps = {
tooltip?: string;
disabled?: boolean;
step?: number;
min?: number;
max?: number;
};
const MEDIUM_DELAY = 500;
@ -57,6 +59,8 @@ export const NumberControl: React.FC< NumberProps > = ( {
placeholder,
disabled,
step = 1,
min = -Infinity,
max = +Infinity,
}: NumberProps ) => {
const id = useInstanceId( BaseControl, 'product_number_field' ) as string;
const [ isFocused, setIsFocused ] = useState( false );
@ -74,6 +78,8 @@ export const NumberControl: React.FC< NumberProps > = ( {
value: value || '',
onChange,
onFocus: () => setIsFocused( true ),
min,
max,
} );
const [ increment, setIncrement ] = useState( 0 );
@ -82,8 +88,11 @@ export const NumberControl: React.FC< NumberProps > = ( {
const isInitialClick = useRef< boolean >( false );
const incrementValue = () =>
onChange( String( parseFloat( value || '0' ) + increment ) );
const incrementValue = () => {
const newValue = parseFloat( value || '0' ) + increment;
if ( newValue >= min && newValue <= max )
onChange( String( newValue ) );
};
useEffect( () => {
if ( increment !== 0 ) {
@ -104,6 +113,15 @@ export const NumberControl: React.FC< NumberProps > = ( {
const resetIncrement = () => setIncrement( 0 );
const 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( {
@ -134,16 +152,12 @@ export const NumberControl: React.FC< NumberProps > = ( {
<Button
className="woocommerce-number-control__increment"
icon={ plus }
onMouseDown={ () => {
onChange(
String(
parseFloat( value || '0' ) +
step
)
);
setIncrement( step );
isInitialClick.current = true;
} }
disabled={
parseFloat( value || '0' ) >= max
}
onMouseDown={ () =>
handleIncrement( step )
}
onMouseLeave={ resetIncrement }
onMouseUp={ resetIncrement }
onBlur={ unfocusIfOutside }
@ -157,18 +171,14 @@ export const NumberControl: React.FC< NumberProps > = ( {
/>
<Button
icon={ reset }
disabled={
parseFloat( value || '0' ) <= min
}
className="woocommerce-number-control__decrement"
onBlur={ unfocusIfOutside }
onMouseDown={ () => {
onChange(
String(
parseFloat( value || '0' ) -
step
)
);
setIncrement( -step );
isInitialClick.current = true;
} }
onMouseDown={ () =>
handleIncrement( -step )
}
onMouseLeave={ resetIncrement }
onMouseUp={ resetIncrement }
isSmall

View File

@ -0,0 +1,5 @@
.woocommerce-number-control {
.components-input-control__suffix {
user-select: none;
}
}

View File

@ -17,6 +17,8 @@ type Props = {
onChange: ( value: string ) => void;
onFocus?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
onKeyDown?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
min?: number;
max?: number;
};
const NOT_NUMBERS_OR_SEPARATORS_REGEX = /[^0-9,.]/g;
@ -26,6 +28,8 @@ export const useNumberInputProps = ( {
onChange,
onFocus,
onKeyDown,
min = -Infinity,
max = +Infinity,
}: Props ) => {
const { formatNumber, parseNumber } = useProductHelper();
@ -47,20 +51,27 @@ export const useNumberInputProps = ( {
const step = Number( event.currentTarget.step || '1' );
if ( event.code === 'ArrowUp' ) {
event.preventDefault();
onChange( String( amount + step ) );
if ( amount + step <= max ) onChange( String( amount + step ) );
}
if ( event.code === 'ArrowDown' ) {
event.preventDefault();
onChange( String( amount - step ) );
if ( amount - step >= min ) onChange( String( amount - step ) );
}
if ( onKeyDown ) {
onKeyDown( event );
}
},
onChange( newValue: string ) {
const sanitizeValue = parseNumber(
let sanitizeValue = parseNumber(
newValue.replace( NOT_NUMBERS_OR_SEPARATORS_REGEX, '' )
);
const numberValue = Number( sanitizeValue );
if ( sanitizeValue && numberValue >= max ) {
sanitizeValue = String( max );
}
if ( sanitizeValue && numberValue <= min ) {
sanitizeValue = String( min );
}
onChange( sanitizeValue );
},
};

View File

@ -51,6 +51,7 @@
@import "components/custom-fields/style.scss";
@import "components/text-control/style.scss";
@import "components/attribute-combobox-field/styles.scss";
@import "components/number-control/style.scss";
/* Field Blocks */