Skip to content

Commit 2a57538

Browse files
committed
Merge pull request #4 from vlad-ignatov/dev
Merge branch dev for v2
2 parents c24a4d7 + 561b739 commit 2a57538

19 files changed

+2200
-1115
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
index.js
2+
dist/

README.md

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# <img align="right" src="http://react-numeric-input.jsdevel.com/ReactNumericInput.png" width="197"/>React Numeric Input
1+
# <img align="right" src="http://vlad-ignatov.github.io/react-numeric-input/examples/v2.0.0/screenshot.png" width="123"/>React Numeric Input
22

33
[![Build Status](https://travis-ci.org/vlad-ignatov/react-numeric-input.svg?branch=master)](https://travis-ci.org/vlad-ignatov/react-numeric-input)
44

@@ -8,7 +8,7 @@ the browsers. Additionally this component offers more flexible options and can
88
be used for any values (differently formatted representations of the internal
99
numeric value).
1010

11-
[Live demo](http://react-numeric-input.jsdevel.com/)
11+
[Live demo](http://vlad-ignatov.github.io/react-numeric-input/examples/v2.0.0/index.html)
1212

1313
## Installation
1414
```sh
@@ -58,68 +58,85 @@ function myFormat(num) {
5858
```
5959

6060
## Props
61-
Option | Type | Default
62-
-------------|---------------------|:-------:
63-
**value** |`number` or `string` | `""` which converts to 0
64-
**min** |`number` | `Number.MIN_SAFE_INTEGER`
65-
**max** |`number` | `Number.MAX_SAFE_INTEGER`
66-
**step** |`number` | 1
67-
**precision**|`number` | 0
68-
**parse** |`function` | parseFloat
69-
**format** |`function` | none
70-
**className**| `string` | none
71-
**disabled** |`boolean` | none
72-
**readOnly** |`boolean` | none
73-
**style** |`object` | none
74-
**size** |`number` | none
61+
Option | Type | Default
62+
-------------|-------------------------------------|:-------:
63+
**value** |`number` or `string` | `""` which converts to 0
64+
**min** |`number` | `Number.MIN_SAFE_INTEGER`
65+
**max** |`number` | `Number.MAX_SAFE_INTEGER`
66+
**step** |`number` | 1
67+
**precision**|`number` | 0
68+
**parse** |`function` | parseFloat
69+
**format** |`function` | none
70+
**className**|`string` | none
71+
**disabled** |`boolean` | none
72+
**readOnly** |`boolean` | none
73+
**style** |`object` | none
74+
**size** |`number` or `string` | none
75+
**mobile** |`true`, `false`, 'auto' or `function`|`auto`
7576

7677
Any other option is passed directly the input created by the component. Just
7778
don't forget to camelCase the attributes. For example `readonly` must be `readOnly`.
7879
See examples/index.html for examples.
7980

8081
## Styling
81-
This component comes with styles written in LESS and precompiled to CSS in
82-
[src/style](./src/style). It's up to you to decide how to use them but here are a few options:
83-
* Copy [src/style/NumericInput.css](./src/style/NumericInput.css) code to your css
84-
* Setup less preprocessing from [src/style/NumericInput.less](./src/style/NumericInput.less) to wherever you need.
85-
* Use modern tool like webpack and then just do `require('node_modules/react-numeric-input/src/style/NumericInput.less');`.
86-
See [examples/examples.jsx](./examples/examples.jsx) for example.
87-
88-
89-
90-
####v2.0.0 (not released yet)
91-
92-
93-
----------
94-
Since v2 the component uses inline styles which you can customise. The `style` prop is not added directly to the component but instead it is a container for styles which you can overwrite. For example
82+
The component uses inline styles which you can customize. The `style` prop is not added
83+
directly to the component but instead it is a container for styles which you can overwrite.
84+
For example
9585
```xml
9686
<NumericInput style={{
9787
input: {
9888
color: 'red'
9989
}
10090
}}>
10191
```
102-
You can modify the styles for everything including states like `:hover`, `:active` and `:disabled`. Take a look at the source to see what styles are supported.
103-
104-
Also, the style is stored as static class property so that you can change it and affect all the components from your script. Example:
92+
You can modify the styles for everything including states like `:hover`, `:active` and
93+
`:disabled`. Take a look at the source to see what styles are supported. Also, the style is
94+
stored as static class property so that you can change it and affect all the components
95+
from your script. Example:
10596
```js
10697
import NumericInput from 'react-numeric-input';
10798
NumericInput.style.input.color = 'red';
10899
```
109100

110-
Finally, you can still use CSS if you want. Each component's root element has the `react-numeric-input` class so that it is easy to find these widgets on the page. However, keep in mind that because of the inline styles you might need to use `!important` for some rules. Example:
101+
Finally, you can still use CSS if you want. Each component's root element has the
102+
`react-numeric-input` class so that it is easy to find these widgets on the page. However,
103+
keep in mind that because of the inline styles you might need to use `!important` for some
104+
rules. Example:
111105
```css
112106
.react-numeric-input input {
113107
color: red;
114108
}
115109
```
116110

117111
## Keyboard navigation
118-
* You can use <kbd>Up</kbd> and <kbd>Down</kbd> arrow keys to increment/decrement the input value.
119-
* <kbd>Ctrl/Command + Up</kbd> and <kbd>Ctrl/Command + Down</kbd> to use smaller step (`step / 10`).
112+
* You can use <kbd></kbd> and <kbd></kbd> arrow keys to increment/decrement the input value.
113+
* <kbd>Ctrl + ⬆</kbd> or <kbd>⌘ + ⬆</kbd> and <kbd>Ctrl + ⬇</kbd> or <kbd>⌘ + ⬇</kbd> to use smaller step (`step / 10`).
120114
Note that this will only work if you have specified a `precision` option that supports it.
121-
* <kbd>Shift + Up</kbd> and <kbd>Shift + Down</kbd> to use bigger step (`step * 10`).
122-
115+
* <kbd>Shift + ⬆</kbd> and <kbd>Shift + ⬇</kbd> to use bigger step (`step * 10`).
116+
117+
## Integration with external scripts
118+
This component aims to provide good integration not only with React but with any third party script
119+
that might want to work with it on the current page.
120+
121+
### valueAsNumber
122+
The native number inputs have special property called `valueAsNumber`. It provides access to the
123+
value as number to be used by scripts. In this react component this becomes even more desirable as
124+
the display value might be formatted and have nothing in common with the underlying value meaning
125+
that one might need to call parse to find out what the numeric value is. For that reason this
126+
component exposes `_valueAsNumber` property on the input element. Note the underscore in front -
127+
the `valueAsNumber` is readonly and not even accessible on input[type="text"]. Also keep in mind
128+
that this really is a number (float) so it might be different from the displayed value. For
129+
example an input showing "12.30" will have `_valueAsNumber` of `12.3`.
130+
131+
### setValue()
132+
An external script that does not "understand" React can still work with this component by reading
133+
the `_valueAsNumber` or by calling the `setValue()` method exposed on the input element. Here is
134+
an example with jQuery:
135+
```js
136+
$('input[name="some-input"]')[0].setValue('123mph');
137+
```
138+
Calling this method will invoke the component's `parse` method with the provided argument and then
139+
it will `setState` causing the usual re-rendering.
123140

124141
## License
125142
MIT

__tests__/NumericInput.test.jsx

Lines changed: 73 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
/* global describe, it, ReactDOM, React */
22
import expect from 'expect';
3-
import { default as NumericInput, DELAY } from '../src/NumericInput.jsx';
3+
import NumericInput from '../src/NumericInput.jsx';
44

55
const TestUtils = React.addons.TestUtils;
66
const KEYCODE_UP = 38;
77
const KEYCODE_DOWN = 40;
8+
const DELAY = NumericInput.DELAY;
89

910

10-
describe('NumericInput', () => {
11+
describe('NumericInput', function() {
12+
this.timeout(10000);
1113

1214
it('works like inpit[type="number"] by default', () => {
13-
var widget = TestUtils.renderIntoDocument(
14-
<NumericInput />
15-
),
16-
widgetNode = ReactDOM.findDOMNode(widget),
17-
inputNode = widgetNode.firstChild;
18-
19-
expect(inputNode.value).toEqual('');
20-
expect(inputNode.type).toEqual('text');
21-
// expect(inputNode.className).toEqual('numeric-input-input');
15+
var widget = TestUtils.renderIntoDocument(<NumericInput />);
16+
expect(widget.refs.input.value).toEqual('');
17+
expect(widget.refs.input.type).toEqual('text');
2218
});
2319

2420
it('accepts all the props', () => {
@@ -32,10 +28,9 @@ describe('NumericInput', () => {
3228
className="form-control"
3329
/>
3430
),
35-
widgetNode = ReactDOM.findDOMNode(widget),
36-
inputNode = widgetNode.firstChild;
31+
inputNode = widget.refs.input;
3732

38-
// Test the precision
33+
// Test the precision
3934
expect(inputNode.value).toEqual('5.00');
4035
expect(inputNode.className).toEqual('form-control');
4136

@@ -55,6 +50,7 @@ describe('NumericInput', () => {
5550
});
5651

5752
it('can auto-increase', (done) => {
53+
this.timeout
5854
var widget = TestUtils.renderIntoDocument(<NumericInput/>),
5955
widgetNode = ReactDOM.findDOMNode(widget),
6056
inputNode = widgetNode.firstChild,
@@ -177,142 +173,168 @@ describe('NumericInput', () => {
177173
expect(inputNode.value).toEqual('');
178174
});
179175

180-
// Testing styles ------------------------------------------------------------------
176+
// setValue() and getValueAsNumber() ---------------------------------------
177+
it('exposes setValue() and getValueAsNumber() on the input', () => {
178+
var widget = TestUtils.renderIntoDocument(<NumericInput />);
179+
expect(widget.refs.input.getValueAsNumber()).toEqual(0);
180+
widget.refs.input.setValue(123.56);
181+
expect(widget.refs.input.getValueAsNumber()).toEqual(123.56);
182+
});
183+
184+
// Testing styles ----------------------------------------------------------
181185
it('can set wrapper styles', () => {
182186
var widget = TestUtils.renderIntoDocument(
183187
<NumericInput style={{
184188
wrap: {
185-
color: 'red'
189+
fontStyle: 'italic'
186190
}
187191
}}/>
188192
),
189193
widgetNode = ReactDOM.findDOMNode(widget);
190194

191-
expect(widgetNode.style.color).toEqual('red');
195+
expect(widgetNode.style.fontStyle).toEqual('italic');
192196
});
193197

194198
it('can set input styles', () => {
195199
var widget = TestUtils.renderIntoDocument(
196200
<NumericInput style={{
197201
input: {
198-
color: 'red'
202+
fontStyle: 'italic'
199203
}
200204
}}/>
201205
),
202206
widgetNode = ReactDOM.findDOMNode(widget),
203207
inputNode = widgetNode.firstChild;
204208

205-
expect(inputNode.style.color).toEqual('red');
209+
expect(inputNode.style.fontStyle).toEqual('italic');
206210
});
207211

208212
it('can set btnUp styles', () => {
209213
var widget = TestUtils.renderIntoDocument(
210214
<NumericInput style={{
211215
btnUp: {
212-
color: 'red'
216+
fontStyle: 'italic'
213217
}
214218
}}/>
215219
),
216220
widgetNode = ReactDOM.findDOMNode(widget),
217221
btnNode = widgetNode.firstChild.nextElementSibling;
218222

219-
expect(btnNode.style.color).toEqual('red');
223+
expect(btnNode.style.fontStyle).toEqual('italic');
220224
});
221225

222226
it('can set btnDown styles', () => {
223227
var widget = TestUtils.renderIntoDocument(
224228
<NumericInput style={{
225229
btnDown: {
226-
color: 'red'
230+
fontStyle: 'italic'
227231
}
228232
}}/>
229233
),
230234
widgetNode = ReactDOM.findDOMNode(widget),
231235
btnNode = widgetNode.lastChild;
232236

233-
expect(btnNode.style.color).toEqual('red');
237+
expect(btnNode.style.fontStyle).toEqual('italic');
234238
});
235239

236240
it('can set arrowDown styles', () => {
237241
var widget = TestUtils.renderIntoDocument(
238242
<NumericInput style={{
239243
arrowDown: {
240-
color: 'red'
244+
fontStyle: 'italic'
241245
}
242-
}}/>
246+
}} mobile={false}/>
243247
),
244248
widgetNode = ReactDOM.findDOMNode(widget),
245249
arrowDown = widgetNode.lastChild.firstChild;
246250

247-
expect(arrowDown.style.color).toEqual('red');
251+
expect(arrowDown.style.fontStyle).toEqual('italic');
248252
});
249253

250254
it('can set arrowUp styles', () => {
251255
var widget = TestUtils.renderIntoDocument(
252256
<NumericInput style={{
253257
arrowUp: {
254-
color: 'red'
258+
fontStyle: 'italic'
255259
}
256-
}}/>
260+
}} mobile={false}/>
257261
),
258262
widgetNode = ReactDOM.findDOMNode(widget),
259263
arrowUp = widgetNode.firstChild.nextElementSibling.firstChild;
260264

261-
expect(arrowUp.style.color).toEqual('red');
265+
expect(arrowUp.style.fontStyle).toEqual('italic');
262266
});
263267

264268
it('can set btn:state styles', () => {
265269
var disabled = false;
266270
var widget = TestUtils.renderIntoDocument(
267271
<NumericInput disabled={disabled} style={{
268-
'btn' : { color: 'red' },
269-
'btn:hover' : { color: 'blue' },
270-
'btn:active' : { color: 'pink' },
271-
'btn:disabled': { color: 'skyblue'}
272-
}}/>
272+
'btn' : { color: 'rgb(1, 2, 3)' },
273+
'btn:hover' : { color: 'rgb(2, 3, 4)' },
274+
'btn:active' : { color: 'rgb(3, 4, 5)' },
275+
'btn:disabled': { color: 'rgb(4, 5, 6)' }
276+
}} mobile={false}/>
273277
),
274278
widgetNode = ReactDOM.findDOMNode(widget),
275279
btnUpNode = widgetNode.firstChild.nextElementSibling,
276280
btnDownNode = widgetNode.lastChild;
277281

278282
// normal
279-
expect(btnUpNode.style.color).toEqual('red');
280-
expect(btnDownNode.style.color).toEqual('red');
283+
expect(btnUpNode.style.color).toEqual('rgb(1, 2, 3)');
284+
expect(btnDownNode.style.color).toEqual('rgb(1, 2, 3)');
281285

282286
// :hover
283287
TestUtils.Simulate.mouseEnter(btnUpNode);
284-
expect(btnUpNode.style.color).toEqual('blue');
288+
expect(btnUpNode.style.color).toEqual('rgb(2, 3, 4)');
285289
TestUtils.Simulate.mouseEnter(btnDownNode);
286-
expect(btnDownNode.style.color).toEqual('blue');
290+
expect(btnDownNode.style.color).toEqual('rgb(2, 3, 4)');
287291

288292
// :active
289293
TestUtils.Simulate.mouseDown(btnUpNode);
290-
expect(btnUpNode.style.color).toEqual('pink');
294+
expect(btnUpNode.style.color).toEqual('rgb(3, 4, 5)');
291295
TestUtils.Simulate.mouseDown(btnDownNode);
292-
expect(btnDownNode.style.color).toEqual('pink');
296+
expect(btnDownNode.style.color).toEqual('rgb(3, 4, 5)');
293297

294298
// :disabled
295299
widget = TestUtils.renderIntoDocument(
296300
<NumericInput disabled style={{
297-
'btn' : { color: 'red' },
298-
'btn:hover' : { color: 'blue' },
299-
'btn:active' : { color: 'pink' },
300-
'btn:disabled': { color: 'skyblue'}
301-
}}/>
301+
'btn' : { color: 'rgb(1, 2, 3)'},
302+
'btn:hover' : { color: 'rgb(2, 3, 4)'},
303+
'btn:active' : { color: 'rgb(3, 4, 5)'},
304+
'btn:disabled': { color: 'rgb(4, 5, 6)'}
305+
}} mobile={false}/>
302306
);
303307
widgetNode = ReactDOM.findDOMNode(widget);
304308
btnUpNode = widgetNode.firstChild.nextElementSibling;
305309
btnDownNode = widgetNode.lastChild;
306310

307-
expect(btnUpNode.style.color).toEqual('skyblue');
308-
expect(btnDownNode.style.color).toEqual('skyblue');
311+
expect(btnUpNode.style.color).toEqual('rgb(4, 5, 6)');
312+
expect(btnDownNode.style.color).toEqual('rgb(4, 5, 6)');
309313
TestUtils.Simulate.mouseEnter(btnUpNode);
310-
expect(btnUpNode.style.color).toEqual('skyblue');
314+
expect(btnUpNode.style.color).toEqual('rgb(4, 5, 6)');
311315
TestUtils.Simulate.mouseEnter(btnDownNode);
312-
expect(btnDownNode.style.color).toEqual('skyblue');
316+
expect(btnDownNode.style.color).toEqual('rgb(4, 5, 6)');
313317
TestUtils.Simulate.mouseDown(btnUpNode);
314-
expect(btnUpNode.style.color).toEqual('skyblue');
318+
expect(btnUpNode.style.color).toEqual('rgb(4, 5, 6)');
315319
TestUtils.Simulate.mouseDown(btnDownNode);
316-
expect(btnDownNode.style.color).toEqual('skyblue');
320+
expect(btnDownNode.style.color).toEqual('rgb(4, 5, 6)');
317321
});
322+
323+
it ('can set mobile styles', () => {
324+
var widget = TestUtils.renderIntoDocument(<NumericInput mobile/>),
325+
widgetNode = ReactDOM.findDOMNode(widget),
326+
btnUpNode = widgetNode.firstChild.nextElementSibling,
327+
btnDownNode = widgetNode.lastChild;
328+
329+
expect(btnUpNode.style.bottom).toEqual('2px');
330+
expect(btnDownNode.style.left).toEqual('2px');
331+
332+
widget = TestUtils.renderIntoDocument(<NumericInput mobile={false}/>),
333+
widgetNode = ReactDOM.findDOMNode(widget),
334+
btnUpNode = widgetNode.firstChild.nextElementSibling,
335+
btnDownNode = widgetNode.lastChild;
336+
337+
expect(btnUpNode.style.bottom).toEqual('50%');
338+
expect(btnDownNode.style.top).toEqual('50%');
339+
})
318340
});

0 commit comments

Comments
 (0)