NumberSliderWidget

Source

index.js
import React from 'react';
import PropTypes from 'prop-types';

import style from 'PVWStyle/ReactWidgets/NumberSliderWidget.mcss';

export default class NumberSliderWidget extends React.Component {
constructor(props) {
super(props);
this.state = {
max: props.max,
min: props.min,
value: props.value,
};

// Bind callback
this.valInput = this.valInput.bind(this);
this.value = this.value.bind(this);
}

valInput(e) {
this.setState({ value: e.target.value });
if (this.props.onChange) {
if (this.props.name) {
e.target.name = this.props.name;
}
this.props.onChange(e);
}
}

value(newVal) {
if (newVal === null || newVal === undefined) {
return this.state.value;
}

const value = Math.max(this.state.min, Math.min(newVal, this.state.max));
this.setState({ value });
return value;
}

render() {
const [min, max] = [this.props.min, this.props.max];

return (
<div className={style.container}>
<input
type="range"
className={style.range}
value={this.props.value}
onChange={this.valInput}
max={max}
min={min}
step={this.props.step || 'any'}
/>
<input
type="number"
className={style.text}
value={this.props.value}
onChange={this.valInput}
max={max}
min={min}
step="any"
/>
</div>
);
}
}

NumberSliderWidget.propTypes = {
max: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
min: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
name: PropTypes.string,
onChange: PropTypes.func,
step: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
};

NumberSliderWidget.defaultProps = {
max: 100,
min: 0,
step: 1,
value: 50,
name: '',
onChange: undefined,
};
test.js
import expect from 'expect';
import NumberSliderControl from './index';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react/lib/ReactTestUtils';

class Mock extends React.Component {
constructor(props) {
super(props);
this.state = {
val: 25,
name: 'razzmatazz',
};

// Bind callback
this.updateVal = this.updateVal.bind(this);
}

updateVal(e) {
this.setState({ val: e.target.value, name: e.target.name });
}

render() {
return (
<NumberSliderControl
name={newName}
min={20}
max={30}
value={this.state.val}
onChange={this.updateVal}
/>
);
}
}

describe('NumberSliderControl', function() {
afterEach(function(done) {
ReactDOM.unmountComponentAtNode(document.body);
document.body.innerHTML = '';
setTimeout(done);
});

it('has two inputs whose values are equal', function() {
var el = TestUtils.renderIntoDocument(
<NumberSliderControl min={20} value={25} max={30} />
),
inputs = TestUtils.scryRenderedDOMComponentsWithTag(el, 'input');
expect(inputs.length).toBe(2);
expect(inputs[0].value).toBe(inputs[1].value);
});
it('takes an external value and a value can be read with the same function', function() {
var el = TestUtils.renderIntoDocument(<NumberSliderControl />),
newVal = 75;

el.value(newVal);
expect(el.value()).toEqual(newVal);
});
it('clamps a value if a given value is too big', function() {
var el = TestUtils.renderIntoDocument(<NumberSliderControl />),
newVal = 250,
expectedVal = 100;

el.value(newVal);
expect(el.value()).toEqual(expectedVal);
});
it('keeps the values of two inputs the same', function() {
var oldName = 'razzmatazz',
newName = 'pogo';

var el = TestUtils.renderIntoDocument(<Mock />),
newVal = '28',
inputSlider = ReactDOM.findDOMNode(el).querySelector('input[type=range]'),
inputField = ReactDOM.findDOMNode(el).querySelector('input[type=number]');

expect(el.state.val).toEqual(25); //sanity
expect(el.state.name).toEqual(oldName); //sanity

TestUtils.Simulate.change(inputSlider, { target: { value: newVal } });
expect(el.state.val).toEqual(newVal);
expect(el.state.name).toEqual(newName);
expect(inputField.value).toEqual(newVal);
});
});