diff --git a/src/js/ScrollArea.jsx b/src/js/ScrollArea.jsx index 8316a0e..45a5957 100644 --- a/src/js/ScrollArea.jsx +++ b/src/js/ScrollArea.jsx @@ -138,18 +138,23 @@ export default class ScrollArea extends React.Component { let springifiedContentStyle = withMotion ? modifyObjValues(contentStyle, x => spring(x)) : contentStyle; return ( - + { style => -
this.wrapper = x} style={this.props.style} className={classes} - onWheel={this.handleWheel.bind(this)}> -
this.content = x} - style={style} - className={contentClasses} - onTouchStart={this.handleTouchStart.bind(this)} - onTouchMove={this.handleTouchMove.bind(this)} - onTouchEnd={this.handleTouchEnd.bind(this)} - onKeyDown={this.handleKeyDown.bind(this)} - tabIndex={this.props.focusableTabIndex} +
this.wrapper = x} + className={classes} + style={this.props.style} + onWheel={this.handleWheel.bind(this)} + > +
this.content = x} + style={{ ...this.props.contentStyle, ...style }} + className={contentClasses} + onTouchStart={this.handleTouchStart.bind(this)} + onTouchMove={this.handleTouchMove.bind(this)} + onTouchEnd={this.handleTouchEnd.bind(this)} + onKeyDown={this.handleKeyDown.bind(this)} + tabIndex={this.props.focusableTabIndex} > {children}
@@ -475,4 +480,4 @@ ScrollArea.defaultProps = { contentWindow: (typeof window === "object") ? window : undefined, ownerDocument: (typeof document === "object") ? document : undefined, focusableTabIndex: 1 -}; \ No newline at end of file +}; diff --git a/src/js/Scrollbar.jsx b/src/js/Scrollbar.jsx index be85441..98258d4 100644 --- a/src/js/Scrollbar.jsx +++ b/src/js/Scrollbar.jsx @@ -26,7 +26,7 @@ class ScrollBar extends React.Component { if (this.props.ownerDocument) { this.props.ownerDocument.addEventListener("mousemove", this.bindedHandleMouseMove); this.props.ownerDocument.addEventListener("mouseup", this.bindedHandleMouseUp); - } + } } componentWillReceiveProps(nextProps){ @@ -42,16 +42,16 @@ class ScrollBar extends React.Component { calculateFractionalPosition(realContentSize, containerSize, contentPosition){ let relativeSize = realContentSize - containerSize; - + return 1 - ((relativeSize - contentPosition) / relativeSize); } calculateState(props){ - let fractionalPosition = this.calculateFractionalPosition(props.realSize, props.containerSize, props.position); + let fractionalPosition = this.calculateFractionalPosition(props.realSize, props.containerSize, props.position); let proportionalToPageScrollSize = props.containerSize * props.containerSize / props.realSize; let scrollSize = proportionalToPageScrollSize < props.minScrollSize ? props.minScrollSize : proportionalToPageScrollSize; - let scrollPosition = (props.containerSize - scrollSize) * fractionalPosition; + let scrollPosition = (props.containerSize - scrollSize) * fractionalPosition; return { scrollSize: scrollSize, position: Math.round(scrollPosition) @@ -65,44 +65,45 @@ class ScrollBar extends React.Component { let scrollStyles = this.createScrollStyles(); let springifiedScrollStyles = smoothScrolling ? modifyObjValues(scrollStyles, x => spring(x)) : scrollStyles; - let scrollbarClasses = `scrollbar-container ${isDragging ? 'active' : ''} ${isVoriziontal ? 'horizontal' : ''} ${isVertical ? 'vertical' : ''}`; + let scrollbarClasses = `scrollbar-container ${isDragging ? 'active' : ''} ${isVoriziontal ? 'horizontal' : ''} ${isVertical ? 'vertical' : ''}`; return ( - - { style => -
+ { style => +
{ this.scrollbarContainer = x}}> - -
this.scrollbarContainer = x } + > +
-
+ />
} ); } - + handleScrollBarContainerClick(e) { - e.preventDefault(); + e.preventDefault(); let multiplier = this.computeMultiplier(); let clientPosition = this.isVertical() ? e.clientY : e.clientX; let { top, left } = this.scrollbarContainer.getBoundingClientRect(); - let clientScrollPosition = this.isVertical() ? top : left; - + let clientScrollPosition = this.isVertical() ? top : left; + let position = clientPosition - clientScrollPosition; let proportionalToPageScrollSize = this.props.containerSize * this.props.containerSize / this.props.realSize; - + this.setState({isDragging: true, lastClientPosition: clientPosition }); this.props.onPositionChange((position - proportionalToPageScrollSize / 2) / multiplier); } handleMouseMoveForHorizontal(e){ let multiplier = this.computeMultiplier(); - + if(this.state.isDragging){ e.preventDefault(); let deltaX = this.state.lastClientPosition - e.clientX; @@ -113,7 +114,7 @@ class ScrollBar extends React.Component { handleMouseMoveForVertical(e){ let multiplier = this.computeMultiplier(); - + if(this.state.isDragging){ e.preventDefault(); let deltaY = this.state.lastClientPosition - e.clientY; @@ -149,11 +150,11 @@ class ScrollBar extends React.Component { }; } } - + computeMultiplier(){ return (this.props.containerSize) / this.props.realSize; } - + isVertical(){ return this.props.type === 'vertical'; } diff --git a/test/scrollArea.spec.js b/test/scrollArea.spec.js index 06baa23..a76591c 100644 --- a/test/scrollArea.spec.js +++ b/test/scrollArea.spec.js @@ -42,7 +42,7 @@ function setupComponentWithMockedSizes(props) { realWidth: 300, containerWidth: 100 }); - + return component; } @@ -51,14 +51,14 @@ function getRendererComponentInstance(renderer){ } describe('ScrollArea component', () => { - it('Should render children and both scrollbars', () => { + it('Should render children and both scrollbars', () => { let {scrollbars, content} = setupComponentWithMockedSizes(); expect(scrollbars.length).toBe(2); console.log(content); expect(content).toEqualJSX( -
{}} - style={undefined} +
{}} + style={{}} className="scrollarea-content " onTouchStart={() => {}} onTouchMove={() => {}} @@ -76,7 +76,7 @@ describe('ScrollArea component', () => { expect(scrollbars.length).toBe(2); expect(content).toEqualJSX(
{}} - style={undefined} + style={{}} tabIndex={100} className="scrollarea-content " onTouchStart={() => {}} @@ -87,29 +87,37 @@ describe('ScrollArea component', () => {

content

); }); - + it('Could render only vertical scrollbar', () => { let {scrollbars} = setupComponentWithMockedSizes({vertical: true, horizontal: false}); let scrollbar = scrollbars[0]; - + expect(scrollbars.length).toBe(1); expect(scrollbar.props.type).toBe('vertical'); }); - + it('Could render only horizontal scrollbar', () => { let {scrollbars} = setupComponentWithMockedSizes({vertical: false, horizontal: true}); let scrollbar = scrollbars[0]; - + expect(scrollbars.length).toBe(1); - expect(scrollbar.props.type).toBe('horizontal'); + expect(scrollbar.props.type).toBe('horizontal'); }); - + it('Should change content element class when contentClassName prop is used', () => { let {content} = setup({contentClassName: 'test-class'}); - + expect(content.props.className).toInclude('test-class'); }); - + + it('Should have proper element style when contentStyle prop is used', () => { + let {content, instance} = setupComponentWithMockedSizes({ + contentStyle: {test: 'contentStyle'}, + }); + + expect(content.props.style).toEqual({ test: 'contentStyle' }); + }); + it('Should have proper scrollbars styles', () => { let {content, scrollbars} = setupComponentWithMockedSizes({ vertical: true, @@ -119,21 +127,21 @@ describe('ScrollArea component', () => { horizontalScrollbarStyle: {test: 'horizontalScrollbarStyle'}, horizontalContainerStyle: {test: 'horizontalContainerStyle'} }); - + let verticalScrollbar = scrollbars.filter(component => component.props.type === 'vertical')[0]; let horizontalScrollbar = scrollbars.filter(component => component.props.type === 'horizontal')[0]; - + expect(verticalScrollbar.props.containerStyle).toEqual({test: 'verticalContainerStyle'}); expect(verticalScrollbar.props.scrollbarStyle).toEqual({test: 'verticalScrollbarStyle'}); expect(horizontalScrollbar.props.containerStyle).toEqual({test: 'horizontalContainerStyle'}); expect(horizontalScrollbar.props.scrollbarStyle).toEqual({test: 'horizontalScrollbarStyle'}); }); - + it('normalizeTopPosition() should returns proper value', () => { let {instance} = setup(); let {normalizeTopPosition} = instance; let sizes = {realHeight: 30, containerHeight: 20}; - + expect(normalizeTopPosition(0, sizes)).toBe(0); expect(normalizeTopPosition(5, sizes)).toBe(5); expect(normalizeTopPosition(10, sizes)).toBe(10); @@ -142,12 +150,12 @@ describe('ScrollArea component', () => { expect(normalizeTopPosition(-60, sizes)).toBe(0); expect(normalizeTopPosition(100, sizes)).toBe(10); }); - + it('normalizeLeftPosition() should returns proper value', () => { let {instance} = setup(); let {normalizeLeftPosition} = instance; let sizes = {realWidth: 30, containerWidth: 20}; - + expect(normalizeLeftPosition(0, sizes)).toBe(0); expect(normalizeLeftPosition(5, sizes)).toBe(5); expect(normalizeLeftPosition(10, sizes)).toBe(10); @@ -156,80 +164,80 @@ describe('ScrollArea component', () => { expect(normalizeLeftPosition(-60, sizes)).toBe(0); expect(normalizeLeftPosition(100, sizes)).toBe(10); }); - + it('handleWheel method work properly when scrolling down', () => { let {instance} = setupComponentWithMockedSizes(); - let e = {deltaY:20, deltaX: 0, preventDefault: () => {}, stopPropagation: () => {}}; + let e = {deltaY:20, deltaX: 0, preventDefault: () => {}, stopPropagation: () => {}}; instance.handleWheel(e); - + expect(instance.state.topPosition).toBe(20); }); - + it('handleWheel method work properly when scrolling up and actual topPosition is 0', () => { let {instance} = setupComponentWithMockedSizes(); - let e = {deltaY:-10, deltaX: 0, preventDefault: () => {}, stopPropagation: () => {}}; + let e = {deltaY:-10, deltaX: 0, preventDefault: () => {}, stopPropagation: () => {}}; instance.handleWheel(e); - + expect(instance.state.topPosition).toBe(0); }); - + it('handleWheel method work properly when scrolling down more then content height', () => { let {instance} = setupComponentWithMockedSizes(); - + for(let i = 0; i < 10; i++){ - let e = {deltaY:50, deltaX: 0, preventDefault: () => {}, stopPropagation: () => {}}; + let e = {deltaY:50, deltaX: 0, preventDefault: () => {}, stopPropagation: () => {}}; instance.handleWheel(e); - } - + } + expect(instance.state.topPosition).toBe(200); }); - + it('handleWheel method work properly when scrolling right', () => { let {instance} = setupComponentWithMockedSizes(); - let e = {deltaY:0, deltaX: 20, preventDefault: () => {}, stopPropagation: () => {}}; + let e = {deltaY:0, deltaX: 20, preventDefault: () => {}, stopPropagation: () => {}}; instance.handleWheel(e); expect(instance.state.leftPosition).toBe(20); }); - + it('handleWheel method work properly when scrolling left and actual leftPosition is 0', () => { let {instance} = setupComponentWithMockedSizes(); - let e = {deltaY:0, deltaX: -10, preventDefault: () => {}, stopPropagation: () => {}}; + let e = {deltaY:0, deltaX: -10, preventDefault: () => {}, stopPropagation: () => {}}; instance.handleWheel(e); - + expect(instance.state.leftPosition).toBe(0); }); - + it('handleWheel method work properly when scrolling right more then content height', () => { let {instance} = setupComponentWithMockedSizes(); - + for(let i = 0; i < 10; i++){ - let e = {deltaY:0, deltaX: 50, preventDefault: () => {}, stopPropagation: () => {}}; + let e = {deltaY:0, deltaX: 50, preventDefault: () => {}, stopPropagation: () => {}}; instance.handleWheel(e); - } - + } + expect(instance.state.leftPosition).toBe(200); }); - + it('handleWheel method should scroll down on scrollRight wheel event when revertWheelAxes prop is set to true', () => { let {instance} = setupComponentWithMockedSizes({ swapWheelAxes: true - }); - - let e = {deltaY:0, deltaX: 20, preventDefault: () => {}, stopPropagation: () => {}}; + }); + + let e = {deltaY:0, deltaX: 20, preventDefault: () => {}, stopPropagation: () => {}}; instance.handleWheel(e); - + expect(instance.state.topPosition).toBe(20); }); - + it('handleWheel method should scroll down on scrollRight wheel event when revertWheelAxes prop is set to true', () => { let {instance} = setupComponentWithMockedSizes({ swapWheelAxes: true - }); - - let e = {deltaY:20, deltaX: 0, preventDefault: () => {}, stopPropagation: () => {}}; + }); + + let e = {deltaY:20, deltaX: 0, preventDefault: () => {}, stopPropagation: () => {}}; instance.handleWheel(e); - + expect(instance.state.leftPosition).toBe(20); }); @@ -268,7 +276,7 @@ describe('ScrollArea component', () => { expect(instance.state.topPosition).toBe(0); }); - + it('scrollBottom() method should work when content is smaller then container', () => { let {instance} = setup({}, { realHeight: 30, @@ -276,12 +284,12 @@ describe('ScrollArea component', () => { realWidth: 30, containerWidth: 100 }); - - instance.scrollBottom(); - expect(instance.state.topPosition).toBe(0); + + instance.scrollBottom(); + expect(instance.state.topPosition).toBe(0); }); - - + + it('scrollBottom() method should scroll to bottom', () => { let {instance} = setup({}, { realHeight: 200, @@ -289,11 +297,11 @@ describe('ScrollArea component', () => { realWidth: 200, containerWidth: 100 }); - - instance.scrollBottom(); - expect(instance.state.topPosition).toBe(100); + + instance.scrollBottom(); + expect(instance.state.topPosition).toBe(100); }); - + it('scrollBottom() should be impossible when there is disabled vertical scroll', () => { let {instance} = setup({vertical: false}, { realHeight: 200, @@ -301,11 +309,11 @@ describe('ScrollArea component', () => { realWidth: 200, containerWidth: 100 }); - - instance.scrollBottom(); - expect(instance.state.topPosition).toBe(0); + + instance.scrollBottom(); + expect(instance.state.topPosition).toBe(0); }); - + it('scrollRight() method should work when content is smaller then container', () => { let {instance} = setup({}, { realHeight: 30, @@ -313,12 +321,12 @@ describe('ScrollArea component', () => { realWidth: 30, containerWidth: 100 }); - - instance.scrollRight(); - expect(instance.state.topPosition).toBe(0); + + instance.scrollRight(); + expect(instance.state.topPosition).toBe(0); }); - - + + it('scrollRight() method should scroll to right', () => { let {instance} = setup({}, { realHeight: 200, @@ -326,11 +334,11 @@ describe('ScrollArea component', () => { realWidth: 200, containerWidth: 100 }); - - instance.scrollRight(); - expect(instance.state.leftPosition).toBe(100); + + instance.scrollRight(); + expect(instance.state.leftPosition).toBe(100); }); - + it('scrollRight() should be impossible when there is disabled horizontal scroll', () => { let {instance} = setup({horizontal: false}, { realHeight: 200, @@ -338,34 +346,34 @@ describe('ScrollArea component', () => { realWidth: 200, containerWidth: 100 }); - - instance.scrollRight(); - expect(instance.state.topPosition).toBe(0); + + instance.scrollRight(); + expect(instance.state.topPosition).toBe(0); }); - + it('scrollLeft() method should scroll to left', () => { let {instance} = setup({}, { realHeight: 200, containerHeight: 100, realWidth: 200, containerWidth: 100 - }); + }); instance.scrollXTo(50); - - instance.scrollLeft(); - expect(instance.state.leftPosition).toBe(0); + + instance.scrollLeft(); + expect(instance.state.leftPosition).toBe(0); }); - + it('scrollTop() method should scroll to top', () => { let {instance} = setup({}, { realHeight: 200, containerHeight: 100, realWidth: 200, containerWidth: 100 - }); + }); instance.scrollYTo(50); - - instance.scrollTop(); - expect(instance.state.topPosition).toBe(0); + + instance.scrollTop(); + expect(instance.state.topPosition).toBe(0); }); }); diff --git a/test/scrollBar.spec.js b/test/scrollBar.spec.js index abdecef..f6fe2a4 100644 --- a/test/scrollBar.spec.js +++ b/test/scrollBar.spec.js @@ -37,12 +37,48 @@ describe('ScrollBar component', () => { expect(wrapper.props.className).toInclude('vertical'); }); + it('Vertical should have proper container styles', () => { + let {wrapper} = setupScrollbar({ + type: 'vertical', + containerStyle: {test: 'containerStyle'}, + }); + + expect(wrapper.props.style).toEqual({test: 'containerStyle'}); + }); + + it('Vertical should have proper scrollbar styles', () => { + let {content} = setupScrollbar({ + type: 'vertical', + scrollbarStyle: {test: 'scrollbarStyle'}, + }); + + expect(content.props.style).toEqual({test: 'scrollbarStyle'}); + }); + it('Horizontal should have proper class', () => { let {wrapper} = setupScrollbar({type: 'horizontal'}); expect(wrapper.props.className).toInclude('horizontal'); }); + it('Horizontal should have proper container styles', () => { + let {wrapper} = setupScrollbar({ + type: 'horizontal', + containerStyle: {test: 'containerStyle'}, + }); + + expect(wrapper.props.style).toEqual({test: 'containerStyle'}); + }); + + it('Horizontal should have proper scrollbar styles', () => { + let {content} = setupScrollbar({ + type: 'horizontal', + scrollbarStyle: {test: 'scrollbarStyle'}, + }); + + expect(content.props.style).toEqual({test: 'scrollbarStyle'}); + }); + it('ScrollBar should be in proper position', () => { let {instance} = setupScrollbar({ realSize: 400, @@ -254,4 +290,4 @@ describe('ScrollBar component', () => { expect(handlePositionChangeSpy.calls.length).toEqual(1); expect(handlePositionChangeSpy.calls[0].arguments).toEqual([200]); }); -}); \ No newline at end of file +});