Skip to content

Commit

Permalink
Update roundClamp to match HTML number input stepping behavior
Browse files Browse the repository at this point in the history
- Rounds to steps starting from min
- Uses rounding precision determined by the greater of min or step
- If max is not on a step, uses the closest step less than max
  • Loading branch information
stokesman committed Feb 21, 2022
1 parent 5226d73 commit 0d12c6a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 12 deletions.
18 changes: 15 additions & 3 deletions packages/components/src/utils/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,21 @@ export function roundClamp(
) {
const baseValue = getNumber( value );
const stepValue = getNumber( step );
const precision = getPrecision( step );
const rounded = Math.round( baseValue / stepValue ) * stepValue;
const clampedValue = clamp( rounded, min, max );
const precision = Math.max( getPrecision( step ), getPrecision( min ) );
const realMin = min === Infinity ? 0 : min;

let tare = 0;
if ( realMin % stepValue ) {
tare = realMin;
}

let maxOnStep = max;
if ( max % stepValue || Math.abs( tare ) ) {
maxOnStep = Math.floor( ( max - tare ) / stepValue ) * stepValue + tare;
}

const rounded = Math.round( ( baseValue - tare ) / stepValue ) * stepValue;
const clampedValue = clamp( rounded + tare, min, maxOnStep );

return precision
? getNumber( clampedValue.toFixed( precision ) )
Expand Down
35 changes: 26 additions & 9 deletions packages/components/src/utils/test/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ describe( 'roundClamp', () => {
} );

it( 'should clamp with step', () => {
expect( roundClamp( 40, 1, 100, 10 ) ).toBe( 40 );
expect( roundClamp( 42, 1, 100, 10 ) ).toBe( 40 );
expect( roundClamp( 45, 1, 100, 10 ) ).toBe( 50 );
expect( roundClamp( 49, 1, 100, 10 ) ).toBe( 50 );
expect( roundClamp( 50, 1, 100, 10 ) ).toBe( 50 );

expect( roundClamp( 50, 1, 100, 15 ) ).toBe( 45 );
expect( roundClamp( 50, 1, 100, '15' ) ).toBe( 45 );
expect( roundClamp( 50, 1, 100, 11 ) ).toBe( 55 );
expect( roundClamp( 40, 0, 100, 10 ) ).toBe( 40 );
expect( roundClamp( 42, 0, 100, 10 ) ).toBe( 40 );
expect( roundClamp( 45, 0, 100, 10 ) ).toBe( 50 );
expect( roundClamp( 49, 0, 100, 10 ) ).toBe( 50 );
expect( roundClamp( 50, 0, 100, 10 ) ).toBe( 50 );

expect( roundClamp( 50, 0, 100, 15 ) ).toBe( 45 );
expect( roundClamp( 50, 0, 100, '15' ) ).toBe( 45 );
expect( roundClamp( 50, 0, 100, 11 ) ).toBe( 55 );
} );

it( 'should clamp with float in step', () => {
Expand All @@ -77,4 +77,21 @@ describe( 'roundClamp', () => {
expect( roundClamp( 40.06, 1, 100, 0.1 ) ).toBe( 40.1 );
expect( roundClamp( 40.123005, 1, 100, 0.001 ) ).toBe( 40.123 );
} );

it( 'should round to steps starting from min', () => {
expect( roundClamp( 10, 0.25, 100, 1 ) ).toBe( 10.25 );
expect( roundClamp( 10, -20.25, 100, 1 ) ).toBe( 9.75 );
expect( roundClamp( 10.5, 0.05, 100, 0.1 ) ).toBe( 10.45 );
expect( roundClamp( 10.51, 0.05, 100, 0.1 ) ).toBe( 10.55 );
} );

it( 'should restrain max to a value that is a multiple of step starting from min', () => {
expect( roundClamp( 10, 1, 10, 2 ) ).toBe( 9 );
expect( roundClamp( 10.125, 0.1, 10, 0.125 ) ).toBe( 9.975 );
} );

it( 'should round with a precision that’s the greater of min and step', () => {
expect( roundClamp( 10.061, 0.01, 20, 0.1 ) ).toBe( 10.11 );
expect( roundClamp( 10.105, 0.1, 20, 0.01 ) ).toBe( 10.11 );
} );
} );

0 comments on commit 0d12c6a

Please sign in to comment.