Skip to content

Commit 42337dd

Browse files
committed
Spinner: Fix step mismatches whenever stepping, paging or using the value setter.
Thanks Nate Ferrero
1 parent cedac75 commit 42337dd

File tree

4 files changed

+58
-39
lines changed

4 files changed

+58
-39
lines changed

tests/unit/spinner/spinner_core.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,12 @@ test( "mouse click on up button, increases value not greater than max", function
109109
});
110110

111111
test( "mousewheel on input", function() {
112-
expect( 5 );
112+
expect( 4 );
113113

114-
var element = $( "#spin" ).spinner({
114+
var element = $( "#spin" ).val( 0 ).spinner({
115115
step: 2
116116
});
117117

118-
equal( element.val(), 0 );
119-
120118
element.trigger( "mousewheel" );
121119
equal( element.val(), 0, "mousewheel event without delta does not change value" );
122120

@@ -198,10 +196,11 @@ test( "precision", function() {
198196
equal( element.val(), "0.0501", "precision from step" );
199197

200198
element.val( 1.05 ).spinner( "option", {
201-
step: 1
199+
step: 1,
200+
min: -9.95
202201
});
203202
element.spinner( "stepDown" );
204-
equal( element.val(), "0.05", "precision from value" );
203+
equal( element.val(), "0.05", "precision from min" );
205204
});
206205

207206
})( jQuery );

tests/unit/spinner/spinner_methods.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ test( "disable", function() {
5454

5555
test( "enable", function() {
5656
expect( 5 );
57-
var element = $( "#spin" ).spinner({ disabled: true })
57+
var element = $( "#spin" ).val( 1 ).spinner({ disabled: true })
5858
wrapper = element.spinner( "widget" );
5959

6060
ok( wrapper.hasClass( "ui-spinner-disabled" ), "before: wrapper has ui-spinner-disabled class" );
@@ -66,7 +66,7 @@ test( "enable", function() {
6666
ok( !element.is( ":disabled" ), "after: input does not have disabled attribute" );
6767

6868
spinner_simulateKeyDownUp( element, $.ui.keyCode.UP );
69-
equals( 1, element.val(), "keyboard - value does not change on key UP" );
69+
equals( 2, element.val(), "keyboard - value changes on key UP" );
7070
});
7171

7272
test( "pageDown", function() {
@@ -117,23 +117,23 @@ test( "stepDown", function() {
117117
});
118118

119119
element.spinner( "stepDown" );
120-
equals( element.val(), -2, "stepDown 1 step" );
120+
equals( element.val(), "-1", "stepDown 1 step" );
121121

122122
element.spinner( "stepDown", 5 );
123-
equals( element.val(), -12, "stepDown 5 steps" );
123+
equals( element.val(), "-11", "stepDown 5 steps" );
124124

125125
element.spinner( "stepDown", 4 );
126-
equals( element.val(), -15, "close to min and stepDown 4 steps" );
126+
equals( element.val(), "-15", "close to min and stepDown 4 steps" );
127127

128128
element.spinner( "stepDown" );
129-
equals( element.val(), -15, "at min and stepDown 1 step" );
129+
equals( element.val(), "-15", "at min and stepDown 1 step" );
130130
});
131131

132132
test( "stepUp", function() {
133133
expect( 4 );
134134
var element = $( "#spin" ).val( 0 ).spinner({
135135
step: 2,
136-
max: 15
136+
max: 16
137137
});
138138

139139
element.spinner( "stepUp" );
@@ -143,20 +143,22 @@ test( "stepUp", function() {
143143
equals( element.val(), 12, "stepUp 5 steps" );
144144

145145
element.spinner( "stepUp", 4 );
146-
equals( element.val(), 15, "close to min and stepUp 4 steps" );
146+
equals( element.val(), 16, "close to min and stepUp 4 steps" );
147147

148148
element.spinner( "stepUp" );
149-
equals( element.val(), 15, "at max and stepUp 1 step" );
149+
equals( element.val(), 16, "at max and stepUp 1 step" );
150150
});
151151

152152
test( "value", function() {
153153
expect( 2 );
154-
var element = $( "#spin" ).val( 0 ).spinner();
154+
var element = $( "#spin" ).val( 0 ).spinner({
155+
step: 3
156+
});
155157

156158
element.spinner( "value", 10 );
157-
equals( element.val(), 10, "change value via value method" );
159+
equals( element.val(), 9, "change value via value method" );
158160

159-
equals( element.spinner( "value" ), 10, "get value via value method" );
161+
equals( element.spinner( "value" ), 9, "get value via value method" );
160162
});
161163

162164
})( jQuery );

tests/unit/spinner/spinner_options.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,11 @@ test( "step, 2", function() {
123123
equals( element.val(), "2", "stepUp" );
124124

125125
element.spinner( "value", "10.5" );
126-
equals( element.val(), "10.5", "value reset to 10.5" );
126+
equals( element.val(), "10", "value reset to 10" );
127127

128+
element.val( "4.5" );
128129
element.spinner( "stepUp" );
129-
equals( element.val(), "12.5", "stepUp" );
130+
equals( element.val(), "6", "stepUp" );
130131
});
131132

132133
test( "step, 0.7", function() {

ui/jquery.ui.spinner.js

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -213,20 +213,16 @@ $.widget( "ui.spinner", {
213213
},
214214

215215
_spin: function( step, event ) {
216+
var value = this.value() || 0;
217+
216218
if ( !this.counter ) {
217219
this.counter = 1;
218220
}
219221

220-
var value = this.value(),
221-
newVal = value + step * this._increment( this.counter ),
222-
// fix precision from bad JS floating point math
223-
precision = Math.max( this._precision( value ),
224-
this._precision( this.options.step ) );
225-
// clamp the new value
226-
newVal = this._trimValue( newVal.toFixed( precision ) );
222+
value = this._adjustValue( value + step * this._increment( this.counter ) );
227223

228-
if ( !this.spinning || this._trigger( "spin", event, { value: newVal } ) !== false) {
229-
this._value( newVal );
224+
if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
225+
this._value( value );
230226
this.counter++;
231227
}
232228
},
@@ -244,19 +240,40 @@ $.widget( "ui.spinner", {
244240
},
245241

246242
_precision: function( num ) {
243+
var precision = this._precisionOf( this.options.step );
244+
if ( this.options.min !== null ) {
245+
precision = Math.max( precision, this._precisionOf( this.options.min ) );
246+
}
247+
return precision;
248+
},
249+
250+
_precisionOf: function( num ) {
247251
var str = num.toString(),
248252
decimal = str.indexOf( "." );
249253
return decimal === -1 ? 0 : str.length - decimal - 1;
250254
},
251255

252-
_trimValue: function( value ) {
253-
var options = this.options;
256+
_adjustValue: function( value ) {
257+
var base, aboveMin,
258+
options = this.options;
254259

255-
if ( options.max != null && value > options.max) {
260+
// make sure we're at a valid step
261+
// - find out where we are relative to the base (min or 0)
262+
base = options.min !== null ? options.min : 0;
263+
aboveMin = value - base;
264+
// - round to the nearest step
265+
aboveMin = Math.round(aboveMin / options.step) * options.step;
266+
// - rounding is based on 0, so adjust back to our base
267+
value = base + aboveMin;
268+
269+
// fix precision from bad JS floating point math
270+
value = parseFloat( value.toFixed( this._precision() ) );
271+
272+
// clamp the value
273+
if ( options.max !== null && value > options.max) {
256274
return options.max;
257275
}
258-
259-
if ( options.min != null && value < options.min ) {
276+
if ( options.min !== null && value < options.min ) {
260277
return options.min;
261278
}
262279

@@ -295,10 +312,10 @@ $.widget( "ui.spinner", {
295312
}),
296313

297314
_parse: function( val ) {
298-
if ( typeof val === "string" ) {
315+
if ( typeof val === "string" && val !== "" ) {
299316
val = window.Globalize && this.options.numberFormat ? Globalize.parseFloat( val ) : +val;
300317
}
301-
return isNaN( val ) ? null : val;
318+
return val === "" || isNaN( val ) ? null : val;
302319
},
303320

304321
_format: function( value ) {
@@ -320,13 +337,13 @@ $.widget( "ui.spinner", {
320337
},
321338

322339
// update the value without triggering change
323-
_value: function( value, ignoreRange ) {
340+
_value: function( value, allowAny ) {
324341
var parsed;
325342
if ( value !== "" ) {
326343
parsed = this._parse( value );
327344
if ( parsed !== null ) {
328-
if ( !ignoreRange ) {
329-
parsed = this._trimValue( parsed );
345+
if ( !allowAny ) {
346+
parsed = this._adjustValue( parsed );
330347
}
331348
value = this._format( parsed );
332349
}

0 commit comments

Comments
 (0)