I'm trying to test a slider component.
This slider component can be variable in width. When you click on the "track" of the slider it should change the value and trigger an onChange
callback. The value is a based on where you click on the track. If you click the halfway point when the min value is 100 and the max value is 200, then it should report a value of 150.
The problem I'm running into is that when I render the component using ReactTest.renderIntoDocument
the component doesn't have any width, so it can't calculate a new value when you click on it.
Here is the component Slider.js
import React, {PropTypes} from 'react';
import ReactDOM from 'react-dom';
import { noop } from 'lodash';
import style from './style.scss';
export default class Slider extends React.Component {
render() {
return (
<div
className='Slider'
onClick={this.handleClick.bind(this)}
{...this.props}
>
<div
className='handle'
style={{left: `${this.calculateLeft()}%`}}>
</div>
<div className='track'></div>
</div>
);
}
handleClick(e) {
let node = ReactDOM.findDOMNode(this);
let {clientX, clientY} = e;
let {offsetLeft, offsetWidth, clientWidth} = node;
let xPercent = (clientX - offsetLeft) / offsetWidth;
console.log(offsetLeft, offsetWidth, clientWidth, xPercent);
this.props.onChange(normalize(xPercent, this.props.min, this.props.max));
}
calculateLeft() {
let numerator = this.props.value - this.props.min;
let denominator = this.props.max - this.props.min;
return numerator / denominator * 100;
}
}
// Proptypes
// ----------------------------------------------------------------------------
Slider.propTypes = {
// Callback for when the value changes.
onChange: PropTypes.func,
// The value for when the slider is at 0%
min: PropTypes.number,
// The value for when the slider is at 100%
max: PropTypes.number,
// The starting value
value: validateValue,
}
Slider.defaultProps = {
onChange: noop,
min: 0,
max: 100,
}
// Custom Validation
// ----------------------------------------------------------------------------
function validateValue(props, propName, componentName) {
let value = props[propName];
if (typeof(value) !== 'number') {
return new Error(`value must be a number, got ${typeof(value)}`);
}
if (value > props.max || value < props.min) {
return new Error(
`value: ${value} must be between max: ${props.max}
and min: ${props.min}`
);
}
}
// Helpers
// ---------------------------------------------------------------------------
function normalize(floatValue, min, max) {
let range = max - min;
let normalizedValue = floatValue * range + min;
// cleverly restrict the value be between the min and max
return [min, normalizedValue, max].sort()[1];
}
Stylesheet (style.scss
):
.Slider {
position: relative;
display: block;
width: 100px;
.track {
height: 4px;
background: #666;
border-radius: 2px;
}
.handle {
width: 12px;
height: 12px;
background: #fff;
border-radius: 10px;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
transition: left 100ms linear;
}
}
Here is my test:
import Slider from './Slider';
import React from 'react';
import {
renderIntoDocument,
findRenderedDOMComponentWithClass,
findRenderedDOMComponentWithTag,
Simulate
} from 'react-addons-test-utils';
describe('Slider', function() {
describe('click', function() {
it('triggers the onChange callback', function() {
const onChange = sinon.spy();
const component = renderIntoDocument(
<Slider
style={{width: 100, height: 40}}
min={100}
max={200}
value={150}
onChange={onChange}
/>
);
const track = findRenderedDOMComponentWithClass(component, 'track');
Simulate.click(track, {clientY: 0, clientX: 10})
expect(onChange).to.have.been.calledWith(110);
});
});
});
Test output
LOG LOG: 0, 0, 0, Infinity
click
✗ triggers the onChange callback
AssertionError: expected onChange to have been called with arguments 10
onChange(200)
at /components/Slider/test.js:99 < webpack:///src/components/Slider/test.js:55:6
Those log statements are from the handleClick()
function in the component.
The width is zero so the denominator ends up being zero when calculating xPercent, which causes it to be Infinity. This causes it to just use the max
value of 200.
TLDR
How do I make the component have width when rendering it during a test?
Aucun commentaire:
Enregistrer un commentaire