From 94d6b4a8f7498fbc3ef9fc3a65f8ad6e0bcceb38 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Fri, 27 Aug 2021 13:52:09 -0700 Subject: [PATCH 01/14] =?UTF-8?q?Support=20=E2=80=9Cany=E2=80=9D=20step=20?= =?UTF-8?q?for=20NumberControl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This eliminates the only thing useJumpStep was affecting so the hook is removed from NumberControl. --- .../components/src/number-control/index.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/components/src/number-control/index.js b/packages/components/src/number-control/index.js index 4aa29e17cee79a..3fa7b38c3ea008 100644 --- a/packages/components/src/number-control/index.js +++ b/packages/components/src/number-control/index.js @@ -17,7 +17,6 @@ import { Input } from './styles/number-control-styles'; import * as inputControlActionTypes from '../input-control/reducer/actions'; import { composeStateReducers } from '../input-control/reducer/reducer'; import { add, subtract, roundClamp } from '../utils/math'; -import { useJumpStep } from '../utils/hooks'; import { isValueEmpty } from '../utils/values'; export function NumberControl( @@ -40,13 +39,14 @@ export function NumberControl( }, ref ) { - const baseValue = roundClamp( 0, min, max, step ); - - const jumpStep = useJumpStep( { - step, - shiftStep, - isShiftStepEnabled, - } ); + const baseStep = step === 'any' ? 1 : parseFloat( step ); + const baseValue = roundClamp( 0, min, max, baseStep ); + const constrainValue = ( value ) => { + // When step is "any" clamp the value, otherwise round and clamp it + return step === 'any' + ? Math.min( max, Math.max( min, value ) ) + : roundClamp( value, min, max, baseStep ); + }; const autoComplete = typeProp === 'number' ? 'off' : null; const classes = classNames( 'components-number-control', className ); @@ -75,8 +75,8 @@ export function NumberControl( const enableShift = event.shiftKey && isShiftStepEnabled; const incrementalValue = enableShift - ? parseFloat( shiftStep ) * parseFloat( step ) - : parseFloat( step ); + ? parseFloat( shiftStep ) * baseStep + : baseStep; let nextValue = isValueEmpty( currentValue ) ? baseValue : currentValue; @@ -105,8 +105,8 @@ export function NumberControl( const { delta, shiftKey } = payload; const [ x, y ] = delta; const modifier = shiftKey - ? parseFloat( shiftStep ) * parseFloat( step ) - : parseFloat( step ); + ? parseFloat( shiftStep ) * baseStep + : baseStep; let directionModifier; let directionBaseValue; @@ -159,7 +159,7 @@ export function NumberControl( state.value = applyEmptyValue ? currentValue - : roundClamp( currentValue, min, max, step ); + : constrainValue( currentValue ); } return state; @@ -179,7 +179,7 @@ export function NumberControl( min={ min } ref={ ref } required={ required } - step={ jumpStep } + step={ step } type={ typeProp } value={ valueProp } __unstableStateReducer={ composeStateReducers( From 754a7089f5e2150cf8ba4c155492c9aba0e62ade Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Sun, 29 Aug 2021 09:24:18 -0700 Subject: [PATCH 02/14] =?UTF-8?q?Omit=20rounding=20in=20drag=20and=20keybo?= =?UTF-8?q?ard=20actions=20when=20step=20is=20=E2=80=9Cany=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/src/number-control/index.js | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/packages/components/src/number-control/index.js b/packages/components/src/number-control/index.js index 3fa7b38c3ea008..fd0feebbfd9e9f 100644 --- a/packages/components/src/number-control/index.js +++ b/packages/components/src/number-control/index.js @@ -41,11 +41,11 @@ export function NumberControl( ) { const baseStep = step === 'any' ? 1 : parseFloat( step ); const baseValue = roundClamp( 0, min, max, baseStep ); - const constrainValue = ( value ) => { + const constrainValue = ( value, stepOverride ) => { // When step is "any" clamp the value, otherwise round and clamp it return step === 'any' ? Math.min( max, Math.max( min, value ) ) - : roundClamp( value, min, max, baseStep ); + : roundClamp( value, min, max, stepOverride ?? baseStep ); }; const autoComplete = typeProp === 'number' ? 'off' : null; @@ -93,58 +93,54 @@ export function NumberControl( nextValue = subtract( nextValue, incrementalValue ); } - nextValue = roundClamp( nextValue, min, max, incrementalValue ); - - state.value = nextValue; + state.value = constrainValue( + nextValue, + enableShift ? incrementalValue : null + ); } /** * Handles drag to update events */ if ( type === inputControlActionTypes.DRAG && isDragEnabled ) { - const { delta, shiftKey } = payload; - const [ x, y ] = delta; - const modifier = shiftKey + const [ x, y ] = payload.delta; + const modifier = payload.shiftKey ? parseFloat( shiftStep ) * baseStep : baseStep; let directionModifier; - let directionBaseValue; + let delta; switch ( dragDirection ) { case 'n': - directionBaseValue = y; + delta = y; directionModifier = -1; break; case 'e': - directionBaseValue = x; + delta = x; directionModifier = isRTL() ? -1 : 1; break; case 's': - directionBaseValue = y; + delta = y; directionModifier = 1; break; case 'w': - directionBaseValue = x; + delta = x; directionModifier = isRTL() ? 1 : -1; break; } - const distance = directionBaseValue * modifier * directionModifier; - let nextValue; + if ( delta !== 0 ) { + delta = Math.ceil( Math.abs( delta ) ) * Math.sign( delta ); + const distance = delta * modifier * directionModifier; - if ( distance !== 0 ) { - nextValue = roundClamp( + state.value = constrainValue( add( currentValue, distance ), - min, - max, - modifier + payload.shiftKey ? modifier : null ); - - state.value = nextValue; } } From 60db8b610a796fd8d8620c3234f8c27ffdaf05f0 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Wed, 8 Sep 2021 14:31:51 -0700 Subject: [PATCH 03/14] Update story for NumberControl --- packages/components/src/number-control/stories/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/number-control/stories/index.js b/packages/components/src/number-control/stories/index.js index bb9c4d8df02925..c7bb15b41e80d0 100644 --- a/packages/components/src/number-control/stories/index.js +++ b/packages/components/src/number-control/stories/index.js @@ -32,7 +32,7 @@ function Example() { placeholder: text( 'placeholder', 0 ), required: boolean( 'required', false ), shiftStep: number( 'shiftStep', 10 ), - step: number( 'step', 1 ), + step: text( 'step', 1 ), }; return ( From c5bd97c1102b33d50fe06f0ee37528a656c374e4 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Wed, 8 Sep 2021 14:40:46 -0700 Subject: [PATCH 04/14] Update NumberControl README --- .../components/src/number-control/README.md | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/components/src/number-control/README.md b/packages/components/src/number-control/README.md index 9a237c4049df02..396554ce4977dd 100644 --- a/packages/components/src/number-control/README.md +++ b/packages/components/src/number-control/README.md @@ -81,6 +81,22 @@ The position of the label (`top`, `side`, `bottom`, or `edge`). - Type: `String` - Required: No +### max + +The maximum `value` allowed. + +- Type: `Number` +- Required: No +- Default: Infinity + +### min + +The minimum `value` allowed. + +- Type: `Number` +- Required: No +- Default: -Infinity + ### required If `true` enforces a valid number within the control's min/max range. If `false` allows an empty string as a valid value. @@ -99,8 +115,8 @@ Amount to increment by when the `SHIFT` key is held down. This shift value is a ### step -Amount to increment by when incrementing/decrementing. +Amount by which the `value` is changed when incrementing/decrementing. It is also a factor in validation as `value` must be a multiple of `step` (offset by `min`, if specified) to be valid. Accepts the special string value `any` that voids the validation constraint and causes stepping actions to increment/decrement by `1`. -- Type: `Number` +- Type: `Number | "any"` - Required: No - Default: `1` From 95969fba2eed6e43ac29cff27344e7c83c09fc7a Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Wed, 8 Sep 2021 15:18:07 -0700 Subject: [PATCH 05/14] Use isShiftStepEnabled to determine behavior in drag actions --- packages/components/src/number-control/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/components/src/number-control/index.js b/packages/components/src/number-control/index.js index fd0feebbfd9e9f..2a917b2c6f8159 100644 --- a/packages/components/src/number-control/index.js +++ b/packages/components/src/number-control/index.js @@ -104,7 +104,8 @@ export function NumberControl( */ if ( type === inputControlActionTypes.DRAG && isDragEnabled ) { const [ x, y ] = payload.delta; - const modifier = payload.shiftKey + const enableShift = payload.shiftKey && isShiftStepEnabled; + const modifier = enableShift ? parseFloat( shiftStep ) * baseStep : baseStep; @@ -139,7 +140,7 @@ export function NumberControl( state.value = constrainValue( add( currentValue, distance ), - payload.shiftKey ? modifier : null + enableShift ? modifier : null ); } } From 28a85454f3d4d71295e856a2946f07c689f01739 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Fri, 17 Sep 2021 10:43:44 -0700 Subject: [PATCH 06/14] Try simple support of `step="any"` in RangeControl --- packages/components/src/range-control/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/components/src/range-control/index.js b/packages/components/src/range-control/index.js index c7c718df9fa04c..7073862d413000 100644 --- a/packages/components/src/range-control/index.js +++ b/packages/components/src/range-control/index.js @@ -77,6 +77,14 @@ function RangeControl( initial: initialPosition, } ); const isResetPendent = useRef( false ); + + if ( step === 'any' ) { + // The tooltip and number input field are hidden when the step is "any" + // because the decimals get too lengthy to fit well. + showTooltipProp = false; + withInputField = false; + } + const [ showTooltip, setShowTooltip ] = useState( showTooltipProp ); const [ isFocused, setIsFocused ] = useState( false ); From 4e4173d491adc8a19989d33222989de264521522 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Fri, 24 Sep 2021 19:09:21 -0700 Subject: [PATCH 07/14] Update RangeControl README --- packages/components/src/range-control/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/components/src/range-control/README.md b/packages/components/src/range-control/README.md index d45529e98bc003..21769fec6afbb1 100644 --- a/packages/components/src/range-control/README.md +++ b/packages/components/src/range-control/README.md @@ -272,7 +272,7 @@ The value to revert to if the Reset button is clicked (enabled by `allowReset`) #### showTooltip -Forcing the Tooltip UI to show or hide. +Forcing the Tooltip UI to show or hide. This is overriden to `false` when `step` is set to the special string value `any`. - Type: `Boolean` - Required: No @@ -280,9 +280,9 @@ Forcing the Tooltip UI to show or hide. #### step -The stepping interval between `min` and `max` values. Step is used both for user interface and validation purposes. +The minimum amount by which `value` changes. It is also a factor in validation as `value` must be a multiple of `step` (offset by `min`) to be valid. Accepts the special string value `any` that voids the validation constraint and overrides both `withInputField` and `showTooltip` props to `false`. -- Type: `Number` +- Type: `Number | "any"` - Required: No - Platform: Web @@ -311,7 +311,7 @@ The current value of the range slider. #### withInputField -Determines if the `input` number field will render next to the RangeControl. +Determines if the `input` number field will render next to the RangeControl. This is overriden to `false` when `step` is set to the special string value `any`. - Type: `Boolean` - Required: No From a3d5d411d71b94ae9c98c6593c9ddbe8fe2cb2e3 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Fri, 24 Sep 2021 20:25:58 -0700 Subject: [PATCH 08/14] Add story for any step and update default and marks story --- packages/components/src/range-control/stories/index.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/components/src/range-control/stories/index.js b/packages/components/src/range-control/stories/index.js index 756f6ca1f0e030..febc98b4c8d809 100644 --- a/packages/components/src/range-control/stories/index.js +++ b/packages/components/src/range-control/stories/index.js @@ -43,7 +43,7 @@ const DefaultExample = () => { max: number( 'max', 100 ), min: number( 'min', 0 ), showTooltip: boolean( 'showTooltip', false ), - step: number( 'step', 1 ), + step: text( 'step', 1 ), railColor: text( 'railColor', null ), trackColor: text( 'trackColor', null ), withInputField: boolean( 'withInputField', true ), @@ -81,6 +81,10 @@ export const InitialValueZero = () => { ); }; +export const withAnyStep = () => { + return ; +}; + export const withHelp = () => { const label = text( 'Label', 'How many columns should this use?' ); const help = text( @@ -174,6 +178,10 @@ export const marks = () => {

Negative Range

+ +

Any Step

+ + ); }; From 0bef207329c420e6a9d55c3a718418ab021b65a7 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Fri, 24 Sep 2021 20:30:00 -0700 Subject: [PATCH 09/14] Add any step handling for marks on RangeControl --- packages/components/src/range-control/rail.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/components/src/range-control/rail.js b/packages/components/src/range-control/rail.js index cc8fdc33062e7f..34caa88a736228 100644 --- a/packages/components/src/range-control/rail.js +++ b/packages/components/src/range-control/rail.js @@ -44,6 +44,9 @@ function Marks( { step = 1, value = 0, } ) { + if ( step === 'any' ) { + step = 1; + } const marksData = useMarks( { marks, min, max, step, value } ); return ( From f4cbeb6dfc6ca6ea70a176e0500c9fc549861c32 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Mon, 27 Sep 2021 07:59:17 -0700 Subject: [PATCH 10/14] Restore jump step hook in NumberControl --- packages/components/src/number-control/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/components/src/number-control/index.js b/packages/components/src/number-control/index.js index 2a917b2c6f8159..66ed87459a3fbd 100644 --- a/packages/components/src/number-control/index.js +++ b/packages/components/src/number-control/index.js @@ -17,6 +17,7 @@ import { Input } from './styles/number-control-styles'; import * as inputControlActionTypes from '../input-control/reducer/actions'; import { composeStateReducers } from '../input-control/reducer/reducer'; import { add, subtract, roundClamp } from '../utils/math'; +import { useJumpStep } from '../utils/hooks'; import { isValueEmpty } from '../utils/values'; export function NumberControl( @@ -40,6 +41,8 @@ export function NumberControl( ref ) { const baseStep = step === 'any' ? 1 : parseFloat( step ); + const jumpStep = useJumpStep( { baseStep, shiftStep, isShiftStepEnabled } ); + const baseValue = roundClamp( 0, min, max, baseStep ); const constrainValue = ( value, stepOverride ) => { // When step is "any" clamp the value, otherwise round and clamp it @@ -176,7 +179,7 @@ export function NumberControl( min={ min } ref={ ref } required={ required } - step={ step } + step={ jumpStep } type={ typeProp } value={ valueProp } __unstableStateReducer={ composeStateReducers( From 0e5bb09731ee8464e39160980fa1f21996d7b61a Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Tue, 28 Sep 2021 09:44:52 -0700 Subject: [PATCH 11/14] Fix use of jump step hook and doc fixups Co-authored-by: Marco Ciampini --- packages/components/src/number-control/README.md | 4 ++-- packages/components/src/number-control/index.js | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/components/src/number-control/README.md b/packages/components/src/number-control/README.md index 396554ce4977dd..2e57f69fbc36c4 100644 --- a/packages/components/src/number-control/README.md +++ b/packages/components/src/number-control/README.md @@ -87,7 +87,7 @@ The maximum `value` allowed. - Type: `Number` - Required: No -- Default: Infinity +- Default: `Infinity` ### min @@ -95,7 +95,7 @@ The minimum `value` allowed. - Type: `Number` - Required: No -- Default: -Infinity +- Default: `-Infinity` ### required diff --git a/packages/components/src/number-control/index.js b/packages/components/src/number-control/index.js index 66ed87459a3fbd..24c8b56a6dbc51 100644 --- a/packages/components/src/number-control/index.js +++ b/packages/components/src/number-control/index.js @@ -41,7 +41,11 @@ export function NumberControl( ref ) { const baseStep = step === 'any' ? 1 : parseFloat( step ); - const jumpStep = useJumpStep( { baseStep, shiftStep, isShiftStepEnabled } ); + const jumpStep = useJumpStep( { + step: baseStep, + shiftStep, + isShiftStepEnabled, + } ); const baseValue = roundClamp( 0, min, max, baseStep ); const constrainValue = ( value, stepOverride ) => { From a21729377d721e0b9589009a270e187315c66673 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Mon, 4 Oct 2021 17:52:18 -0700 Subject: [PATCH 12/14] Again remove jump step hook --- packages/components/src/number-control/index.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/components/src/number-control/index.js b/packages/components/src/number-control/index.js index 24c8b56a6dbc51..00ea6756a1ebab 100644 --- a/packages/components/src/number-control/index.js +++ b/packages/components/src/number-control/index.js @@ -17,7 +17,6 @@ import { Input } from './styles/number-control-styles'; import * as inputControlActionTypes from '../input-control/reducer/actions'; import { composeStateReducers } from '../input-control/reducer/reducer'; import { add, subtract, roundClamp } from '../utils/math'; -import { useJumpStep } from '../utils/hooks'; import { isValueEmpty } from '../utils/values'; export function NumberControl( @@ -40,17 +39,12 @@ export function NumberControl( }, ref ) { - const baseStep = step === 'any' ? 1 : parseFloat( step ); - const jumpStep = useJumpStep( { - step: baseStep, - shiftStep, - isShiftStepEnabled, - } ); - + const isStepAny = step === 'any'; + const baseStep = isStepAny ? 1 : parseFloat( step ); const baseValue = roundClamp( 0, min, max, baseStep ); const constrainValue = ( value, stepOverride ) => { // When step is "any" clamp the value, otherwise round and clamp it - return step === 'any' + return isStepAny ? Math.min( max, Math.max( min, value ) ) : roundClamp( value, min, max, stepOverride ?? baseStep ); }; @@ -183,7 +177,7 @@ export function NumberControl( min={ min } ref={ ref } required={ required } - step={ jumpStep } + step={ step } type={ typeProp } value={ valueProp } __unstableStateReducer={ composeStateReducers( From cbc8ff0d930dba335a05a7e67a8337b417e23334 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Mon, 4 Oct 2021 22:25:21 -0700 Subject: [PATCH 13/14] Add `NumberControl` tests for `step="any"` --- .../src/number-control/test/index.js | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/packages/components/src/number-control/test/index.js b/packages/components/src/number-control/test/index.js index 2ac4c008977187..db51a66681c670 100644 --- a/packages/components/src/number-control/test/index.js +++ b/packages/components/src/number-control/test/index.js @@ -170,6 +170,16 @@ describe( 'NumberControl', () => { expect( input.value ).toBe( '-4' ); } ); + it( 'should increment while preserving the decimal value when `step` is “any”', () => { + render( ); + + const input = getInput(); + input.focus(); + fireKeyDown( { keyCode: UP } ); + + expect( input.value ).toBe( '867.5309' ); + } ); + it( 'should increment by shiftStep on key UP + shift press', () => { render( ); @@ -180,6 +190,16 @@ describe( 'NumberControl', () => { expect( input.value ).toBe( '20' ); } ); + it( 'should increment by shiftStep while preserving the decimal value when `step` is “any”', () => { + render( ); + + const input = getInput(); + input.focus(); + fireKeyDown( { keyCode: UP, shiftKey: true } ); + + expect( input.value ).toBe( '867.5309' ); + } ); + it( 'should increment by custom shiftStep on key UP + shift press', () => { render( ); @@ -254,6 +274,16 @@ describe( 'NumberControl', () => { expect( input.value ).toBe( '-6' ); } ); + it( 'should decrement while preserving the decimal value when `step` is “any”', () => { + render( ); + + const input = getInput(); + input.focus(); + fireKeyDown( { keyCode: DOWN } ); + + expect( input.value ).toBe( '867.5309' ); + } ); + it( 'should decrement by shiftStep on key DOWN + shift press', () => { render( ); @@ -264,6 +294,16 @@ describe( 'NumberControl', () => { expect( input.value ).toBe( '0' ); } ); + it( 'should decrement by shiftStep while preserving the decimal value when `step` is “any”', () => { + render( ); + + const input = getInput(); + input.focus(); + fireKeyDown( { keyCode: DOWN, shiftKey: true } ); + + expect( input.value ).toBe( '867.5309' ); + } ); + it( 'should decrement by custom shiftStep on key DOWN + shift press', () => { render( ); From f39dc37c5a574d15c8efae986fd9e160802dbd88 Mon Sep 17 00:00:00 2001 From: Mitchell Austin Date: Sun, 10 Oct 2021 20:36:03 -0700 Subject: [PATCH 14/14] Add changelog entry --- packages/components/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index c743ebb3dc8a5c..8ee778054d30ab 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -9,9 +9,10 @@ - Changed `RangeControl` component to not apply `shiftStep` to inputs from its `` ([35020](https://github.com/WordPress/gutenberg/pull/35020)). - Removed `isAction` prop from `Item`. The component will now rely on `onClick` to render as a `button` ([35152](https://github.com/WordPress/gutenberg/pull/35152)). -### New Feature +### New Features - Add an experimental `Navigator` components ([#34904](https://github.com/WordPress/gutenberg/pull/34904)) as a replacement for the previous `Navigation` related components. +- Added support for `step="any"` in `NumberControl` and `RangeControl` ([#34542](https://github.com/WordPress/gutenberg/pull/34542)). ### Bug Fix