Skip to content

Commit

Permalink
Merge pull request #168 from NGMarmaduke/carousel
Browse files Browse the repository at this point in the history
Update Carousel component to use nuka-carousel
  • Loading branch information
NGMarmaduke authored Feb 2, 2017
2 parents 3a860ab + 0fa42e6 commit a19f74b
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 176 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ build
# misc
.DS_Store
npm-debug.log
yarn-error.log
19 changes: 5 additions & 14 deletions components/Carousel/Carousel.css
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
.root {
white-space: nowrap;
-webkit-overflow-scrolling: touch;
width: 100%;
.wrapper {
overflow: hidden;
}

.root * {
box-sizing: border-box;
}

.animate {
transition: transform 500ms;
}

.slide {
display: inline-block;
.peaking {
padding-left: 8%;
padding-right: 8%;
}
84 changes: 60 additions & 24 deletions components/Carousel/Carousel.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,80 @@
import React, { PropTypes, Component } from 'react';
import React, { PropTypes, Component, Children } from 'react';
import cx from 'classnames';
import noop from '../../utils/noop';

import Inner from './CarouselInner';
import NukaCarousel from '@appearhere/nuka-carousel';

import css from './Carousel.css';

export default class Carousel extends Component {
static propTypes = {
children: PropTypes.oneOfType([
PropTypes.element,
PropTypes.array,
]).isRequired,
className: PropTypes.string,
lowestVisibleItemIndex: PropTypes.number,
items: PropTypes.array.isRequired,
itemsPerColumn: PropTypes.number,
onChange: PropTypes.func,
peaking: PropTypes.bool,
speed: PropTypes.number,

};

static defaultProps = {
lowestVisibleItemIndex: 0,
items: [],
itemsPerColumn: 1,
peaking: false,
speed: 300,
onChange: noop,
};

render() {
const { items, itemsPerColumn, lowestVisibleItemIndex } = this.props;
constructor(props) {
super(props);
this.state = { lowestVisibleItemIndex: props.lowestVisibleItemIndex };
}

const renderItems = items.filter(item => item);
componentWillReceiveProps(nextProps) {
const { lowestVisibleItemIndex } = this.state;
const nextIndex = nextProps.lowestVisibleItemIndex;

const renderPerColumn = itemsPerColumn < renderItems.length
? itemsPerColumn
: renderItems.length;
const slideWidth = 100 / renderPerColumn;
if (nextIndex !== lowestVisibleItemIndex) {
this.setState({ lowestVisibleItemIndex: nextIndex }, () => {
const childLength = Children.count(nextProps.children);
const willWrapToEnd = lowestVisibleItemIndex === 0 && nextIndex === childLength - 1;
const willWrapToStart = lowestVisibleItemIndex === childLength - 1 && nextIndex === 0;

const position = lowestVisibleItemIndex * (-1 * slideWidth);
const transform = `translate3d(${position}%, 0, 0)`;
if (willWrapToEnd) {
this.carousel.previousSlide();
} else if (willWrapToStart) {
this.carousel.nextSlide();
} else {
this.carousel.goToSlide(nextIndex);
}
});
}
}

handleChange = (lowestVisibleItemIndex) => {
const { onChange } = this.props;
this.setState({ lowestVisibleItemIndex }, () => { onChange(lowestVisibleItemIndex); });
}

render() {
const { peaking, children, className, ...rest } = this.props;
const frameOverflow = peaking ? 'visible' : 'hidden';

return (
<div>
<Inner
style={ {
transform,
} }
slideWidth={ slideWidth }
<div className={ cx(css.wrapper, className, peaking ? css.peaking : null) }>
<NukaCarousel
ref={ (c) => { this.carousel = c; } }
decorators={ [] }
frameOverflow={ frameOverflow }
peaking={ peaking }
afterSlide={ this.handleChange }
{ ...rest }
>
{ renderItems }
</Inner>
{ children }
</NukaCarousel>
</div>
);
}
}
}
111 changes: 49 additions & 62 deletions components/Carousel/Carousel.story.js
Original file line number Diff line number Diff line change
@@ -1,73 +1,60 @@
import React, { PropTypes, Component } from 'react';
import { storiesOf } from '@kadira/storybook';
import Carousel from './Carousel';
import React, { PropTypes } from 'react';
import { storiesOf, action } from '@kadira/storybook';

import getValidIndex from '../../utils/getValidIndex/getValidIndex';
import PictureCard from '../Cards/PictureCard/PictureCard';
import Carousel from './Carousel';
import ControlledCarousel from './ControlledCarousel';

const Slide = ({ i }) => (
<div
style={ {
width: '100%',
height: '300px',
} }
>
<div
const StorySlide = ({ number }) => (
<div key={ `slide-${number}` } style={ { paddingLeft: '2%', paddingRight: '2%' } }>
<PictureCard
src={ `http://placekitten.com/g/287/4${(number * 2) + 10}` }
style={ {
width: '100%',
height: '300px',
backgroundColor: 'red',
verticalAlign: 'middle',
textAlign: 'center',
fontSize: '5rem',
} }
center
href="#"
>
slide { i }
</div>
{ number }
</PictureCard>
</div>
);

Slide.propTypes = {
i: PropTypes.number,
};

const slides = [<Slide i={ 0 } />, <Slide i={ 1 } />, <Slide i={ 2 } />, <Slide i={ 3 } />, <Slide i={ 4 } />, <Slide i={ 5 } />, <Slide i={ 6 } />, <Slide i={ 7 } />];

class TestComponent extends Component {
constructor(props) {
super(props);

this.state = {
lowestVisibleItemIndex: 0,
itemsPerColumn: 1,
}
}

goToIndex = (i) => {
const { itemsPerColumn } = this.state;

if (i < 0 || i >= slides.length) return;
const lowestVisibleItemIndex = getValidIndex(i, slides.length, itemsPerColumn);
this.setState({ lowestVisibleItemIndex });
}

render() {
const { lowestVisibleItemIndex, itemsPerColumn } = this.state;

return (
<div style={ { width: '80vW', overflowX: 'visible', margin: '0 auto' } }>
<Carousel
lowestVisibleItemIndex={ lowestVisibleItemIndex }
items={ slides }
itemsPerColumn={ itemsPerColumn }
/>
<button onClick={this.goToIndex.bind(this, 0)}>Go to 0</button>
<button onClick={this.goToIndex.bind(this, 1)}>Go to 1</button>
<button onClick={this.goToIndex.bind(this, 2)}>Go to 2</button>
<button onClick={this.goToIndex.bind(this, 3)}>Go to 3</button>
<button onClick={this.goToIndex.bind(this, 4)}>Go to 4</button>
</div>
);
}
}
StorySlide.propTypes = { number: PropTypes.number };
const slides = [...Array(10).keys()].map(i => <StorySlide number={ i } key={ i } />);

storiesOf('Carousel', module)
.add('Default', () => (
<TestComponent />
));
<Carousel onChange={ action('Slide changed') }>
{ slides }
</Carousel>
));

storiesOf('Controlled Carousel', module)
.add('Default', () => (
<ControlledCarousel>
{ slides }
</ControlledCarousel>
))
.add('Muliple in view 💯', () => (
<ControlledCarousel slidesToShow={ 3 }>
{ slides }
</ControlledCarousel>
))
.add('Infinite ∞', () => (
<ControlledCarousel wrapAround>
{ slides }
</ControlledCarousel>
))
.add('Peaking 🐦', () => (
<ControlledCarousel peaking>
{ slides }
</ControlledCarousel>
))
.add('🐦 + ∞ + 💯', () => (
<ControlledCarousel peaking wrapAround slidesToShow={ 3 }>
{ slides }
</ControlledCarousel>
));
39 changes: 1 addition & 38 deletions components/Carousel/Carousel.test.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { createRenderer } from 'react-addons-test-utils';

import Carousel from './Carousel';

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Carousel />, div);
});

it('calculates the width of slides correctly ', () => {
const items = [<div key="lmao" />, <div key="lol" />];
const shallowRenderer = createRenderer();
let result = null;

shallowRenderer.render(
<Carousel
items={items}
itemsPerColumn={1}
/>
);

result = shallowRenderer.getRenderOutput();
expect(result.props.children.props.slideWidth).toBe(100);

shallowRenderer.render(
<Carousel
items={items}
itemsPerColumn={2}
/>
);

result = shallowRenderer.getRenderOutput();
expect(result.props.children.props.slideWidth).toBe(50);

shallowRenderer.render(
<Carousel
items={items}
itemsPerColumn={3}
/>
);

result = shallowRenderer.getRenderOutput();
expect(result.props.children.props.slideWidth).toBe(50);
ReactDOM.render(<Carousel><span>child</span></Carousel>, div);
});
37 changes: 0 additions & 37 deletions components/Carousel/CarouselInner.js

This file was deleted.

Loading

0 comments on commit a19f74b

Please sign in to comment.