├── .babelrc ├── .eslintrc ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .prettierrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __tests__ ├── SimpleSlider.test.js ├── TestComponents.js ├── afterChange.test.js ├── arrows.js ├── beforeChange.test.js ├── lazyLoad.test.js ├── observations.json ├── regression │ ├── fix-1813.test.js │ ├── fix-1874.test.js │ ├── fix-2315.test.js │ └── fix-2414.test.js ├── sliderStyles.test.js ├── testUtils.js └── utils │ └── filterSettings.test.js ├── docs ├── api.md ├── common.md ├── demos.js ├── docs.css ├── docs.js ├── img │ └── react-slick │ │ ├── abstract01.jpg │ │ ├── abstract02.jpg │ │ ├── abstract03.jpg │ │ └── abstract04.jpg ├── index.html ├── index.js ├── routes.js ├── scripts │ ├── generateExampleConfigs.js │ └── generateExamples.js ├── single-demo.js ├── slick-theme.css └── slick.css ├── examples ├── AdaptiveHeight.js ├── AppendDots.js ├── AsNavFor.js ├── AutoPlay.js ├── AutoPlayMethods.js ├── CenterMode.js ├── CustomArrows.js ├── CustomPaging.js ├── CustomSlides.js ├── DynamicSlides.js ├── Fade.js ├── FocusOnSelect.js ├── LazyLoad.js ├── MultipleItems.js ├── MultipleRows.js ├── PauseOnHover.js ├── PreviousNextMethods.js ├── Resizable.js ├── Responsive.js ├── Rtl.js ├── SimpleSlider.js ├── SlickGoTo.js ├── SlideChangeHooks.js ├── SwipeToSlide.js ├── UnevenSetsFinite.js ├── UnevenSetsInfinite.js ├── VariableWidth.js ├── VerticalMode.js ├── VerticalSwipeToSlide.js ├── __tests__ │ ├── CentreMode.test.js │ ├── Fade.js │ ├── FocusOnSelect.test.js │ ├── MultipleItems.test.js │ ├── SimpleSlider.test.js │ ├── SlickGoTo.test.js │ ├── UnevenSets.test.js │ └── sample.test.js └── config.js ├── gulpfile.js ├── jest.config.js ├── package.json ├── playwright-ct.config.js ├── playwright-tests ├── features │ └── responsive │ │ ├── responsive.spec.tsx │ │ └── responsive.story.tsx ├── regression │ └── fix-1930 │ │ ├── fix-1930.spec.tsx │ │ └── fix-1930.story.tsx └── sample │ ├── sample.spec.tsx │ └── sample.story.tsx ├── playwright ├── index.html └── index.jsx ├── src ├── arrows.js ├── default-props.js ├── dots.js ├── index.js ├── initial-state.js ├── inner-slider.js ├── slider.js ├── track.js └── utils │ └── innerSliderUtils.js ├── test-setup.js ├── test-utils.js ├── webpack.config.dist.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | "@babel/plugin-proposal-class-properties" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-extra-parens": 0, 4 | "react/jsx-uses-vars": 1, 5 | "strict": 0, 6 | "no-underscore-dangle": 0, 7 | "space-infix-ops": 0, 8 | "no-alert": 0, 9 | "react/prop-types": 0, 10 | "react/no-find-dom-node": 0, 11 | "react/display-name": 0, 12 | "no-console": 0, 13 | "no-prototype-builtins": 0 14 | }, 15 | "env": { 16 | "node": true, 17 | "browser": true, 18 | "es6": true, 19 | "jasmine": true 20 | }, 21 | "parser": "@babel/eslint-parser", 22 | "parserOptions": { 23 | "requireConfigFile": false 24 | }, 25 | "plugins": [ 26 | "react", 27 | "import" 28 | ], 29 | "extends": [ 30 | "eslint:recommended", 31 | "plugin:import/errors", 32 | "plugin:react/recommended" 33 | ] 34 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: akiran 2 | open_collective: react-slick 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 3 | 4 | ### Guidelines for posting a new issue 5 | 6 | * Please replicate your issue with this [CodeSandBox](https://codesandbox.io/s/ppwkk5l6xx) and provide a link to it along with the issue description 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | .sass-cache 4 | build 5 | demos/* 6 | demos1 7 | TODO.md 8 | npm-debug.log 9 | lib 10 | *.sublime-* 11 | .idea 12 | dist 13 | yarn.lock 14 | .vscode 15 | exampleslib 16 | examples/scripts/configs.json 17 | jquery.html 18 | docs/fonts/ 19 | docs/ajax-loader.gif 20 | package-lock.json 21 | .DS_Store 22 | /test-results/ 23 | /playwright-report/ 24 | /blob-report/ 25 | /playwright/.cache/ 26 | src-jsx 27 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | .sass-cache 3 | build 4 | demos 5 | demos1 6 | TODO.md 7 | test 8 | testlib 9 | bower.json 10 | gulpfile.js 11 | karma.conf.js 12 | LICENSE 13 | webpack.config.dist.js 14 | webpack.config.js 15 | ISSUE_TEMPLATE.md 16 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | trailingComma: none -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "stable" 5 | 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [Unreleased](https://github.com/akiran/react-slick/tree/HEAD) 4 | 5 | ## 0.22.0 6 | 7 | **Release Changes** 8 | 9 | - Internal Changes 10 | - converted InnerSlider from createReactClass object to ES6 class 11 | - removed all the mixins, created classMethods and pure utility functions instead 12 | - changed autoplay from setTimeout to setInterval 13 | - added images onload handlers to update dynamically 14 | - added autoplaying state for the betterment of autoplay and pause 15 | - removed usage of assign or Object.assign, using object spreading instead 16 | - implemented effects of touchMove props 17 | - fixed transition in opposite direction in case of continuous scrolling 18 | - added separate onclick event listener for images 19 | - added missing classes `regular` and `slider` 20 | - renamed events 21 | - edgeEvent => onEdge 22 | - init => onInit 23 | - reInit => onReInit 24 | - implemented `pauseOnDotsHover` property 25 | - implemented Progressive LazyLoad property, lazyLoad is now ondemand/progressive 26 | - implemented lazyloadError event 27 | - implemented useTransform property 28 | - implemented pauseOnFocus property 29 | - added resize observer to update on slider resize 30 | 31 | - Bug Fixes 32 | - dynamic track updates on image load 33 | - fixed slickPause and autoPlay issues (paused slider would resume autoplay sometime) 34 | - fixed trackStyle update on window resize 35 | - fixed NodeList forEach problem for chrome 51 or below 36 | - fixed bugs due to uncleared callback timers 37 | - fixed update issues on just slider resize 38 | 39 | 40 | ## 0.21.0 41 | 42 | **Release Changes** 43 | 44 | - Fixed issues 45 | - dataset undefined error in case of swipeToSlide but finite slides 46 | - slideWidth issue by transform scale 47 | - variableWidth + finite alignment problems 48 | - wrapper direction rtl issues 49 | - added onload update handler for images 50 | - fixed breaking of animation on setState 51 | 52 | - Mixins to Pure Functions 53 | - getWidth, getHeight 54 | - swipeDirection 55 | - initialize, update 56 | 57 | - Other Changes 58 | - removed sass, using pure CSS instead 59 | - enforced dir='ltr' in the slider, so dir='rtl' in wrapper doesn't break the slider 60 | - corrected up/down direction conventions 61 | - added more tests along with snapshots 62 | 63 | ## 0.20.0 64 | 65 | **Release Changes** 66 | 67 | - handled responsive breakpoint collision 68 | - renamed autoPlayTimer to autoplayTimer and removed it from state 69 | - changed es5 module.exports with es6 export default in src/index 70 | - implemented slider syncing with asNavFor prop 71 | - made all the slides untabbable 72 | - implemented getSlick method as in slick 73 | - implemented slickGetOption method 74 | - implemented lazyLoaded event 75 | - implemented reInit event 76 | - implemented onSwipe event and documented edgeEvent 77 | 78 | 79 | ## 0.19.0 80 | 81 | **Release Changes** 82 | 83 | Following are the changes to be mentioned: 84 | 85 | - fixed slideWidth calculation approximation 86 | - fixed unusual scrolls in focusOnSelect mode 87 | - added appendDots method for customization of dots 88 | - modified logic for handling odd/even cases where there were unusual scrolls in opposite direction 89 | - implemented unslick feature properly 90 | - fixed variableWidth issues like blank spaces at edges, improper alignment 91 | - handling focus loss in case of fade=true 92 | - responsive lazyloading bug fixed 93 | - increased verticalswiping resistance from 4 to 10 94 | 95 | 96 | ## 0.18.0 97 | 98 | **Major Changes:** 99 | 100 | - `centerPadding` prop now accepts % value as well 101 | - Fixed dots count in certain cases, where it was wrong 102 | - Fixed fade property mess-up on click 103 | - Fixed invisibility issue when fade & vertical are true 104 | - Modified logic for updating lazyLoadedList, earlier there were some whitespaces at ends, now they're gone 105 | - Fixed getTrackLeft issue for slideCount=1 106 | 107 | 108 | ## 0.17.1 109 | 110 | **Major Changes** 111 | 112 | * Enforced some settings in specific configurations like: 113 | - `slidesToScroll = 1` *when fade is true* 114 | - `slidesToScroll = 1` *when centerMode is true* 115 | - `slidesToShow = 1` *when fade is true* 116 | 117 | * Changed the number of clones (preclones and postclones), that fixed couple of issues like blank spaces after last slide and/or before first slide which occurred in several cases. 118 | 119 | 120 | **Minor Changes** 121 | 122 | - Rich amount of tests and test-utilities added 123 | - Additional documentation comments added 124 | - Refactored small snippets for betterment 125 | - Fixed several lazyload and centerMode bugs 126 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | ## Introduction 4 | 5 | First, thank you for considering contributing to react-slick! It's people like you that make the open source community such a great community! 😊 6 | 7 | We welcome any type of contribution, not only code. You can help with 8 | - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open) 9 | - **Marketing**: writing blog posts, howto's, printing stickers, ... 10 | - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... 11 | - **Code**: take a look at the [open issues](https://github.com/akiran/react-slick/issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. To get started you can also [sign up to triage react-slick issues on CodeTriage](https://www.codetriage.com/akiran/react-slick). 12 | - **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-slick). 13 | 14 | ## Your First Contribution 15 | 16 | Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 17 | 18 | ## Submitting code 19 | 20 | Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests. 21 | 22 | ## Code review process 23 | 24 | The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge. 25 | It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you? 26 | 27 | ## Financial contributions 28 | 29 | We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-slick). 30 | Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed. 31 | 32 | ## Questions 33 | 34 | If you have any questions, create an [issue](https://github.com/akiran/react-slick/issues) (protip: do a quick search first to see if someone else didn't ask the same question before!). 35 | You can also reach us at hello@react-slick.opencollective.com. 36 | 37 | ## Credits 38 | 39 | ### Contributors 40 | 41 | Thank you to all the people who have already contributed to react-slick! 42 | 43 | 44 | 45 | ### Backers 46 | 47 | Thank you to all our backers! [[Become a backer](https://opencollective.com/react-slick#backer)] 48 | 49 | 50 | 51 | 52 | ### Sponsors 53 | 54 | Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/react-slick#sponsor)) 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kiran Abburi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### react-slick 2 | 3 | [![Backers on Open Collective](https://opencollective.com/react-slick/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/react-slick/sponsors/badge.svg)](#sponsors) 4 | 5 | ##### Carousel component built with React. It is a react port of [slick carousel](http://kenwheeler.github.io/slick/) 6 | 7 | ## [Documentation](http://react-slick.neostack.com) 8 | 9 | ### Installation 10 | 11 | **npm** 12 | 13 | ```bash 14 | npm install react-slick --save 15 | ``` 16 | 17 | **yarn** 18 | 19 | ```bash 20 | yarn add react-slick 21 | ``` 22 | 23 | **Also install slick-carousel for css and font** 24 | 25 | ```bash 26 | npm install slick-carousel 27 | 28 | // Import css files 29 | import "slick-carousel/slick/slick.css"; 30 | import "slick-carousel/slick/slick-theme.css"; 31 | ``` 32 | 33 | or add cdn link in your html 34 | 35 | ```html 36 | 42 | 47 | ``` 48 | 49 | ### [PlayGround](https://stackblitz.com/edit/vitejs-vite-ownrun?file=src%2FImageSlider.jsx) 50 | 51 | ### Example 52 | 53 | ```js 54 | import React from "react"; 55 | import Slider from "react-slick"; 56 | 57 | export default function SimpleSlider() { 58 | var settings = { 59 | dots: true, 60 | infinite: true, 61 | speed: 500, 62 | slidesToShow: 1, 63 | slidesToScroll: 1 64 | }; 65 | return ( 66 | 67 |
68 |

1

69 |
70 |
71 |

2

72 |
73 |
74 |

3

75 |
76 |
77 |

4

78 |
79 |
80 |

5

81 |
82 |
83 |

6

84 |
85 |
86 | ); 87 | } 88 | ``` 89 | 90 | ### Props 91 | 92 | For all available props, go [here](https://react-slick.neostack.com/docs/api/). 93 | 94 | ### Methods 95 | 96 | For all available methods, go [here](https://react-slick.neostack.com/docs/api#methods) 97 | 98 | ### Development 99 | 100 | Want to run demos locally 101 | 102 | ```bash 103 | git clone https://github.com/akiran/react-slick 104 | cd react-slick 105 | npm install 106 | npm start 107 | open http://localhost:8080 108 | ``` 109 | 110 | ## Community 111 | 112 | Join our [discord channel](https://discord.gg/z7stRE4Cyb) to discuss react-slick bugs and ask for help 113 | 114 | ## Contributing 115 | 116 | Please see the [contributing guidelines](./CONTRIBUTING.md) 117 | -------------------------------------------------------------------------------- /__tests__/SimpleSlider.test.js: -------------------------------------------------------------------------------- 1 | // includes tests of 2 | // SimpleSlider, MultipleItems 3 | import { testSlider } from "./testUtils"; 4 | 5 | describe("SimpleSlider with combinations of possibilities", function() { 6 | // try around several possibilities 7 | let _noOfSlides = [2, 5, 12]; 8 | let _slidesToShow = [2, 5, 10]; 9 | let _slidesToScroll = [1, 2, 3, 10]; 10 | if (true) { 11 | // for switching real quick to lesser/easier tests for simplicity 12 | _noOfSlides = [5]; 13 | _slidesToShow = [2]; 14 | _slidesToScroll = [1, 2]; 15 | } 16 | 17 | for (let noOfSlides of _noOfSlides) { 18 | for (let slidesToShow of _slidesToShow) { 19 | for (let slidesToScroll of _slidesToScroll) { 20 | // following restrictions may not be 100% correct, and there may be more restrictions 21 | if (slidesToShow > noOfSlides || slidesToScroll > slidesToShow) { 22 | continue; 23 | } 24 | if (noOfSlides === slidesToShow) { 25 | // temporary, jquery slick disables arrows in this case, so the tests fail 26 | continue; 27 | } 28 | if (slidesToShow === slidesToScroll) { 29 | // temporary, active-class is not being assigned properly, so tests fail 30 | continue; 31 | } 32 | const settings1 = { 33 | infinite: true, 34 | speed: 0, 35 | noOfSlides, 36 | slidesToShow, 37 | slidesToScroll, 38 | useCSS: false 39 | }; 40 | test(`Test with settings => noOfSlides: ${noOfSlides}, slidesToShow: ${slidesToShow}, slidesToScroll: ${slidesToScroll}`, function() { 41 | testSlider(settings1); 42 | }); 43 | } 44 | } 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /__tests__/TestComponents.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "../src/index"; 3 | 4 | export function GenericSliderComponent({ slidesCount, settings }) { 5 | const slides = [...Array(slidesCount).keys()].map(item => ( 6 |
{item + 1}
7 | )); 8 | return {slides}; 9 | } 10 | 11 | test("fake test", () => { 12 | expect(1).toBe(1); 13 | }); 14 | -------------------------------------------------------------------------------- /__tests__/afterChange.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, fireEvent } from "@testing-library/react"; 3 | import Slider from "../src/index"; 4 | import { 5 | getActiveSlide, 6 | clickNext, 7 | clickPrevious, 8 | getCurrentSlide 9 | } from "../test-utils"; 10 | 11 | class SliderWithAfterChange extends React.Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | currentSlide: null 16 | }; 17 | this.afterChange = this.afterChange.bind(this); 18 | } 19 | 20 | afterChange(currentSlide) { 21 | console.log(currentSlide, "afterChange"); 22 | this.setState({ 23 | currentSlide 24 | }); 25 | } 26 | render() { 27 | return ( 28 | 29 |
slide1
30 |
slide2
31 |
slide3
32 |
slide4
33 |
34 | ); 35 | } 36 | } 37 | 38 | describe("After change Slider", function() { 39 | it("should render", function() { 40 | const { container } = render(); 41 | clickNext(container); 42 | setTimeout(() => { 43 | expect(getActiveSlide(container).textContent).toEqual("slide2"); 44 | }, 1000); 45 | clickNext(container); 46 | setTimeout(() => { 47 | expect(getActiveSlide(container).textContent).toEqual("slide3"); 48 | }, 1000); 49 | clickPrevious(container); 50 | setTimeout(() => { 51 | expect(getActiveSlide(container).textContent).toEqual("slide2"); 52 | }, 1000); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /__tests__/arrows.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Arrow component tests 3 | */ 4 | 5 | sinon.stub(console, "error"); 6 | 7 | import { render } from "@testing-library/react"; 8 | import React from "react"; 9 | import sinon from "sinon"; 10 | 11 | import { NextArrow, PrevArrow } from "../src/arrows"; 12 | 13 | function CustomArrow(props) { 14 | return ( 15 | 20 | ); 21 | } 22 | 23 | describe("Previous arrows", () => { 24 | it("should render arrow", () => { 25 | const { container } = render(); 26 | expect(Array.from(container.getElementsByTagName("button"))).toHaveLength( 27 | 1 28 | ); 29 | }); 30 | 31 | it("should not result in errors", () => { 32 | render(); 33 | 34 | expect(console.error.called).toBe(false); 35 | }); 36 | 37 | // it('should pass slide data to custom arrow', () => { 38 | // let elAttributes; 39 | // let arr = 40 | 41 | // const {container}= render(); 42 | 43 | // elAttributes =x=> container.querySelectorAll('.sample')[0].getAttribute(x); 44 | // expect(elAttributes('data-currentslide')).toBe('3'); 45 | // expect(elAttributes('data-slidecount')).toBe('5'); 46 | // }); 47 | }); 48 | 49 | describe("Next arrows", () => { 50 | it("should render arrow", () => { 51 | const { container } = render(); 52 | expect(Array.from(container.getElementsByTagName("button"))).toHaveLength( 53 | 1 54 | ); 55 | }); 56 | 57 | // it('should not result in errors', () => { 58 | // render(); 59 | 60 | // expect(console.error.called).toBe(false); 61 | // }); 62 | 63 | // it('should pass slide data to custom arrow', () => { 64 | // let elAttributes; 65 | // let arr = 66 | 67 | // const {container} = render(); 68 | 69 | // elAttributes =(x)=> container.querySelectorAll('.sample')[0].getAttribute(x); 70 | // expect(elAttributes('data-currentslide')).toBe('6'); 71 | // expect(elAttributes('data-slidecount')).toBe('9'); 72 | // }); 73 | }); 74 | -------------------------------------------------------------------------------- /__tests__/beforeChange.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import Slider from "../src/index"; 4 | import { 5 | getActiveSlide, 6 | clickNext, 7 | clickPrevious, 8 | getCurrentSlide 9 | } from "../test-utils"; 10 | 11 | class SliderWithBeforeChange extends React.Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = { 15 | currentSlide: null, 16 | nextSlide: null 17 | }; 18 | this.beforeChange = this.beforeChange.bind(this); 19 | } 20 | beforeChange(currentSlide, nextSlide) { 21 | this.setState({ 22 | currentSlide, 23 | nextSlide 24 | }); 25 | } 26 | render() { 27 | return ( 28 | 29 |
slide1
30 |
slide2
31 |
slide3
32 |
slide4
33 |
34 | ); 35 | } 36 | } 37 | 38 | describe("Slider", function() { 39 | it("should render", function() { 40 | const { container } = render(); 41 | clickNext(container); 42 | expect(getActiveSlide(container).textContent).toEqual("slide2"); 43 | clickNext(container); 44 | expect(getActiveSlide(container).textContent).toEqual("slide3"); 45 | clickPrevious(container); 46 | expect(getActiveSlide(container).textContent).toEqual("slide2"); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /__tests__/lazyLoad.test.js: -------------------------------------------------------------------------------- 1 | import { render } from "@testing-library/react"; 2 | import assign from "object-assign"; 3 | import { getRequiredLazySlides } from "../src/utils/innerSliderUtils"; 4 | import { 5 | createInnerSliderWrapper, 6 | clickNext, 7 | clickPrev, 8 | tryAllConfigs 9 | } from "./testUtils"; 10 | 11 | // const testSettings = settings => { 12 | // let {container} = createInnerSliderWrapper(settings); 13 | // for (let click = 0; click < settings.noOfSlides + 2; click++) { 14 | // let lazyLoadedList = slider.state().lazyLoadedList; 15 | // let expectedLazyLoadedList = getRequiredLazySlides( 16 | // assign({}, slider.props(), slider.state()) 17 | // ); 18 | // expectedLazyLoadedList.forEach(slide => { 19 | // expect(lazyLoadedList.indexOf(slide) >= 0).toEqual(true); 20 | // }); 21 | // clickNext(slider); 22 | // } 23 | 24 | // slider = createInnerSliderWrapper(settings); 25 | // for (let click = 0; click < settings.noOfSlides + 2; click++) { 26 | // let lazyLoadedList = slider.state().lazyLoadedList; 27 | // let expectedLazyLoadedList = getRequiredLazySlides( 28 | // assign({}, slider.props(), slider.state()) 29 | // ); 30 | // expectedLazyLoadedList.forEach(slide => { 31 | // expect(lazyLoadedList.indexOf(slide) >= 0).toEqual(true); 32 | // }); 33 | // clickPrev(slider); 34 | // } 35 | 36 | // slider = createInnerSliderWrapper(settings); 37 | // for (let click = 0; click < settings.noOfSlides + 2; click++) { 38 | // let lazyLoadedList = slider.state().lazyLoadedList; 39 | // lazyLoadedList.forEach(slideIndex => { 40 | // expect( 41 | // slider.find(`[data-index=${slideIndex}]`).props().children !== undefined 42 | // ).toBe(true); 43 | // }); 44 | // } 45 | // }; 46 | 47 | // describe("LazyLoad test", () => { 48 | // let settings = { 49 | // lazyLoad: true, 50 | // useCSS: false, 51 | // speed: 0, 52 | // noOfSlides: [7, 8], 53 | // initialSlide: [0, 5], 54 | // slidesToShow: [1, 3, 4], 55 | // slidesToScroll: [1, 3], 56 | // centerMode: [true, false] 57 | // }; 58 | // let settingsList = []; 59 | // tryAllConfigs(settings, settingsList); 60 | // // shuffle the list 61 | // settingsList.sort(() => 0.5 - Math.random()); 62 | // settingsList.forEach((settings, index) => { 63 | // if (Math.random() < 0.5) { 64 | // test(`Testing config no. ${index}`, () => testSettings(settings)); 65 | // } 66 | // }); 67 | // }); 68 | -------------------------------------------------------------------------------- /__tests__/observations.json: -------------------------------------------------------------------------------- 1 | { 2 | "jQueryTest": [ 3 | { 4 | "observation": "Clicks on arrows are not working properly", 5 | "possibleCause": "Animation effects are taking effects somehow", 6 | "solutions": [ 7 | { 8 | "description": "set useCSS property to false", 9 | "status": "did not work" 10 | }, 11 | { 12 | "description": "set speed property to 0", 13 | "status": "worked, now the clicks are working as of now" 14 | } 15 | ] 16 | }, 17 | { 18 | "observation": "arrows are disabled when slidesToShow are equal to noOfSlides", 19 | "status": "causes few tests to fail" 20 | }, 21 | { 22 | "observation": "tests are very slow", 23 | "possibleCause": "synchronous click event simulation and slow DOM api", 24 | "status": "not tried yet" 25 | } 26 | ], 27 | "reactTest": [ 28 | { 29 | "observation": "Clicks on arrows are not working properly", 30 | "possibleCause": "Animation effects are taking effects somehow", 31 | "solutions": [ 32 | { 33 | "description": "set useCSS property to false", 34 | "status": "worked, now the clicks are working as of now" 35 | } 36 | ] 37 | } 38 | ], 39 | "misc": [ 40 | { 41 | "observation": "In case of reverse scrolling, slick-active class is not being assigned properly.", 42 | "example": { 43 | "settings": { 44 | "noOfSlides": 5, 45 | "slidesToShow": 2, 46 | "slidesToScroll": 2 47 | }, 48 | "jqueryBehaviour": "after one prev click, current-slide is 5th and active-class is assigned to slide 4th and 5th while the same are displayed in frame", 49 | "reactBehaviour": "after one prev click, current-slide is 5th and active-class is assigned to slide 5th and 1st(cloned) while 4th and 5th are displayed in frame", 50 | "status": "several tests are failing due to this property" 51 | } 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /__tests__/regression/fix-1813.test.js: -------------------------------------------------------------------------------- 1 | //Test fix of #1813: In infinite mode, when slidesToShow equal to the length of slides, infinite functionality is not working. 2 | // Reversed the fix for #1813 to match the slick carousel functionality. When slides <= slidesToShow, unslick will be activated 3 | 4 | import React from "react"; 5 | import { render, fireEvent } from "@testing-library/react"; 6 | 7 | import { 8 | clickNext, 9 | clickPrevious, 10 | getActiveButton, 11 | getActiveSlidesCount, 12 | getActiveSlidesText, 13 | getButtons, 14 | getButtonsLength, 15 | getClonesCount, 16 | getCurrentSlide, 17 | getSlidesCount, 18 | hasArrows, 19 | hasDots 20 | } from "../../test-utils"; 21 | import { GenericSliderComponent } from "../TestComponents"; 22 | 23 | function MultipleItems() { 24 | const settings = { 25 | dots: true, 26 | infinite: true, 27 | speed: 500, 28 | slidesToShow: 9, 29 | slidesToScroll: 3 30 | }; 31 | return ; 32 | } 33 | 34 | describe("Multiple Items with slidesToShow = slides count in infinite mode", function() { 35 | it("should have 9 active slides", function() { 36 | const { container } = render(); 37 | expect(getActiveSlidesCount(container)).toEqual(9); 38 | }); 39 | it("should show first 9 slides", function() { 40 | const { container } = render(); 41 | //expect(getActiveButton(container)).toEqual(["1"]); 42 | expect(getActiveSlidesText(container)).toEqual([ 43 | "1", 44 | "2", 45 | "3", 46 | "4", 47 | "5", 48 | "6", 49 | "7", 50 | "8", 51 | "9" 52 | ]); 53 | }); 54 | it("shouldn't have any arrows", () => { 55 | const { container } = render(); 56 | expect(hasArrows(container)).toEqual(false); 57 | }); 58 | it("shouldn't have any dots", () => { 59 | const { container } = render(); 60 | expect(hasDots(container)).toEqual(false); 61 | }); 62 | // it("should have 0 dots", function() { 63 | // const { container } = render(); 64 | // expect(getButtonsLength(container)).toEqual(0); 65 | // }); 66 | // it("should show slides from 4 when next button is clicked", function() { 67 | // const { container } = render(); 68 | // clickNext(container); 69 | // expect(getActiveButton(container)).toEqual(["2"]); 70 | // expect(getActiveSlidesText(container)).toEqual([ 71 | // "4", 72 | // "5", 73 | // "6", 74 | // "7", 75 | // "8", 76 | // "9", 77 | // "1", 78 | // "2", 79 | // "3" 80 | // ]); 81 | // }); 82 | // it("should show slides from 7 when previous button is clicked", function() { 83 | // const { container } = render(); 84 | // clickPrevious(container); 85 | // expect(getActiveButton(container)).toEqual(["3"]); 86 | // expect(getActiveSlidesText(container)).toEqual([ 87 | // "7", 88 | // "8", 89 | // "9", 90 | // "1", 91 | // "2", 92 | // "3", 93 | // "4", 94 | // "5", 95 | // "6" 96 | // ]); 97 | // }); 98 | // it("should show slides first 9 slides when first dot is clicked", function() { 99 | // const { container } = render(); 100 | // fireEvent( 101 | // getButtons(container)[0], 102 | // new MouseEvent("click", { 103 | // bubbles: true, 104 | // cancelable: true 105 | // }) 106 | // ); 107 | // expect(getActiveButton(container)).toEqual(["1"]); 108 | // expect(getActiveSlidesText(container)).toEqual([ 109 | // "1", 110 | // "2", 111 | // "3", 112 | // "4", 113 | // "5", 114 | // "6", 115 | // "7", 116 | // "8", 117 | // "9" 118 | // ]); 119 | // }); 120 | // it("should show slides from 4 when middle dot is clicked", function() { 121 | // const { container } = render(); 122 | // fireEvent( 123 | // getButtons(container)[1], 124 | // new MouseEvent("click", { 125 | // bubbles: true, 126 | // cancelable: true 127 | // }) 128 | // ); 129 | // expect(getActiveButton(container)).toEqual(["2"]); 130 | // expect(getActiveSlidesText(container)).toEqual([ 131 | // "4", 132 | // "5", 133 | // "6", 134 | // "7", 135 | // "8", 136 | // "9", 137 | // "1", 138 | // "2", 139 | // "3" 140 | // ]); 141 | // }); 142 | // it("should show slides from 7 when last dot is clicked", function() { 143 | // const { container } = render(); 144 | // fireEvent( 145 | // getButtons(container)[2], 146 | // new MouseEvent("click", { 147 | // bubbles: true, 148 | // cancelable: true 149 | // }) 150 | // ); 151 | // expect(getActiveButton(container)).toEqual(["3"]); 152 | // expect(getActiveSlidesText(container)).toEqual([ 153 | // "7", 154 | // "8", 155 | // "9", 156 | // "1", 157 | // "2", 158 | // "3", 159 | // "4", 160 | // "5", 161 | // "6" 162 | // ]); 163 | // }); 164 | }); 165 | -------------------------------------------------------------------------------- /__tests__/regression/fix-1874.test.js: -------------------------------------------------------------------------------- 1 | // Test fix of#1874: "slick-current" is always on first slide despite initialSlide != 0 2 | 3 | import React from "react"; 4 | import { render } from "@testing-library/react"; 5 | 6 | import { getCurrentSlideContent, clickNext } from "../../test-utils"; 7 | import { GenericSliderComponent } from "../TestComponents"; 8 | 9 | describe("currentSlide test with different initialSlide values", () => { 10 | it("currentSlide is 0 when initialSlide is 0", function() { 11 | const { container } = render(); 12 | expect(getCurrentSlideContent(container)).toEqual("1"); 13 | clickNext(container); 14 | expect(getCurrentSlideContent(container)).toEqual("2"); 15 | }); 16 | 17 | it("currentSlide is 2 when initialSlide is 2", function() { 18 | const { container } = render( 19 | 20 | ); 21 | expect(getCurrentSlideContent(container)).toEqual("3"); 22 | clickNext(container); 23 | expect(getCurrentSlideContent(container)).toEqual("4"); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /__tests__/regression/fix-2315.test.js: -------------------------------------------------------------------------------- 1 | // Test fix of #2315: Slider crashing after a state change in parent component 2 | import React from "react"; 3 | import { render, fireEvent } from "@testing-library/react"; 4 | 5 | import { getCurrentSlideContent, getSlidesCount } from "../../test-utils"; 6 | import { GenericSliderComponent } from "../TestComponents"; 7 | 8 | function TestSlider() { 9 | const [count, setCount] = React.useState(); 10 | 11 | return ( 12 |
13 | 16 | 17 |
18 | ); 19 | } 20 | 21 | describe("State change in parent component of slider", () => { 22 | it("Slider shoud work afer clicking on Increment button", function() { 23 | const { container } = render(); 24 | fireEvent( 25 | container.getElementsByClassName("increment-button")[0], 26 | new MouseEvent("click", { 27 | bubbles: true, 28 | cancelable: true 29 | }) 30 | ); 31 | // Throws an error "Maximum update depth exceeded." if the bug exists 32 | expect(getCurrentSlideContent(container)).toEqual("1"); 33 | expect(getSlidesCount(container)).toEqual(8); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /__tests__/regression/fix-2414.test.js: -------------------------------------------------------------------------------- 1 | // Test fix of #2414: Extra clones in infinite mode when there is only one slide or unslick is true 2 | import React from "react"; 3 | import { render, fireEvent } from "@testing-library/react"; 4 | 5 | import { 6 | getCurrentSlideContent, 7 | getSlidesCount, 8 | getActiveSlidesCount, 9 | getClonesCount, 10 | hasArrows, 11 | hasDots 12 | } from "../../test-utils"; 13 | import { GenericSliderComponent } from "../TestComponents"; 14 | 15 | function SliderWithOneSlide() { 16 | const settings = { 17 | dots: true, 18 | infinite: true 19 | }; 20 | return ; 21 | } 22 | 23 | function SliderWithUnslick() { 24 | const settings = { 25 | dots: true, 26 | infinite: true, 27 | unslick: true 28 | }; 29 | return ; 30 | } 31 | 32 | describe("Slider with one slide", function() { 33 | it("should have 1 active slide", function() { 34 | const { container } = render(); 35 | expect(getActiveSlidesCount(container)).toEqual(1); 36 | }); 37 | it("should not have any clones", function() { 38 | const { container } = render(); 39 | expect(getClonesCount(container)).toEqual(0); 40 | }); 41 | it("should no have dots and arrows", function() { 42 | const { container } = render(); 43 | expect(hasArrows(container)).toEqual(false); 44 | expect(hasDots(container)).toEqual(false); 45 | }); 46 | }); 47 | 48 | describe("Slider with unslick=true", function() { 49 | it("should have one active slide", function() { 50 | const { container } = render(); 51 | expect(getActiveSlidesCount(container)).toEqual(1); 52 | }); 53 | it("should not have any clones", function() { 54 | const { container } = render(); 55 | expect(getClonesCount(container)).toEqual(0); 56 | }); 57 | it("should no have dots and arrows", function() { 58 | const { container } = render(); 59 | expect(hasArrows(container)).toEqual(false); 60 | expect(hasDots(container)).toEqual(false); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /__tests__/sliderStyles.test.js: -------------------------------------------------------------------------------- 1 | import assign from "object-assign"; 2 | import { getRequiredLazySlides } from "../src/utils/innerSliderUtils"; 3 | import { 4 | createInnerSliderWrapper, 5 | clickNext, 6 | clickPrev, 7 | tryAllConfigs, 8 | actualTrackLeft, 9 | testTrackLeft 10 | } from "./testUtils"; 11 | import { getTrackLeft } from "../src/utils/innerSliderUtils"; 12 | 13 | const testSettings = settings => { 14 | let slider = createInnerSliderWrapper(settings); 15 | for (let click = 0; click < settings.noOfSlides + 2; click++) { 16 | testTrackLeft(slider); 17 | clickNext(slider); 18 | } 19 | slider = createInnerSliderWrapper(settings); 20 | for (let click = 0; click < settings.noOfSlides + 2; click++) { 21 | testTrackLeft(slider); 22 | clickPrev(slider); 23 | } 24 | }; 25 | 26 | describe("Slider Styles Tests", () => { 27 | let settings = { 28 | useCSS: false, 29 | speed: 0, 30 | centerMode: [true, false], 31 | noOfSlides: [7, 8], 32 | initialSlide: [0, 5], 33 | slidesToShow: [1, 3, 4] 34 | }; 35 | let settingsList = []; 36 | tryAllConfigs(settings, settingsList); 37 | // shuffle the list 38 | settingsList.sort(() => 0.5 - Math.random()); 39 | settingsList.forEach((settings, index) => { 40 | // if (Math.random() < 0.5) { 41 | // test(`Testing config no. ${index}`, () => testSettings(settings)); 42 | // } 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /__tests__/testUtils.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import $ from "jquery"; 3 | import assign from "object-assign"; 4 | import { render } from "@testing-library/react"; 5 | import Slider from "../src/slider"; 6 | import { InnerSlider } from "../src/inner-slider"; 7 | import defaultProps from "../src/default-props"; 8 | import * as slickCarousel from "slick-carousel"; // defining slick in global environment 9 | import { getTrackLeft } from "../src/utils/innerSliderUtils"; 10 | import { 11 | getActiveSlides, 12 | getActiveSlidesCount, 13 | clickNext, 14 | clickPrevious 15 | } from "../test-utils"; 16 | 17 | // finds active slide number in the last transition in the forward direction 18 | export function activeSlideInLastTransition( 19 | noOfSlides, 20 | slidesToShow, 21 | slidesToScroll 22 | ) { 23 | let currentSlide = 0; 24 | while (currentSlide < noOfSlides) { 25 | currentSlide += slidesToScroll; 26 | } 27 | return currentSlide - slidesToScroll; 28 | } 29 | 30 | // create jsx-form children for react slider 31 | export function createReactSliderChildren(noOfSlides) { 32 | return Array.from(Array(noOfSlides).keys()).map(i => ( 33 |
34 |

{i + 1}

35 |
36 | )); 37 | } 38 | 39 | // create a react-slider with given noOfSlides and other props 40 | // variable widths are ignored for now for simplicity 41 | export function createReactSlider({ noOfSlides, ...props }) { 42 | return {createReactSliderChildren(noOfSlides)}; 43 | } 44 | 45 | // create a react inner-slider with given noOfSlides and other props 46 | // performs most operations like the ones when mounted inside Slider component 47 | export function createInnerSlider({ noOfSlides, ...settings }) { 48 | if (settings.centerMode) { 49 | settings.slidesToScroll = 1; // always scroll by one when centerMode is enabled 50 | } 51 | settings = assign({}, defaultProps, settings); 52 | const children = React.Children.toArray( 53 | createReactSliderChildren(noOfSlides) 54 | ); 55 | return {children}; 56 | } 57 | 58 | export function createInnerSliderWrapper(settings) { 59 | return render(createInnerSlider(settings)).container; 60 | } 61 | 62 | // creates a dom string, containing children of slick children 63 | export function createJQuerySliderChildren(noOfSlides) { 64 | let children = []; 65 | for (let i = 0; i < noOfSlides; i++) { 66 | children.push(`

${i + 1}

`); 67 | } 68 | return children.join(""); 69 | } 70 | 71 | // performs the very basic tests while clicking next or prev 72 | export function testSliderScroll({ direction, ...settings }) { 73 | const { noOfSlides, slidesToShow, slidesToScroll, initialSlide } = settings; 74 | // initialize react slider 75 | const { container } = render(createReactSlider(settings)); 76 | // initialize jquery slider 77 | document.body.innerHTML = ` 78 |
79 | ${createJQuerySliderChildren(noOfSlides)} 80 |
81 | `; 82 | $(".regular.slider").slick({ 83 | ...settings 84 | }); 85 | // console.log('setings:', settings) 86 | 87 | let expectedSlideIndex = initialSlide || 0; 88 | for (let click = 0; click < 2 * noOfSlides + 2; click++) { 89 | let activeslides = getActiveSlides(container); 90 | let $activeSlides = $(".regular.slider").find("div.slick-active"); 91 | expect(getActiveSlidesCount(container)).toEqual(slidesToShow || 1); 92 | expect($activeSlides.length).toEqual(slidesToShow || 1); 93 | let firstActiveSlide = activeslides[0]; 94 | let $firstActiveSlide = $activeSlides.first(); 95 | // console.log('classes', $firstActiveSlide.attr('data-slick-index')) 96 | // console.warn('currentSlide:', firstActiveSlide.prop('data-index'), 'expected slide', expectedSlideIndex) 97 | expect(parseInt(firstActiveSlide.getAttribute("data-index"))).toEqual( 98 | expectedSlideIndex % noOfSlides 99 | ); 100 | expect(parseInt($firstActiveSlide.attr("data-slick-index"))).toEqual( 101 | expectedSlideIndex % noOfSlides 102 | ); 103 | if (direction === "next") { 104 | // click the next arrow button 105 | clickNext(container); 106 | $("button.slick-next").click(); 107 | expectedSlideIndex += slidesToScroll || 1; 108 | if (expectedSlideIndex >= noOfSlides) { 109 | expectedSlideIndex = 0; 110 | } 111 | } else { 112 | // click on the prev arrow button 113 | clickPrevious(container); 114 | $("button.slick-prev").click(); 115 | expectedSlideIndex -= slidesToScroll || 1; 116 | if (expectedSlideIndex < 0) { 117 | expectedSlideIndex = activeSlideInLastTransition( 118 | noOfSlides, 119 | slidesToShow, 120 | slidesToScroll 121 | ); 122 | } 123 | } 124 | } 125 | } 126 | 127 | // function to run tests on a slider, 128 | // scrolls slider to the right by clicking right arrow several times 129 | // scrolls slider to the left by clicking left arrow several times 130 | export function testSlider(settings) { 131 | const settings1 = { direction: "next", ...settings }; 132 | const settings2 = { direction: "prev", ...settings }; 133 | testSliderScroll(settings1); 134 | testSliderScroll(settings2); 135 | } 136 | 137 | export const tryAllConfigs = (settings, settingsList) => { 138 | let leaf = true; 139 | for (let key of Object.keys(settings)) { 140 | if (Array.isArray(settings[key])) { 141 | leaf = false; 142 | for (let val of settings[key]) { 143 | tryAllConfigs({ ...settings, [key]: val }, settingsList); 144 | } 145 | } 146 | } 147 | if (leaf) { 148 | if ( 149 | settingsList 150 | .map(setting => JSON.stringify(setting)) 151 | .indexOf(JSON.stringify(settings)) < 0 152 | ) { 153 | settingsList.push(settings); 154 | } 155 | } 156 | }; 157 | 158 | export const actualTrackLeft = container => 159 | container 160 | .querySelector(".slick-track") 161 | .style.transform.match(/translate3d\((\d+)px/i)[1]; 162 | 163 | export const testTrackLeft = container => { 164 | let trackLeft = parseInt(actualTrackLeft(container)); 165 | let spec = assign({}, wrapper.props(), wrapper.state(), { 166 | slideIndex: wrapper.state().currentSlide, 167 | trackRef: null 168 | }); 169 | let expectedTrackLeft = getTrackLeft(spec); 170 | expect(trackLeft).toEqual(parseInt(expectedTrackLeft)); 171 | }; 172 | test("fake test", () => { 173 | expect(1).toBe(1); 174 | }); 175 | -------------------------------------------------------------------------------- /__tests__/utils/filterSettings.test.js: -------------------------------------------------------------------------------- 1 | import { filterSettings } from "../../src/utils/innerSliderUtils"; 2 | 3 | describe("filterSettings", () => { 4 | it("returns empty object if there are no valid settings", () => { 5 | expect(filterSettings({})).toEqual({}); 6 | expect(filterSettings({ age: 10 })).toEqual({}); 7 | }); 8 | it("return an object with valid settings and omits extra properties", () => { 9 | expect(filterSettings({ arrows: true, dots: true })).toEqual({ 10 | arrows: true, 11 | dots: true 12 | }); 13 | expect(filterSettings({ arrows: true, dots: true, age: 10 })).toEqual({ 14 | arrows: true, 15 | dots: true 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | ### Methods 2 | 3 | | Name | Arguments | Description | 4 | | ------------ | ------------------ | --------------------------- | 5 | | `slickPrev` | None | go to previous slide | 6 | | `slickNext` | None | go to next slide | 7 | | `slickGoTo` | index, dontAnimate | go to the given slide index | 8 | | `slickPause` | None | pause the autoplay | 9 | | `slickPlay` | None | start the autoplay | 10 | 11 | #### Followings are not going to be implemented 12 | 13 | | Name | type | Reason | 14 | | ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 15 | | `unslick` | method | same functionality can be achieved with `unslick` prop | 16 | | `slickSetOption` | method | same functionality can be achieved via props and managing state for them in wrapper | 17 | | `slickFilter` | method | same functionality can be achieved as with dynamic slides, look at dynamic slides [example](https://github.com/akiran/react-slick/blob/master/examples/DynamicSlides.js) | 18 | | `slickUnfilter` | method | same functionality can be achieved as with dynamic slides, look at dynamic slides [example](https://github.com/akiran/react-slick/blob/master/examples/DynamicSlides.js) | 19 | | `slickAdd` | method | same functionality can be achieved as with dynamic slides, look at dynamic slides [example](https://github.com/akiran/react-slick/blob/master/examples/DynamicSlides.js) | 20 | | `slickRemove` | method | same functionality can be achieved as with dynamic slides, look at dynamic slides [example](https://github.com/akiran/react-slick/blob/master/examples/DynamicSlides.js) | 21 | | `slickCurrentSlide` | method | same functionality can be achieved with `beforeChange hook` | 22 | | `slickGetOption` | method | manage wrapper state for desired options | 23 | | `getSlick` | method | a simple ref will do | 24 | -------------------------------------------------------------------------------- /docs/common.md: -------------------------------------------------------------------------------- 1 | #### `responsive` property 2 | 3 | Array of objects in the form of `{ breakpoint: int, settings: { ... } }` The breakpoint _int_ is the `maxWidth` so the settings will be applied when resolution is below this value. Breakpoints in the array should be ordered from smallest to greatest. Use 'unslick' in place of the settings object to disable rendering the carousel at that breakpoint. Example: `[ { breakpoint: 768, settings: { slidesToShow: 3 } }, { breakpoint: 1024, settings: { slidesToShow: 5 } }, { breakpoint: 100000, settings: 'unslick' } ]` 4 | 5 | ### Custom next/prev arrows 6 | 7 | To customize the next/prev arrow elements, simply create new React components and set them 8 | as the values of nextArrow and prevArrow. 9 | 10 | ```js 11 | class LeftNavButton extends React.Component { 12 | render() { 13 | return ; 14 | } 15 | } 16 | ``` 17 | 18 | Important: be sure that you pass your component's props to your clickable element 19 | like the example above. If you don't, your custom component won't trigger the click handler. 20 | 21 | You can also set `onClick={this.props.onClick}` if you only want to set the click handler. 22 | 23 | ### Flexbox support 24 | 25 | If you have flex property on container div of slider, add below css 26 | 27 | ```css 28 | * { 29 | min-height: 0; 30 | min-width: 0; 31 | } 32 | ``` 33 | 34 | ### Test Setup 35 | 36 | If you try to run tests with jest in a project that uses react-slick, you may run into this error 37 | 38 | ``` 39 | matchMedia not present, legacy browsers require a polyfill 40 | ``` 41 | 42 | To fix this issue add below snippet in test-setup.js 43 | 44 | ```js 45 | window.matchMedia = 46 | window.matchMedia || 47 | function() { 48 | return { 49 | matches: false, 50 | addListener: function() {}, 51 | removeListener: function() {} 52 | }; 53 | }; 54 | ``` 55 | 56 | and add below jest config in package.json 57 | 58 | ```json 59 | "jest": { 60 | "setupFiles": ["test-setup.js"] 61 | } 62 | ``` 63 | 64 | ### Polyfills for old IE support 65 | 66 | `matchMedia` support from [media-match](https://github.com/weblinc/media-match) 67 | -------------------------------------------------------------------------------- /docs/demos.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import Slider from "../src/slider"; 5 | 6 | import SimpleSlider from "../examples/SimpleSlider"; 7 | import SlideChangeHooks from "../examples/SlideChangeHooks"; 8 | import MultipleItems from "../examples/MultipleItems"; 9 | import MultipleRows from "../examples/MultipleRows"; 10 | import Responsive from "../examples/Responsive"; 11 | import Resizable from "../examples/Resizable"; 12 | import UnevenSetsInfinite from "../examples/UnevenSetsInfinite"; 13 | import UnevenSetsFinite from "../examples/UnevenSetsFinite"; 14 | import CenterMode from "../examples/CenterMode"; 15 | import FocusOnSelect from "../examples/FocusOnSelect"; 16 | import AutoPlay from "../examples/AutoPlay"; 17 | import AutoPlayMethods from "../examples/AutoPlayMethods"; 18 | import PauseOnHover from "../examples/PauseOnHover"; 19 | import Rtl from "../examples/Rtl"; 20 | import VariableWidth from "../examples/VariableWidth"; 21 | import AdaptiveHeight from "../examples/AdaptiveHeight"; 22 | import LazyLoad from "../examples/LazyLoad"; 23 | import Fade from "../examples/Fade"; 24 | import SlickGoTo from "../examples/SlickGoTo"; 25 | import CustomArrows from "../examples/CustomArrows"; 26 | import PreviousNextMethods from "../examples/PreviousNextMethods"; 27 | import DynamicSlides from "../examples/DynamicSlides"; 28 | import VerticalMode from "../examples/VerticalMode"; 29 | import SwipeToSlide from "../examples/SwipeToSlide"; 30 | import VerticalSwipeToSlide from "../examples/VerticalSwipeToSlide"; 31 | import CustomPaging from "../examples/CustomPaging"; 32 | import CustomSlides from "../examples/CustomSlides"; 33 | import AsNavFor from "../examples/AsNavFor"; 34 | import AppendDots from "../examples/AppendDots"; 35 | 36 | export default class App extends React.Component { 37 | render() { 38 | return ( 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/docs.css: -------------------------------------------------------------------------------- 1 | 2 | h3 { 3 | background: #00558B; 4 | color: #fff; 5 | font-size: 36px; 6 | line-height: 100px; 7 | margin: 10px; 8 | padding: 2%; 9 | position: relative; 10 | text-align: center; 11 | } 12 | .variable-width .slick-slide p { 13 | background: #00558B; 14 | height: 100px; 15 | color: #fff; 16 | margin: 5px; 17 | line-height: 100px; 18 | text-align: center; 19 | } 20 | .center .slick-center h3 { 21 | color: #e67e22; 22 | opacity: 1; 23 | transform: scale(1.08); 24 | } 25 | .center h3{ 26 | opacity: 0.8; 27 | transition: all 300ms ease; 28 | } 29 | .content { 30 | padding: 20px; 31 | margin: auto; 32 | } 33 | @media (min-width: 701px) { 34 | .content { 35 | width: 80%; 36 | } 37 | } 38 | @media (max-width: 700px) { 39 | .content { 40 | width: 70%; 41 | } 42 | } 43 | .slick-slide .image { 44 | padding: 10px; 45 | } 46 | .slick-slide img { 47 | border: 5px solid #FFF; 48 | display: block; 49 | margin: auto; 50 | max-width: 80%; 51 | } 52 | .slick-slide img.slick-loading { 53 | border: 0 54 | } 55 | .slick-slider { 56 | margin: 30px auto 50px; 57 | } 58 | .slick-dots { 59 | margin-left: 0; 60 | } 61 | .slick-thumb { 62 | bottom: -45px; 63 | } 64 | .slick-thumb li { 65 | width: 60px; 66 | height: 45px; 67 | } 68 | .slick-thumb li img { 69 | width: 100%; 70 | height: 100%; 71 | filter: grayscale(100%); 72 | } 73 | .slick-thumb li.slick-active img{ 74 | filter: grayscale(0%); 75 | } 76 | @media (max-width: 768px) { 77 | h3 { 78 | font-size:24px; 79 | } 80 | .center { 81 | margin-left: -40px; 82 | margin-right: -40px; 83 | } 84 | .center .slick-center h3 { 85 | color: #e67e22; 86 | opacity: 1; 87 | transform: scale(1); 88 | } 89 | .center h3 { 90 | opacity: 0.8; 91 | transform: scale(0.95); 92 | transition: all 300ms ease; 93 | } 94 | } 95 | .slick-vertical .slick-slide { 96 | height: 180px; 97 | } 98 | .slick-arrow { 99 | background-color: grey; 100 | } 101 | .slick-arrow:hover { 102 | background-color: grey; 103 | } 104 | .slick-arrow:focus { 105 | background-color: grey; 106 | } 107 | .button { 108 | background-color: #00558B; 109 | padding: 10px 20px; 110 | margin: 0px 20px; 111 | border: none; 112 | color: white; 113 | font-size: 20px; 114 | border-radius: 5px; 115 | min-height: 45px 116 | } 117 | -------------------------------------------------------------------------------- /docs/docs.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import Demos from "./demos"; 5 | 6 | export default class Docs extends React.Component { 7 | render() { 8 | return ( 9 |
10 |
11 | React Slick 12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/img/react-slick/abstract01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akiran/react-slick/a5a3cbe69abf4254713989f480e43480d3efdfe4/docs/img/react-slick/abstract01.jpg -------------------------------------------------------------------------------- /docs/img/react-slick/abstract02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akiran/react-slick/a5a3cbe69abf4254713989f480e43480d3efdfe4/docs/img/react-slick/abstract02.jpg -------------------------------------------------------------------------------- /docs/img/react-slick/abstract03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akiran/react-slick/a5a3cbe69abf4254713989f480e43480d3efdfe4/docs/img/react-slick/abstract03.jpg -------------------------------------------------------------------------------- /docs/img/react-slick/abstract04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akiran/react-slick/a5a3cbe69abf4254713989f480e43480d3efdfe4/docs/img/react-slick/abstract04.jpg -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 21 | 22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import { createRoot } from "react-dom/client"; 5 | import Docs from "./docs"; 6 | 7 | const container = document.getElementById("rapp"); 8 | const root = createRoot(container); 9 | 10 | React.initializeTouchEvents && React.initializeTouchEvents(true); 11 | root.render( 12 | 13 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /docs/routes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var React = require("react"); 4 | var Router = require("react-router"); 5 | var Route = Router.Route; 6 | var Docs = require("./docs"); 7 | 8 | var path = 9 | process.env.NODE_ENV === "dev_docs" ? "/" : "/opensource/react-slick"; 10 | var routes = ; 11 | 12 | module.exports = routes; 13 | -------------------------------------------------------------------------------- /docs/scripts/generateExampleConfigs.js: -------------------------------------------------------------------------------- 1 | const React = require("react"); 2 | const fs = require("fs"); 3 | const babel = require("babel-core"); 4 | const ReactDOMServer = require("react-dom/server"); 5 | 6 | let configsObject; 7 | if (fs.existsSync("./configs.json")) { 8 | configsObject = require("./configs.json"); 9 | } else { 10 | configsObject = {}; 11 | } 12 | 13 | const fetchExampleString = exampleName => { 14 | const exampleString = fs.readFileSync(`examples/${exampleName}.js`, "utf-8"); 15 | return exampleString; 16 | }; 17 | 18 | const extractConfig = exampleString => { 19 | const pattern = /(var|const)\s+settings\s*=\s*(\{(.|\n)+?\n\s*\};)/; 20 | let extraction = exampleString.match(pattern); 21 | if (extraction) extraction = extraction[2]; 22 | else return null; 23 | const propPattern = /(\w+)\:((?:.|\n)+?)(?=(,\n)|(\n\s*};))/g; 24 | let match; 25 | let matchObject = {}; 26 | do { 27 | match = propPattern.exec(extraction); 28 | if (!match) break; 29 | if (!matchObject[match[1]]) { 30 | matchObject[match[1]] = match[2].trim(); 31 | } 32 | } while (match); 33 | return matchObject; 34 | }; 35 | 36 | const extractChildren = exampleString => { 37 | const pattern = /\((.|\n)*?)\<\/Slider\>/; 38 | return exampleString.match(pattern)[1]; 39 | }; 40 | 41 | const transpile = exampleString => 42 | babel.transform(exampleString, { 43 | plugins: [ 44 | "transform-react-jsx", 45 | "babel-plugin-transform-object-rest-spread", 46 | "babel-plugin-transform-class-properties", 47 | "babel-plugin-transform-es2015-arrow-functions" 48 | ] 49 | }).code; 50 | 51 | const fetchExampleConfigs = (fileName, index) => { 52 | const exampleName = fileName.substring(0, fileName.length - 3); 53 | const exampleString = fetchExampleString(exampleName); 54 | const transformedString = transpile(exampleString); 55 | let childrenString = extractChildren(exampleString.replace(/\=\>/g, "$$$")); // jsx type string 56 | try { 57 | // react string without jsx 58 | childrenString = eval( 59 | transpile( 60 | `
` + childrenString + "
" 61 | ).replace(/baseUrl/g, "'./img/react-slick'") 62 | ); 63 | console.log("success"); 64 | } catch (error) { 65 | childrenString = ""; 66 | console.error("children error:", fileName); 67 | } 68 | childrenString = ReactDOMServer.renderToString(childrenString); // pure html string 69 | let config = extractConfig(transformedString); 70 | if (config) { 71 | configsObject[exampleName] = { 72 | props: config, 73 | children: childrenString 74 | }; 75 | } else { 76 | console.log("config error:", fileName); 77 | } 78 | }; 79 | 80 | const exampleFiles = fs 81 | .readdirSync("examples") 82 | .filter(file => file.endsWith(".js") && file[0] === file[0].toUpperCase()) 83 | .forEach((fileName, index) => fetchExampleConfigs(fileName, index)); 84 | fs.writeFileSync( 85 | "examples/scripts/configs.json", 86 | JSON.stringify(configsObject, null, 4) 87 | ); 88 | -------------------------------------------------------------------------------- /docs/scripts/generateExamples.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const exampleConfigs = require("./configs.json"); 3 | const exec = require("child_process").exec; 4 | 5 | var procCode = exec( 6 | "cp -r node_modules/slick-carousel/slick/fonts node_modules/slick-carousel/slick/ajax-loader.gif docs/" 7 | ); 8 | 9 | const toString = obj => { 10 | let ret = "{\n"; 11 | Object.keys(obj).forEach(key => { 12 | if ( 13 | obj[key].match("function") || 14 | obj[key].match("React.createElement" || obj[key].match("\n")) 15 | ) { 16 | return; 17 | } 18 | if ( 19 | key.match("style") || 20 | key.match("src") || 21 | key.match("border") || 22 | key.match("settings") || 23 | key.match("responsive") 24 | ) 25 | return; 26 | ret += "\t" + key + ": " + obj[key] + ",\n"; 27 | }); 28 | ret += "}\n"; 29 | return ret; 30 | }; 31 | 32 | let bodyHTML = ""; 33 | let bodyScript = ""; 34 | Object.keys(exampleConfigs).forEach(key => { 35 | const props = exampleConfigs[key]["props"]; 36 | const children = exampleConfigs[key]["children"]; 37 | if (!props || !children) return; 38 | bodyHTML += ` 39 |
40 |

${key}

41 | ${children} 42 |
43 | `; 44 | bodyScript += ` 45 | $('[name="${key}"').slick(${toString(props)}) 46 | `; 47 | }); 48 | 49 | let HTMLString = ` 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 70 | 71 | 72 | 73 |
74 | ${bodyHTML} 75 |
76 | 81 | 82 | 83 | `; 84 | 85 | fs.writeFileSync("docs/jquery.html", HTMLString); 86 | -------------------------------------------------------------------------------- /docs/single-demo.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import { createRoot } from "react-dom/client"; 5 | import Slider from "../src/slider"; 6 | import MultipleItems from "../examples/MultipleItems"; 7 | function SimpleSlider() { 8 | const settings = { 9 | dots: true, 10 | infinite: true, 11 | speed: 500, 12 | slidesToShow: 1, 13 | slidesToScroll: 1 14 | }; 15 | return ( 16 |
17 |

Single Item

18 | 19 |
20 |

1

21 |
22 |
23 |

2

24 |
25 |
26 |

3

27 |
28 |
29 |

4

30 |
31 |
32 |

5

33 |
34 |
35 |

6

36 |
37 |
38 |
39 | ); 40 | } 41 | 42 | function App() { 43 | return ( 44 |
45 | {/* */} 46 | 47 |
48 | ); 49 | } 50 | 51 | const container = document.getElementById("rapp"); 52 | const root = createRoot(container); 53 | 54 | React.initializeTouchEvents && React.initializeTouchEvents(true); 55 | root.render( 56 | 57 | 58 | 59 | ); 60 | -------------------------------------------------------------------------------- /docs/slick-theme.css: -------------------------------------------------------------------------------- 1 | @charset 'UTF-8'; 2 | /* Slider */ 3 | .slick-loading .slick-list 4 | { 5 | background: #fff url('./ajax-loader.gif') center center no-repeat; 6 | } 7 | 8 | /* Icons */ 9 | @font-face 10 | { 11 | font-family: 'slick'; 12 | font-weight: normal; 13 | font-style: normal; 14 | 15 | src: url('./fonts/slick.eot'); 16 | src: url('./fonts/slick.eot?#iefix') format('embedded-opentype'), url('./fonts/slick.woff') format('woff'), url('./fonts/slick.ttf') format('truetype'), url('./fonts/slick.svg#slick') format('svg'); 17 | } 18 | /* Arrows */ 19 | .slick-prev, 20 | .slick-next 21 | { 22 | font-size: 0; 23 | line-height: 0; 24 | 25 | position: absolute; 26 | top: 50%; 27 | 28 | display: block; 29 | 30 | width: 20px; 31 | height: 20px; 32 | padding: 0; 33 | -webkit-transform: translate(0, -50%); 34 | -ms-transform: translate(0, -50%); 35 | transform: translate(0, -50%); 36 | 37 | cursor: pointer; 38 | 39 | color: transparent; 40 | border: none; 41 | outline: none; 42 | background: transparent; 43 | } 44 | .slick-prev:hover, 45 | .slick-prev:focus, 46 | .slick-next:hover, 47 | .slick-next:focus 48 | { 49 | color: transparent; 50 | outline: none; 51 | background: transparent; 52 | } 53 | .slick-prev:hover:before, 54 | .slick-prev:focus:before, 55 | .slick-next:hover:before, 56 | .slick-next:focus:before 57 | { 58 | opacity: 1; 59 | } 60 | .slick-prev.slick-disabled:before, 61 | .slick-next.slick-disabled:before 62 | { 63 | opacity: .25; 64 | } 65 | 66 | .slick-prev:before, 67 | .slick-next:before 68 | { 69 | font-family: 'slick'; 70 | font-size: 20px; 71 | line-height: 1; 72 | 73 | opacity: .75; 74 | color: white; 75 | 76 | -webkit-font-smoothing: antialiased; 77 | -moz-osx-font-smoothing: grayscale; 78 | } 79 | 80 | .slick-prev 81 | { 82 | left: -25px; 83 | } 84 | [dir='rtl'] .slick-prev 85 | { 86 | right: -25px; 87 | left: auto; 88 | } 89 | .slick-prev:before 90 | { 91 | content: '←'; 92 | } 93 | [dir='rtl'] .slick-prev:before 94 | { 95 | content: '→'; 96 | } 97 | 98 | .slick-next 99 | { 100 | right: -25px; 101 | } 102 | [dir='rtl'] .slick-next 103 | { 104 | right: auto; 105 | left: -25px; 106 | } 107 | .slick-next:before 108 | { 109 | content: '→'; 110 | } 111 | [dir='rtl'] .slick-next:before 112 | { 113 | content: '←'; 114 | } 115 | 116 | /* Dots */ 117 | .slick-dotted.slick-slider 118 | { 119 | margin-bottom: 30px; 120 | } 121 | 122 | .slick-dots 123 | { 124 | position: absolute; 125 | bottom: -25px; 126 | 127 | display: block; 128 | 129 | width: 100%; 130 | padding: 0; 131 | margin: 0; 132 | 133 | list-style: none; 134 | 135 | text-align: center; 136 | } 137 | .slick-dots li 138 | { 139 | position: relative; 140 | 141 | display: inline-block; 142 | 143 | width: 20px; 144 | height: 20px; 145 | margin: 0 5px; 146 | padding: 0; 147 | 148 | cursor: pointer; 149 | } 150 | .slick-dots li button 151 | { 152 | font-size: 0; 153 | line-height: 0; 154 | 155 | display: block; 156 | 157 | width: 20px; 158 | height: 20px; 159 | padding: 5px; 160 | 161 | cursor: pointer; 162 | 163 | color: transparent; 164 | border: 0; 165 | outline: none; 166 | background: transparent; 167 | } 168 | .slick-dots li button:hover, 169 | .slick-dots li button:focus 170 | { 171 | outline: none; 172 | } 173 | .slick-dots li button:hover:before, 174 | .slick-dots li button:focus:before 175 | { 176 | opacity: 1; 177 | } 178 | .slick-dots li button:before 179 | { 180 | font-family: 'slick'; 181 | font-size: 6px; 182 | line-height: 20px; 183 | 184 | position: absolute; 185 | top: 0; 186 | left: 0; 187 | 188 | width: 20px; 189 | height: 20px; 190 | 191 | content: '•'; 192 | text-align: center; 193 | 194 | opacity: .25; 195 | color: black; 196 | 197 | -webkit-font-smoothing: antialiased; 198 | -moz-osx-font-smoothing: grayscale; 199 | } 200 | .slick-dots li.slick-active button:before 201 | { 202 | opacity: .75; 203 | color: black; 204 | } 205 | -------------------------------------------------------------------------------- /docs/slick.css: -------------------------------------------------------------------------------- 1 | /* Slider */ 2 | .slick-slider 3 | { 4 | position: relative; 5 | 6 | display: block; 7 | box-sizing: border-box; 8 | 9 | -webkit-user-select: none; 10 | -moz-user-select: none; 11 | -ms-user-select: none; 12 | user-select: none; 13 | 14 | -webkit-touch-callout: none; 15 | -khtml-user-select: none; 16 | -ms-touch-action: pan-y; 17 | touch-action: pan-y; 18 | -webkit-tap-highlight-color: transparent; 19 | } 20 | 21 | .slick-list 22 | { 23 | position: relative; 24 | 25 | display: block; 26 | overflow: hidden; 27 | 28 | margin: 0; 29 | padding: 0; 30 | } 31 | .slick-list:focus 32 | { 33 | outline: none; 34 | } 35 | .slick-list.dragging 36 | { 37 | cursor: pointer; 38 | cursor: hand; 39 | } 40 | 41 | .slick-slider .slick-track, 42 | .slick-slider .slick-list 43 | { 44 | -webkit-transform: translate3d(0, 0, 0); 45 | -moz-transform: translate3d(0, 0, 0); 46 | -ms-transform: translate3d(0, 0, 0); 47 | -o-transform: translate3d(0, 0, 0); 48 | transform: translate3d(0, 0, 0); 49 | } 50 | 51 | .slick-track 52 | { 53 | position: relative; 54 | top: 0; 55 | left: 0; 56 | 57 | display: block; 58 | margin-left: auto; 59 | margin-right: auto; 60 | } 61 | .slick-track:before, 62 | .slick-track:after 63 | { 64 | display: table; 65 | 66 | content: ''; 67 | } 68 | .slick-track:after 69 | { 70 | clear: both; 71 | } 72 | .slick-loading .slick-track 73 | { 74 | visibility: hidden; 75 | } 76 | 77 | .slick-slide 78 | { 79 | display: none; 80 | float: left; 81 | 82 | height: 100%; 83 | min-height: 1px; 84 | } 85 | [dir='rtl'] .slick-slide 86 | { 87 | float: right; 88 | } 89 | .slick-slide img 90 | { 91 | display: block; 92 | } 93 | .slick-slide.slick-loading img 94 | { 95 | display: none; 96 | } 97 | .slick-slide.dragging img 98 | { 99 | pointer-events: none; 100 | } 101 | .slick-initialized .slick-slide 102 | { 103 | display: block; 104 | } 105 | .slick-loading .slick-slide 106 | { 107 | visibility: hidden; 108 | } 109 | .slick-vertical .slick-slide 110 | { 111 | display: block; 112 | 113 | height: auto; 114 | 115 | border: 1px solid transparent; 116 | } 117 | .slick-arrow.slick-hidden { 118 | display: none; 119 | } 120 | -------------------------------------------------------------------------------- /examples/AdaptiveHeight.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function AdaptiveHeight() { 5 | const settings = { 6 | className: "", 7 | dots: true, 8 | infinite: true, 9 | slidesToShow: 1, 10 | slidesToScroll: 1, 11 | adaptiveHeight: true 12 | }; 13 | 14 | return ( 15 |
16 | 17 |
18 |

1

19 |
20 |
21 |

2

22 |

Hello

23 |
24 |
25 |

3

26 |

See ....

27 |

Height is adaptive

28 |
29 |
30 |

4

31 |
32 |
33 |

5

34 |
35 |
36 |

6

37 |
38 |
39 |
40 | ); 41 | } 42 | 43 | export default AdaptiveHeight; 44 | -------------------------------------------------------------------------------- /examples/AppendDots.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function AppendDots() { 5 | const settings = { 6 | dots: true, 7 | infinite: true, 8 | speed: 500, 9 | slidesToShow: 1, 10 | slidesToScroll: 1, 11 | appendDots: dots => ( 12 |
19 |
    {dots}
20 |
21 | ), 22 | customPaging: i => ( 23 |
30 | {i + 1} 31 |
32 | ) 33 | }; 34 | return ( 35 |
36 | 37 |
38 |

1

39 |
40 |
41 |

2

42 |
43 |
44 |

3

45 |
46 |
47 |

4

48 |
49 |
50 |

5

51 |
52 |
53 |

6

54 |
55 |
56 |
57 | ); 58 | } 59 | export default AppendDots; 60 | -------------------------------------------------------------------------------- /examples/AsNavFor.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function AsNavFor() { 5 | const [nav1, setNav1] = useState(null); 6 | const [nav2, setNav2] = useState(null); 7 | let sliderRef1 = useRef(null); 8 | let sliderRef2 = useRef(null); 9 | 10 | useEffect(() => { 11 | setNav1(sliderRef1); 12 | setNav2(sliderRef2); 13 | }, []); 14 | return ( 15 |
16 |

Slider Syncing (AsNavFor)

17 |

First Slider

18 | (sliderRef1 = slider)}> 19 |
20 |

1

21 |
22 |
23 |

2

24 |
25 |
26 |

3

27 |
28 |
29 |

4

30 |
31 |
32 |

5

33 |
34 |
35 |

6

36 |
37 |
38 |

Second Slider

39 | (sliderRef2 = slider)} 42 | slidesToShow={3} 43 | swipeToSlide={true} 44 | focusOnSelect={true} 45 | > 46 |
47 |

1

48 |
49 |
50 |

2

51 |
52 |
53 |

3

54 |
55 |
56 |

4

57 |
58 |
59 |

5

60 |
61 |
62 |

6

63 |
64 |
65 |
66 | ); 67 | } 68 | 69 | export default AsNavFor; 70 | -------------------------------------------------------------------------------- /examples/AutoPlay.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function AutoPlay() { 5 | const settings = { 6 | dots: true, 7 | infinite: true, 8 | slidesToShow: 3, 9 | slidesToScroll: 1, 10 | autoplay: true, 11 | speed: 2000, 12 | autoplaySpeed: 2000, 13 | cssEase: "linear" 14 | }; 15 | return ( 16 |
17 | 18 |
19 |

1

20 |
21 |
22 |

2

23 |
24 |
25 |

3

26 |
27 |
28 |

4

29 |
30 |
31 |

5

32 |
33 |
34 |

6

35 |
36 |
37 |
38 | ); 39 | } 40 | 41 | export default AutoPlay; 42 | -------------------------------------------------------------------------------- /examples/AutoPlayMethods.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function AutoPlayMethods() { 5 | let sliderRef = useRef(null); 6 | const play = () => { 7 | sliderRef.slickPlay(); 8 | }; 9 | const pause = () => { 10 | sliderRef.slickPause(); 11 | }; 12 | 13 | const settings = { 14 | dots: true, 15 | infinite: true, 16 | slidesToShow: 3, 17 | slidesToScroll: 1, 18 | autoplay: true, 19 | autoplaySpeed: 2000 20 | }; 21 | return ( 22 |
23 |

Auto Play {"&"} Pause with buttons

24 | (sliderRef = slider)} {...settings}> 25 |
26 |

1

27 |
28 |
29 |

2

30 |
31 |
32 |

3

33 |
34 |
35 |

4

36 |
37 |
38 |

5

39 |
40 |
41 |

6

42 |
43 |
44 |
45 | 48 | 51 |
52 |
53 | ); 54 | } 55 | export default AutoPlayMethods; 56 | -------------------------------------------------------------------------------- /examples/CenterMode.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function CenterMode() { 5 | const settings = { 6 | className: "center", 7 | centerMode: true, 8 | infinite: true, 9 | centerPadding: "60px", 10 | slidesToShow: 3, 11 | speed: 500 12 | }; 13 | return ( 14 |
15 | 16 |
17 |

1

18 |
19 |
20 |

2

21 |
22 |
23 |

3

24 |
25 |
26 |

4

27 |
28 |
29 |

5

30 |
31 |
32 |

6

33 |
34 |
35 |
36 | ); 37 | } 38 | 39 | export default CenterMode; 40 | -------------------------------------------------------------------------------- /examples/CustomArrows.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function SampleNextArrow(props) { 5 | const { className, style, onClick } = props; 6 | return ( 7 |
12 | ); 13 | } 14 | 15 | function SamplePrevArrow(props) { 16 | const { className, style, onClick } = props; 17 | return ( 18 |
23 | ); 24 | } 25 | 26 | function CustomArrows() { 27 | const settings = { 28 | dots: true, 29 | infinite: true, 30 | slidesToShow: 3, 31 | slidesToScroll: 1, 32 | nextArrow: , 33 | prevArrow: 34 | }; 35 | return ( 36 |
37 | 38 |
39 |

1

40 |
41 |
42 |

2

43 |
44 |
45 |

3

46 |
47 |
48 |

4

49 |
50 |
51 |

5

52 |
53 |
54 |

6

55 |
56 |
57 |
58 | ); 59 | } 60 | 61 | export default CustomArrows; 62 | -------------------------------------------------------------------------------- /examples/CustomPaging.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | import { baseUrl } from "./config"; 4 | 5 | function CustomPaging() { 6 | const settings = { 7 | customPaging: function(i) { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | }, 14 | dots: true, 15 | dotsClass: "slick-dots slick-thumb", 16 | infinite: true, 17 | speed: 500, 18 | slidesToShow: 1, 19 | slidesToScroll: 1 20 | }; 21 | return ( 22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 | ); 39 | } 40 | 41 | export default CustomPaging; 42 | -------------------------------------------------------------------------------- /examples/CustomSlides.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function CustomSlide(props) { 5 | const { index, ...otherProps } = props; 6 | return ( 7 |
8 |

{index}

9 |
10 | ); 11 | } 12 | 13 | function CustomSlides() { 14 | const settings = { 15 | dots: true, 16 | infinite: true, 17 | speed: 500, 18 | slidesToShow: 1, 19 | slidesToScroll: 1 20 | }; 21 | return ( 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | ); 33 | } 34 | 35 | export default CustomSlides; 36 | -------------------------------------------------------------------------------- /examples/DynamicSlides.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function DynamicSlides() { 5 | const [slides, setSlides] = useState([1, 2, 3, 4, 5, 6]); 6 | const handleClick = () => { 7 | setSlides( 8 | slides.length === 6 ? [1, 2, 3, 4, 5, 6, 7, 8, 9] : [1, 2, 3, 4, 5, 6] 9 | ); 10 | }; 11 | const settings = { 12 | dots: true, 13 | infinite: true, 14 | speed: 500, 15 | slidesToShow: 3, 16 | slidesToScroll: 3 17 | }; 18 | return ( 19 |
20 | 23 | 24 | {slides.map(slide => { 25 | return ( 26 |
27 |

{slide}

28 |
29 | ); 30 | })} 31 |
32 |
33 | ); 34 | } 35 | 36 | export default DynamicSlides; 37 | -------------------------------------------------------------------------------- /examples/Fade.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | import { baseUrl } from "./config"; 4 | 5 | function Fade() { 6 | const settings = { 7 | dots: true, 8 | fade: true, 9 | infinite: true, 10 | speed: 500, 11 | slidesToShow: 1, 12 | slidesToScroll: 1, 13 | waitForAnimate: false 14 | }; 15 | return ( 16 |
17 | 18 |
19 | 20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 |
32 | ); 33 | } 34 | 35 | export default Fade; 36 | -------------------------------------------------------------------------------- /examples/FocusOnSelect.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function FocusOnSelect() { 5 | const settings = { 6 | focusOnSelect: true, 7 | infinite: true, 8 | slidesToShow: 3, 9 | slidesToScroll: 1, 10 | speed: 500 11 | }; 12 | return ( 13 |
14 |
Click on any slide to select and make it current slide
15 | 16 |
17 |

1

18 |
19 |
20 |

2

21 |
22 |
23 |

3

24 |
25 |
26 |

4

27 |
28 |
29 |

5

30 |
31 |
32 |

6

33 |
34 |
35 |
36 | ); 37 | } 38 | 39 | export default FocusOnSelect; 40 | -------------------------------------------------------------------------------- /examples/LazyLoad.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | import { baseUrl } from "./config"; 4 | 5 | function LazyLoad() { 6 | const settings = { 7 | dots: true, 8 | lazyLoad: true, 9 | infinite: true, 10 | speed: 500, 11 | slidesToShow: 1, 12 | slidesToScroll: 1, 13 | initialSlide: 2 14 | }; 15 | return ( 16 |
17 | 18 |
19 | 20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 |
32 | ); 33 | } 34 | 35 | export default LazyLoad; 36 | -------------------------------------------------------------------------------- /examples/MultipleItems.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function MultipleItems() { 5 | const settings = { 6 | dots: true, 7 | infinite: true, 8 | speed: 500, 9 | slidesToShow: 3, 10 | slidesToScroll: 3 11 | }; 12 | return ( 13 |
14 | 15 |
16 |

1

17 |
18 |
19 |

2

20 |
21 |
22 |

3

23 |
24 |
25 |

4

26 |
27 |
28 |

5

29 |
30 |
31 |

6

32 |
33 |
34 |

7

35 |
36 |
37 |

8

38 |
39 |
40 |

9

41 |
42 |
43 |
44 | ); 45 | } 46 | 47 | export default MultipleItems; 48 | -------------------------------------------------------------------------------- /examples/MultipleRows.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function MultipleRows() { 5 | const settings = { 6 | className: "center", 7 | centerMode: true, 8 | infinite: true, 9 | centerPadding: "60px", 10 | slidesToShow: 3, 11 | speed: 500, 12 | rows: 2, 13 | slidesPerRow: 2 14 | }; 15 | return ( 16 |
17 | 18 |
19 |

1

20 |
21 |
22 |

2

23 |
24 |
25 |

3

26 |
27 |
28 |

4

29 |
30 |
31 |

5

32 |
33 |
34 |

6

35 |
36 |
37 |

7

38 |
39 |
40 |

8

41 |
42 |
43 |

9

44 |
45 |
46 |

10

47 |
48 |
49 |

11

50 |
51 |
52 |

12

53 |
54 |
55 |

13

56 |
57 |
58 |

14

59 |
60 |
61 |

15

62 |
63 |
64 |

16

65 |
66 |
67 |
68 | ); 69 | } 70 | 71 | export default MultipleRows; 72 | -------------------------------------------------------------------------------- /examples/PauseOnHover.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function PauseOnHover() { 5 | var settings = { 6 | dots: true, 7 | infinite: true, 8 | slidesToShow: 3, 9 | slidesToScroll: 1, 10 | autoplay: true, 11 | autoplaySpeed: 2000, 12 | pauseOnHover: true 13 | }; 14 | return ( 15 |
16 | 17 |
18 |

1

19 |
20 |
21 |

2

22 |
23 |
24 |

3

25 |
26 |
27 |

4

28 |
29 |
30 |

5

31 |
32 |
33 |

6

34 |
35 |
36 |
37 | ); 38 | } 39 | 40 | export default PauseOnHover; 41 | -------------------------------------------------------------------------------- /examples/PreviousNextMethods.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function PreviousNextMethods() { 5 | let sliderRef = useRef(null); 6 | const next = () => { 7 | sliderRef.slickNext(); 8 | }; 9 | const previous = () => { 10 | sliderRef.slickPrev(); 11 | }; 12 | const settings = { 13 | dots: true, 14 | infinite: true, 15 | speed: 500, 16 | slidesToShow: 1, 17 | slidesToScroll: 1 18 | }; 19 | return ( 20 |
21 | { 23 | sliderRef = slider; 24 | }} 25 | {...settings} 26 | > 27 |
28 |

1

29 |
30 |
31 |

2

32 |
33 |
34 |

3

35 |
36 |
37 |

4

38 |
39 |
40 |

5

41 |
42 |
43 |

6

44 |
45 |
46 |
47 | 50 | 53 |
54 |
55 | ); 56 | } 57 | 58 | export default PreviousNextMethods; 59 | -------------------------------------------------------------------------------- /examples/Resizable.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function Resizable() { 5 | const [display, setDisplay] = useState(true); 6 | const [width, setWidth] = useState(600); 7 | 8 | const settings = { 9 | dots: true, 10 | infinite: true, 11 | speed: 500, 12 | slidesToShow: 3, 13 | slidesToScroll: 1 14 | }; 15 | return ( 16 |
17 |

Resizable Collapsible

18 | 22 | 26 | 30 |
36 | 37 |
38 |

1

39 |
40 |
41 |

2

42 |
43 |
44 |

3

45 |
46 |
47 |

4

48 |
49 |
50 |

5

51 |
52 |
53 |

6

54 |
55 |
56 |
57 |
58 | ); 59 | } 60 | 61 | export default Resizable; 62 | -------------------------------------------------------------------------------- /examples/Responsive.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function Responsive() { 5 | var settings = { 6 | dots: true, 7 | infinite: false, 8 | speed: 500, 9 | slidesToShow: 4, 10 | slidesToScroll: 4, 11 | initialSlide: 0, 12 | responsive: [ 13 | { 14 | breakpoint: 1024, 15 | settings: { 16 | slidesToShow: 3, 17 | slidesToScroll: 3, 18 | infinite: true, 19 | dots: true 20 | } 21 | }, 22 | { 23 | breakpoint: 600, 24 | settings: { 25 | slidesToShow: 2, 26 | slidesToScroll: 2, 27 | initialSlide: 2 28 | } 29 | }, 30 | { 31 | breakpoint: 480, 32 | settings: { 33 | slidesToShow: 1, 34 | slidesToScroll: 1 35 | } 36 | } 37 | ] 38 | }; 39 | return ( 40 |
41 | 42 |
43 |

1

44 |
45 |
46 |

2

47 |
48 |
49 |

3

50 |
51 |
52 |

4

53 |
54 |
55 |

5

56 |
57 |
58 |

6

59 |
60 |
61 |

7

62 |
63 |
64 |

8

65 |
66 |
67 |
68 | ); 69 | } 70 | 71 | export default Responsive; 72 | -------------------------------------------------------------------------------- /examples/Rtl.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function Rtl() { 5 | const settings = { 6 | dots: true, 7 | infinite: true, 8 | slidesToShow: 3, 9 | slidesToScroll: 1, 10 | autoplay: true, 11 | autoplaySpeed: 2000, 12 | rtl: true 13 | }; 14 | return ( 15 |
16 |

Right to Left

17 | 18 |
19 |

1

20 |
21 |
22 |

2

23 |
24 |
25 |

3

26 |
27 |
28 |

4

29 |
30 |
31 |

5

32 |
33 |
34 |

6

35 |
36 |
37 |
38 | ); 39 | } 40 | 41 | export default Rtl; 42 | -------------------------------------------------------------------------------- /examples/SimpleSlider.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function SimpleSlider() { 5 | const settings = { 6 | dots: true, 7 | infinite: true, 8 | speed: 500, 9 | slidesToShow: 1, 10 | slidesToScroll: 1 11 | }; 12 | return ( 13 |
14 | 15 |
16 |

1

17 |
18 |
19 |

2

20 |
21 |
22 |

3

23 |
24 |
25 |

4

26 |
27 |
28 |

5

29 |
30 |
31 |

6

32 |
33 |
34 |
35 | ); 36 | } 37 | 38 | export default SimpleSlider; 39 | -------------------------------------------------------------------------------- /examples/SlickGoTo.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef } from "react"; 2 | import Slider from "react-slick"; 3 | import { baseUrl } from "./config"; 4 | 5 | function SlickGoTo() { 6 | const [slideIndex, setSlideIndex] = useState(0); 7 | const [updateCount, setUpdateCount] = useState(0); 8 | let sliderRef = useRef(null); 9 | const settings = { 10 | dots: false, 11 | infinite: true, 12 | speed: 500, 13 | slidesToShow: 1, 14 | slidesToScroll: 1, 15 | afterChange: () => setUpdateCount(updateCount + 1), 16 | beforeChange: (current, next) => setSlideIndex(next) 17 | }; 18 | return ( 19 |
20 |

Total updates: {updateCount}

21 | sliderRef.slickGoTo(e.target.value)} 23 | value={slideIndex} 24 | type="range" 25 | min={0} 26 | max={3} 27 | /> 28 | { 30 | sliderRef = slider; 31 | }} 32 | {...settings} 33 | > 34 |
35 | 36 |
37 |
38 | 39 |
40 |
41 | 42 |
43 |
44 | 45 |
46 |
47 |
48 | ); 49 | } 50 | 51 | export default SlickGoTo; 52 | -------------------------------------------------------------------------------- /examples/SlideChangeHooks.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function SlideChangeHooks() { 5 | const [oldSlide, setOldSlide] = useState(0); 6 | const [activeSlide, setActiveSlide] = useState(0); 7 | const [activeSlide2, setActiveSlide2] = useState(0); 8 | 9 | const settings = { 10 | dots: true, 11 | infinite: true, 12 | speed: 1000, 13 | slidesToShow: 1, 14 | slidesToScroll: 1, 15 | beforeChange: (current, next) => { 16 | setOldSlide(current); 17 | setActiveSlide(next); 18 | }, 19 | afterChange: current => setActiveSlide2(current) 20 | }; 21 | return ( 22 |
23 |

beforeChange and afterChange hooks

24 |

25 | BeforeChange {"=>"} oldSlide: {oldSlide} 26 |

27 |

28 | BeforeChange {"=>"} activeSlide: {activeSlide} 29 |

30 |

31 | AfterChange {"=>"} activeSlide: {activeSlide2} 32 |

33 | 34 |
35 |

1

36 |
37 |
38 |

2

39 |
40 |
41 |

3

42 |
43 |
44 |

4

45 |
46 |
47 |

5

48 |
49 |
50 |

6

51 |
52 |
53 |
54 | ); 55 | } 56 | 57 | export default SlideChangeHooks; 58 | -------------------------------------------------------------------------------- /examples/SwipeToSlide.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function SwipeToSlide() { 5 | const settings = { 6 | className: "center", 7 | infinite: true, 8 | centerPadding: "60px", 9 | slidesToShow: 5, 10 | swipeToSlide: true, 11 | afterChange: function(index) { 12 | console.log( 13 | `Slider Changed to: ${index + 1}, background: #222; color: #bada55` 14 | ); 15 | } 16 | }; 17 | return ( 18 |
19 | 20 |
21 |

1

22 |
23 |
24 |

2

25 |
26 |
27 |

3

28 |
29 |
30 |

4

31 |
32 |
33 |

5

34 |
35 |
36 |

6

37 |
38 |
39 |

7

40 |
41 |
42 |

8

43 |
44 |
45 |

9

46 |
47 |
48 |
49 | ); 50 | } 51 | 52 | export default SwipeToSlide; 53 | -------------------------------------------------------------------------------- /examples/UnevenSetsFinite.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function UnevenSetsFinite() { 5 | var settings = { 6 | dots: true, 7 | infinite: false, 8 | speed: 500, 9 | slidesToScroll: 4, 10 | slidesToShow: 4 11 | }; 12 | return ( 13 |
14 | 15 |
16 |

1

17 |
18 |
19 |

2

20 |
21 |
22 |

3

23 |
24 |
25 |

4

26 |
27 |
28 |

5

29 |
30 |
31 |

6

32 |
33 |
34 |
35 | ); 36 | } 37 | 38 | export default UnevenSetsFinite; 39 | -------------------------------------------------------------------------------- /examples/UnevenSetsInfinite.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function UnevenSetsInfinite() { 5 | var settings = { 6 | dots: true, 7 | infinite: true, 8 | speed: 500, 9 | slidesToScroll: 4, 10 | slidesToShow: 4 11 | }; 12 | return ( 13 |
14 | 15 |
16 |

1

17 |
18 |
19 |

2

20 |
21 |
22 |

3

23 |
24 |
25 |

4

26 |
27 |
28 |

5

29 |
30 |
31 |

6

32 |
33 |
34 |
35 | ); 36 | } 37 | 38 | export default UnevenSetsInfinite; 39 | -------------------------------------------------------------------------------- /examples/VariableWidth.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function VariableWidth() { 5 | const settings = { 6 | className: "slider variable-width", 7 | dots: true, 8 | infinite: true, 9 | centerMode: true, 10 | slidesToShow: 1, 11 | slidesToScroll: 1, 12 | variableWidth: true 13 | }; 14 | return ( 15 |
16 | 17 |
18 |

100

19 |
20 |
21 |

200

22 |
23 |
24 |

75

25 |
26 |
27 |

300

28 |
29 |
30 |

225

31 |
32 |
33 |

175

34 |
35 |
36 |
37 | ); 38 | } 39 | 40 | export default VariableWidth; 41 | -------------------------------------------------------------------------------- /examples/VerticalMode.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function VerticalMode() { 5 | const settings = { 6 | dots: true, 7 | infinite: true, 8 | slidesToShow: 3, 9 | slidesToScroll: 1, 10 | vertical: true, 11 | verticalSwiping: true, 12 | beforeChange: function(currentSlide, nextSlide) { 13 | console.log("before change", currentSlide, nextSlide); 14 | }, 15 | afterChange: function(currentSlide) { 16 | console.log("after change", currentSlide); 17 | } 18 | }; 19 | return ( 20 |
21 | 22 |
23 |

1

24 |
25 |
26 |

2

27 |
28 |
29 |

3

30 |
31 |
32 |

4

33 |
34 |
35 |

5

36 |
37 |
38 |

6

39 |
40 |
41 |
42 | ); 43 | } 44 | 45 | export default VerticalMode; 46 | -------------------------------------------------------------------------------- /examples/VerticalSwipeToSlide.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Slider from "react-slick"; 3 | 4 | function VerticalSwipeToSlide() { 5 | const settings = { 6 | dots: true, 7 | infinite: true, 8 | slidesToShow: 3, 9 | slidesToScroll: 1, 10 | vertical: true, 11 | verticalSwiping: true, 12 | swipeToSlide: true, 13 | beforeChange: function(currentSlide, nextSlide) { 14 | console.log("before change", currentSlide, nextSlide); 15 | }, 16 | afterChange: function(currentSlide) { 17 | console.log("after change", currentSlide); 18 | } 19 | }; 20 | return ( 21 |
22 | 23 |
24 |

1

25 |
26 |
27 |

2

28 |
29 |
30 |

3

31 |
32 |
33 |

4

34 |
35 |
36 |

5

37 |
38 |
39 |

6

40 |
41 |
42 |
43 | ); 44 | } 45 | 46 | export default VerticalSwipeToSlide; 47 | -------------------------------------------------------------------------------- /examples/__tests__/CentreMode.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CenterMode from "../CenterMode"; 3 | import { render } from "@testing-library/react"; 4 | import { html as beautify_html } from "js-beautify"; 5 | import { 6 | getActiveSlides, 7 | getActiveSlidesCount, 8 | getClonesCount, 9 | getCurrentSlide, 10 | getSlidesCount, 11 | clickNext 12 | } from "../../test-utils"; 13 | 14 | describe("CenterMode Tests", () => { 15 | test("Counting test", () => { 16 | const { container } = render(); 17 | let totalSlides = getSlidesCount(container); 18 | let clonedSlides = getClonesCount(container); 19 | let activeSlides = getActiveSlidesCount(container); 20 | expect(totalSlides).toEqual(16); 21 | expect(clonedSlides).toEqual(10); 22 | expect(activeSlides).toEqual(3); 23 | //expect(beautify_html(toString(container))).toMatchSnapshot(); 24 | }); 25 | test("Positioning test", () => { 26 | const { container } = render(); 27 | let currentSlide = getCurrentSlide(container); 28 | let activeslides = getActiveSlides(container); 29 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(0); 30 | expect( 31 | Array.from(activeslides).map(e => parseInt(e.getAttribute("data-index"))) 32 | ).toEqual([-1, 0, 1]); 33 | //expect(beautify_html(toString(container))).toMatchSnapshot(); 34 | }); 35 | test("Activity test", () => { 36 | const { container } = render(); 37 | let currentSlide = getCurrentSlide(container); 38 | let activeslides = getActiveSlides(container); 39 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(0); 40 | expect( 41 | Array.from(activeslides).map(e => parseInt(e.getAttribute("data-index"))) 42 | ).toEqual([-1, 0, 1]); 43 | clickNext(container); 44 | 45 | currentSlide = getCurrentSlide(container); 46 | activeslides = getActiveSlides(container); 47 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(1); 48 | expect( 49 | Array.from(activeslides).map(e => parseInt(e.getAttribute("data-index"))) 50 | ).toEqual([0, 1, 2]); 51 | 52 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /examples/__tests__/Fade.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import Fade from "../Fade"; 4 | import { getActiveSlide, clickNext, clickPrevious } from "../../test-utils"; 5 | 6 | describe("Fade", () => { 7 | it("should change slides when clicked on next & prev buttons", () => { 8 | const { container } = render(); 9 | let activeslide = getActiveSlide(container); 10 | expect(parseInt(activeslide.getAttribute("data-index"))).toEqual(0); 11 | clickNext(container); 12 | activeslide = getActiveSlide(container); 13 | expect(parseInt(activeslide.getAttribute("data-index"))).toEqual(1); 14 | clickPrevious(container); 15 | activeslide = getActiveSlide(container); 16 | expect(parseInt(activeslide.getAttribute("data-index"))).toEqual(0); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/__tests__/FocusOnSelect.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, fireEvent } from "@testing-library/react"; 3 | import { html as beautify_html } from "js-beautify"; 4 | import { 5 | activeSlide, 6 | clickNext, 7 | clickPrevious, 8 | getButtons, 9 | getCurrentSlide 10 | } from "../../test-utils"; 11 | import FocusOnSelect from "../FocusOnSelect"; 12 | 13 | describe("FocusOnSelect Tests", () => { 14 | test("Activity Test", () => { 15 | const { container } = render(); 16 | expect( 17 | parseInt(getCurrentSlide(container).getAttribute("data-index")) 18 | ).toEqual(0); 19 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 20 | Array.from(container.getElementsByClassName("slick-slide")).map(e => 21 | e.getAttribute("data-index") == "2" 22 | ? fireEvent( 23 | e, 24 | new MouseEvent("click", { bubbles: true, cancelable: true }) 25 | ) 26 | : null 27 | ); 28 | expect( 29 | parseInt(getCurrentSlide(container).getAttribute("data-index")) 30 | ).toEqual(2); 31 | //expect(beautify_html(toString(container))).toMatchSnapshot(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /examples/__tests__/MultipleItems.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, fireEvent } from "@testing-library/react"; 3 | import { html as beautify_html } from "js-beautify"; 4 | import { 5 | activeSlide, 6 | activeSlides, 7 | clickNext, 8 | clickPrevious, 9 | getActiveButton, 10 | getActiveSlidesCount, 11 | getActiveSlidesText, 12 | getButtons, 13 | getButtonsLength, 14 | getButtonsListItem, 15 | getClonesCount, 16 | getCurrentSlide, 17 | getSlidesCount, 18 | hasClass 19 | } from "../../test-utils"; 20 | import MultipleItems from "../MultipleItems"; 21 | 22 | describe("Multiple Items", function() { 23 | it("should have 9 actual slides and (3(pre) + 9(post)) clone slides", function() { 24 | const { container } = render(); 25 | expect(getSlidesCount(container)).toEqual(21); 26 | expect(getClonesCount(container)).toEqual(12); 27 | //expect(beautify_html(toString(container))).toMatchSnapshot(); 28 | }); 29 | it("should have 3 active slides", function() { 30 | const { container } = render(); 31 | expect(getActiveSlidesCount(container)).toEqual(3); 32 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 33 | }); 34 | it("should have 3 dots", function() { 35 | const { container } = render(); 36 | expect(getButtonsLength(container)).toEqual(3); 37 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 38 | }); 39 | it("should show first 3 slides", function() { 40 | const { container } = render(); 41 | expect(getActiveButton(container)).toEqual(["1"]); 42 | expect(getActiveSlidesText(container)).toEqual(["1", "2", "3"]); 43 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 44 | }); 45 | it("should show slides from 4 to 6 when next button is clicked", function() { 46 | const { container } = render(); 47 | clickNext(container); 48 | // Array.from(container.querySelectorAll(".slick-current")).map(e=>console.log(e.textContent)) 49 | expect(getActiveButton(container)).toEqual(["2"]); 50 | expect(getActiveSlidesText(container)).toEqual(["4", "5", "6"]); 51 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 52 | }); 53 | it("should show last 3 slides when previous button is clicked", function() { 54 | const { container } = render(); 55 | clickPrevious(container); 56 | expect(getActiveButton(container)).toEqual(["3"]); 57 | expect(getActiveSlidesText(container)).toEqual(["7", "8", "9"]); 58 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 59 | }); 60 | it("should show slides first 3 slides when first dot is clicked", function() { 61 | const { container } = render(); 62 | fireEvent( 63 | getButtons(container)[0], 64 | new MouseEvent("click", { 65 | bubbles: true, 66 | cancelable: true 67 | }) 68 | ); 69 | expect(getActiveButton(container)).toEqual(["1"]); 70 | expect(getActiveSlidesText(container)).toEqual(["1", "2", "3"]); 71 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 72 | }); 73 | it("should show slides from 4 to 6 when middle dot is clicked", function() { 74 | const { container } = render(); 75 | fireEvent( 76 | getButtons(container)[1], 77 | new MouseEvent("click", { 78 | bubbles: true, 79 | cancelable: true 80 | }) 81 | ); 82 | expect(getActiveButton(container)).toEqual(["2"]); 83 | expect(getActiveSlidesText(container)).toEqual(["4", "5", "6"]); 84 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 85 | }); 86 | it("should show last 3 slides when last dot is clicked", function() { 87 | const { container } = render(); 88 | fireEvent( 89 | getButtons(container)[2], 90 | new MouseEvent("click", { 91 | bubbles: true, 92 | cancelable: true 93 | }) 94 | ); 95 | expect(getActiveButton(container)).toEqual(["3"]); 96 | expect(getActiveSlidesText(container)).toEqual(["7", "8", "9"]); 97 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /examples/__tests__/SimpleSlider.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import SimpleSlider from "../SimpleSlider"; 3 | import { render, fireEvent, waitFor, screen } from "@testing-library/react"; 4 | import { html as beautify_html } from "js-beautify"; 5 | import { 6 | getActiveSlide, 7 | clickNext, 8 | clickPrevious, 9 | hasClass, 10 | getActiveSlides, 11 | getActiveSlidesCount, 12 | getActiveSlidesText, 13 | getButtons, 14 | getButtonsListItem, 15 | getCurrentSlide 16 | } from "../../test-utils"; 17 | 18 | describe("SimpleSlider example", () => { 19 | it("should have 13 slides (1(preclone) + 6(actual) + 6(postclone))", function() { 20 | const { container } = render(); 21 | expect(container.getElementsByClassName("slick-slide").length).toBe(13); 22 | }); 23 | it("should have 7 clone slides", function() { 24 | const { container } = render(); 25 | expect(container.getElementsByClassName("slick-cloned").length).toBe(7); 26 | }); 27 | it("should have 1 current slide", function() { 28 | const { container } = render(); 29 | expect( 30 | container.querySelectorAll(".slick-slide.slick-current").length 31 | ).toBe(1); 32 | expect(parseInt(getCurrentSlide(container).textContent) - 1).toBe(0); 33 | }); 34 | it("should have 1 active slide", function() { 35 | const { container } = render(); 36 | expect(container.querySelectorAll(".slick-slide.slick-active").length).toBe( 37 | 1 38 | ); 39 | expect( 40 | Array.from(getActiveSlide(container).children).map( 41 | e => parseInt(e.textContent) - 1 42 | )[0] 43 | ).toBe(0); 44 | }); 45 | it("should have 6 dots", function() { 46 | const { container } = render(); 47 | expect( 48 | container.getElementsByClassName("slick-dots")[0].children.length 49 | ).toBe(6); 50 | }); 51 | it("should have 1 active dot", function() { 52 | const { container } = render(); 53 | 54 | expect(container.querySelectorAll(".slick-dots .slick-active").length).toBe( 55 | 1 56 | ); 57 | }); 58 | it("should have a prev arrow", function() { 59 | const { container } = render(); 60 | expect(container.getElementsByClassName("slick-prev").length).toBe(1); 61 | }); 62 | it("should have a next arrow", function() { 63 | const { container } = render(); 64 | expect(container.getElementsByClassName("slick-next").length).toBe(1); 65 | }); 66 | it("should got to next slide when next button is clicked", function() { 67 | const { container } = render(); 68 | clickNext(container); 69 | expect( 70 | container.querySelectorAll(".slick-slide.slick-active")[0].textContent 71 | ).toBe("2"); 72 | expect(container.querySelectorAll(".slick-dots .slick-active").length).toBe( 73 | 1 74 | ); 75 | expect( 76 | container.querySelectorAll(".slick-dots")[0].children[1] 77 | ).toHaveClass("slick-active"); 78 | }); 79 | it("should goto previous slide when prev button is clicked", function() { 80 | const { container } = render(); 81 | clickPrevious(container); 82 | expect( 83 | container.querySelectorAll(".slick-slide.slick-active")[0].textContent 84 | ).toBe("6"); 85 | expect(container.querySelectorAll(".slick-dots .slick-active").length).toBe( 86 | 1 87 | ); 88 | expect( 89 | container.querySelectorAll(".slick-dots")[0].children[5] 90 | ).toHaveClass("slick-active"); 91 | }); 92 | it("should goto 4th slide when 4th dot is clicked", function() { 93 | const { container } = render(); 94 | fireEvent( 95 | container.querySelectorAll(".slick-dots button")[3], 96 | new MouseEvent("click", { 97 | bubbles: true, 98 | cancelable: true 99 | }) 100 | ); 101 | expect(getActiveSlidesText(container)[0]).toEqual("4"); 102 | expect(getActiveSlidesCount(container)).toEqual(1); 103 | expect(hasClass(getButtonsListItem(container)[3], "slick-active")).toEqual( 104 | true 105 | ); 106 | }); 107 | }); 108 | 109 | describe("Simple Slider Snapshots", function() { 110 | it("slider initial state", function() { 111 | const { container } = render(); 112 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 113 | }); 114 | it("click on next button", function() { 115 | const { container } = render(); 116 | clickNext(container); 117 | //expect(beautify_html(toString(container))).toMatchSnapshot(); 118 | }); 119 | it("click on prev button", function() { 120 | const { container } = render(); 121 | clickPrevious(container); 122 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 123 | }); 124 | it("click on 3rd dot", function() { 125 | const { container } = render(); 126 | fireEvent( 127 | getButtons(container)[2], 128 | new MouseEvent("click", { 129 | bubbles: true, 130 | cancelable: true 131 | }) 132 | ); 133 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /examples/__tests__/SlickGoTo.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { fireEvent, getRoles, render } from "@testing-library/react"; 3 | import SlickGoTo from "../SlickGoTo"; 4 | import { activeSlide, getActiveSlides, getSlidesCount } from "../../test-utils"; 5 | 6 | describe.skip("SlickGoTo", () => { 7 | it("should goto 2nd slide", () => { 8 | const { container } = render(); 9 | fireEvent.change(container.getElementsByTagName("input")[0], { 10 | target: { value: 1 } 11 | }); 12 | let currentImg = Array.from( 13 | getActiveSlide(container).getElementsByTagName("img") 14 | )[0]; 15 | expect(currentImg.getAttribute("src")).toEqual( 16 | "/img/react-slick/abstract02.jpg" 17 | ); 18 | }); 19 | it("should goto 2nd slide, even if input is number in string format", () => { 20 | const { container } = render(); 21 | fireEvent.change(container.getElementsByTagName("input")[0], { 22 | target: { value: "1" } 23 | }); 24 | let currentImg = Array.from( 25 | getActiveSlide(container).getElementsByTagName("img") 26 | )[0]; 27 | expect(currentImg.getAttribute("src")).toEqual( 28 | "/img/react-slick/abstract02.jpg" 29 | ); 30 | }); 31 | it("should remain at 1st slide", () => { 32 | const { container } = render(); 33 | fireEvent.change(container.getElementsByTagName("input")[0], { 34 | target: { value: 0 } 35 | }); 36 | let currentImg = Array.from( 37 | getActiveSlide(container).getElementsByTagName("img") 38 | )[0]; 39 | expect(currentImg.getAttribute("src")).toEqual( 40 | "/img/react-slick/abstract01.jpg" 41 | ); 42 | }); 43 | it.skip("should go to 1st slide from another 3rd slide", () => { 44 | // skipped because two simultaneous clicks dont' work with css and speed>0 45 | const wrapper = render(); 46 | wrapper.find("input").simulate("change", { target: { value: 3 } }); 47 | wrapper.find("input").simulate("change", { target: { value: 0 } }); 48 | expect(wrapper.find(".slick-slide.slick-active img").props().src).toEqual( 49 | "/img/react-slick/abstract01.jpg" 50 | ); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /examples/__tests__/UnevenSets.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, fireEvent } from "@testing-library/react"; 3 | import UnevenSetsFinite from "../UnevenSetsFinite"; 4 | import UnevenSetsInfinite from "../UnevenSetsInfinite"; 5 | import { html as beautify_html } from "js-beautify"; 6 | import { 7 | getActiveSlides, 8 | clickNext, 9 | getActiveSlidesCount, 10 | getButtonsLength, 11 | getClonesCount, 12 | getCurrentSlide, 13 | getSlidesCount 14 | } from "../../test-utils"; 15 | 16 | describe("UnevenSets Finite", () => { 17 | test("Counting test", () => { 18 | const { container } = render(); 19 | let totalSlides = getSlidesCount(container); 20 | let clonedSlides = getClonesCount(container); 21 | let activeSlides = getActiveSlidesCount(container); 22 | let dots = getButtonsLength(container); 23 | expect(totalSlides).toEqual(6); 24 | expect(clonedSlides).toEqual(0); 25 | expect(activeSlides).toEqual(4); 26 | expect(dots).toEqual(2); 27 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 28 | }); 29 | test("Positioning test", () => { 30 | const { container } = render(); 31 | let currentSlide = getCurrentSlide(container); 32 | let activeslides = getActiveSlides(container); 33 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(0); 34 | expect( 35 | Array.from(activeslides).map(slide => 36 | parseInt(slide.getAttribute("data-index")) 37 | ) 38 | ).toEqual([0, 1, 2, 3]); 39 | //expect(beautify_html(toString(container))).toMatchSnapshot(); 40 | }); 41 | test("Activity test", () => { 42 | const { container } = render(); 43 | let currentSlide = getCurrentSlide(container); 44 | let activeslides = getActiveSlides(container); 45 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(0); 46 | expect( 47 | Array.from(activeslides).map(slide => 48 | parseInt(slide.getAttribute("data-index")) 49 | ) 50 | ).toEqual([0, 1, 2, 3]); 51 | 52 | clickNext(container); 53 | 54 | currentSlide = getCurrentSlide(container); 55 | activeslides = getActiveSlides(container); 56 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(4); 57 | expect( 58 | Array.from(activeslides).map(slide => 59 | parseInt(slide.getAttribute("data-index")) 60 | ) 61 | ).toEqual([2, 3, 4, 5]); 62 | 63 | clickNext(container); 64 | 65 | currentSlide = getCurrentSlide(container); 66 | activeslides = getActiveSlides(container); 67 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(4); 68 | expect( 69 | Array.from(activeslides).map(slide => 70 | parseInt(slide.getAttribute("data-index")) 71 | ) 72 | ).toEqual([2, 3, 4, 5]); 73 | 74 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 75 | }); 76 | }); 77 | 78 | describe("UnevenSets Infinite", () => { 79 | test("Counting test", () => { 80 | const { container } = render(); 81 | let totalSlides = getSlidesCount(container); 82 | let clonedSlides = getClonesCount(container); 83 | let activeSlides = getActiveSlidesCount(container); 84 | let dots = getButtonsLength(container); 85 | expect(totalSlides).toEqual(16); 86 | expect(clonedSlides).toEqual(10); 87 | expect(activeSlides).toEqual(4); 88 | expect(dots).toEqual(2); 89 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 90 | }); 91 | test("Positioning test", () => { 92 | const { container } = render(); 93 | let currentSlide = getCurrentSlide(container); 94 | let activeslides = getActiveSlides(container); 95 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(0); 96 | expect( 97 | Array.from(activeslides).map(slide => 98 | parseInt(slide.getAttribute("data-index")) 99 | ) 100 | ).toEqual([0, 1, 2, 3]); 101 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 102 | }); 103 | test("Activity test", () => { 104 | const { container } = render(); 105 | let currentSlide = getCurrentSlide(container); 106 | let activeslides = getActiveSlides(container); 107 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(0); 108 | expect( 109 | Array.from(activeslides).map(slide => 110 | parseInt(slide.getAttribute("data-index")) 111 | ) 112 | ).toEqual([0, 1, 2, 3]); 113 | 114 | clickNext(container); 115 | 116 | currentSlide = getCurrentSlide(container); 117 | activeslides = getActiveSlides(container); 118 | expect(parseInt(currentSlide.getAttribute("data-index"))).toEqual(4); 119 | expect( 120 | Array.from(activeslides).map(slide => 121 | parseInt(slide.getAttribute("data-index")) 122 | ) 123 | ).toEqual([4, 5, 6, 7]); 124 | // expect(beautify_html(toString(container))).toMatchSnapshot(); 125 | }); 126 | }); 127 | -------------------------------------------------------------------------------- /examples/__tests__/sample.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { fireEvent, render } from "@testing-library/react"; 3 | 4 | export default class Counter extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | count: 0 9 | }; 10 | } 11 | render() { 12 | return ( 13 | 16 | ); 17 | } 18 | } 19 | 20 | describe("sample counter test", function() { 21 | it("mutliple counts", function() { 22 | const { container } = render(); 23 | const button = container.getElementsByTagName("Button")[0]; 24 | fireEvent( 25 | button, 26 | new MouseEvent("click", { 27 | bubbles: true, 28 | cancelable: true 29 | }) 30 | ); 31 | fireEvent( 32 | button, 33 | new MouseEvent("click", { 34 | bubbles: true, 35 | cancelable: true 36 | }) 37 | ); 38 | expect(button.textContent).toEqual("Count 2"); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /examples/config.js: -------------------------------------------------------------------------------- 1 | // export const baseUrl = 2 | // process.env.NODE_ENV === "production" 3 | // ? "https://s3.amazonaws.com/static.neostack.com/img/react-slick" 4 | // : "/img/react-slick"; 5 | 6 | export const baseUrl = "/img/react-slick"; 7 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var gulp = require("gulp"); 4 | var del = require("del"); 5 | var rename = require("gulp-rename"); 6 | var webpack = require("webpack"); 7 | var WebpackDevServer = require("webpack-dev-server"); 8 | var assign = require("object-assign"); 9 | var opn = require("opn"); 10 | 11 | var UglifyJsPlugin = require("uglifyjs-webpack-plugin"); 12 | 13 | const DEV_PORT = process.env.DEV_PORT || 8080; 14 | 15 | gulp.task("clean", function() { 16 | return del(["./build/*"]); 17 | }); 18 | 19 | gulp.task("copy", function() { 20 | gulp.src("./docs/index.html").pipe(gulp.dest("./build")); 21 | gulp.src("./docs/docs.css").pipe(gulp.dest("./build")); 22 | gulp.src("./docs/slick.css").pipe(gulp.dest("./build")); 23 | gulp.src("./docs/slick-theme.css").pipe(gulp.dest("./build")); 24 | gulp.src("./docs/img/**/*").pipe(gulp.dest("./build/img")); 25 | gulp 26 | .src("./node_modules/slick-carousel/slick/fonts/*") 27 | .pipe(gulp.dest("./build/fonts")); 28 | return gulp 29 | .src("./node_modules/slick-carousel/slick/ajax-loader.gif") 30 | .pipe(gulp.dest("./build")); 31 | }); 32 | 33 | gulp.task("prepare-playwright", function() { 34 | // Copy files to src-jsx directory with jsx extension 35 | return gulp 36 | .src("./src/**/*.js") 37 | .pipe(rename({ extname: ".jsx" })) 38 | .pipe(gulp.dest("./src-jsx")); 39 | }); 40 | 41 | gulp.task( 42 | "watch", 43 | gulp.series(["copy"], function(done) { 44 | gulp.watch(["./docs/index.html"], gulp.parallel(["copy"])); 45 | gulp.watch(["./docs/docs.css"], gulp.parallel(["copy"])); 46 | gulp.watch(["./docs/slick.css"], gulp.parallel(["copy"])); 47 | gulp.watch(["./docs/slick-theme.css"], gulp.parallel(["copy"])); 48 | done(); 49 | }) 50 | ); 51 | 52 | gulp.task( 53 | "server", 54 | gulp.series(["watch", "copy"], function() { 55 | console.log("Start"); 56 | var myConfig = require("./webpack.config"); 57 | if (process.env.SINGLE_DEMO) { 58 | myConfig.entry = { 59 | "docs.js": "./docs/single-demo.js" 60 | }; 61 | } 62 | myConfig.plugins = myConfig.plugins.concat( 63 | new webpack.DefinePlugin({ 64 | "process.env": { 65 | NODE_ENV: JSON.stringify("dev_docs") 66 | } 67 | }) 68 | ); 69 | 70 | new WebpackDevServer(webpack(myConfig), { 71 | contentBase: "./build", 72 | hot: true, 73 | stats: { 74 | colors: true 75 | } 76 | }).listen(DEV_PORT, "0.0.0.0", function(err, result) { 77 | if (err) { 78 | console.log(err); 79 | } else { 80 | const server_url = `http://0.0.0.0:${DEV_PORT}`; 81 | console.log(`> Dev Server started at ${server_url}`); 82 | opn(server_url); 83 | } 84 | }); 85 | }) 86 | ); 87 | 88 | // gulp tasks for building dist files 89 | gulp.task("dist-clean", function() { 90 | return del(["./dist/*"]); 91 | }); 92 | 93 | var distConfig = require("./webpack.config.dist.js"); 94 | gulp.task("dist-unmin", function(cb) { 95 | var unminConfig = assign({}, distConfig); 96 | unminConfig.output.filename = "react-slick.js"; 97 | unminConfig.mode = "none"; 98 | return webpack(unminConfig, function(err, stat) { 99 | console.error(err); 100 | cb(); 101 | }); 102 | }); 103 | 104 | gulp.task("dist-min", function(cb) { 105 | var minConfig = assign({}, distConfig); 106 | minConfig.output.filename = "react-slick.min.js"; 107 | minConfig.plugins = minConfig.plugins.concat( 108 | new UglifyJsPlugin({ 109 | cache: true, 110 | parallel: true, 111 | sourceMap: true, 112 | uglifyOptions: { 113 | warnings: false 114 | } 115 | }) 116 | ); 117 | return webpack(minConfig, function(err, stat) { 118 | console.error(err); 119 | cb(); 120 | }); 121 | }); 122 | 123 | gulp.task( 124 | "dist", 125 | gulp.series(["dist-clean", "dist-unmin", "dist-min"], function(done) { 126 | done(); 127 | }) 128 | ); 129 | 130 | gulp.task("default", gulp.series(["watch", "server"])); 131 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: "jsdom", 3 | setupFilesAfterEnv: ["/test-setup.js"], 4 | testPathIgnorePatterns: ["/node_modules/", "playwright-tests"] 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-slick", 3 | "version": "0.30.3", 4 | "description": " React port of slick carousel", 5 | "main": "./lib", 6 | "files": [ 7 | "dist", 8 | "lib" 9 | ], 10 | "scripts": { 11 | "start": "NODE_OPTIONS=--openssl-legacy-provider gulp server", 12 | "demo": "SINGLE_DEMO=true DEV_PORT=8000 NODE_OPTIONS=--openssl-legacy-provider gulp server", 13 | "build-dev": "gulp clean && gulp copy && webpack", 14 | "lib": "babel ./src --out-dir ./lib", 15 | "build": "NODE_OPTIONS=--openssl-legacy-provider npm run lib && NODE_OPTIONS=--openssl-legacy-provider gulp dist", 16 | "prepublishOnly": "npm run build", 17 | "lint": "eslint src", 18 | "gen": "node docs/scripts/generateExampleConfigs.js && node docs/scripts/generateExamples.js && xdg-open docs/jquery.html", 19 | "precommit": "lint-staged", 20 | "test": "jest", 21 | "test-watch": "jest --watch", 22 | "clear-jest": "jest --clearCache", 23 | "test-ct": "npm run prepare-playwright && playwright test -c playwright-ct.config.js", 24 | "test-clear": "jest --clearCache && rm -rf ./playwright/.cache", 25 | "prepare-playwright": "rm -rf ./src-jsx && gulp prepare-playwright" 26 | }, 27 | "author": "Kiran Abburi", 28 | "license": "MIT", 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/akiran/react-slick.git" 32 | }, 33 | "keywords": [ 34 | "slick", 35 | "carousel", 36 | "Image slider", 37 | "orbit", 38 | "slider", 39 | "react-component" 40 | ], 41 | "devDependencies": { 42 | "@babel/cli": "^7.0.0", 43 | "@babel/core": "^7.16.0", 44 | "@babel/eslint-parser": "^7.16.3", 45 | "@babel/plugin-proposal-class-properties": "^7.1.0", 46 | "@babel/polyfill": "^7.0.0", 47 | "@babel/preset-env": "^7.1.0", 48 | "@babel/preset-react": "^7.0.0", 49 | "@playwright/experimental-ct-react": "^1.49.1", 50 | "@testing-library/jest-dom": "^5.16.4", 51 | "@testing-library/react": "^16.1.0", 52 | "@testing-library/user-event": "^14.3.0", 53 | "@types/node": "^22.10.2", 54 | "autoprefixer": "^7.1.2", 55 | "babel-core": "^7.0.0-bridge.0", 56 | "babel-jest": "^24.8.0", 57 | "babel-loader": "^8.0.4", 58 | "babel-preset-airbnb": "^2.1.1", 59 | "css-loader": "^2.1.1", 60 | "deepmerge": "^1.1.0", 61 | "del": "^2.2.2", 62 | "es5-shim": "^4.5.9", 63 | "eslint": "^8.4.1", 64 | "eslint-plugin-import": "^2.25.3", 65 | "eslint-plugin-react": "^7.27.1", 66 | "express": "^4.14.0", 67 | "foundation-apps": "^1.2.0", 68 | "gulp": "^4.0.0", 69 | "gulp-rename": "^2.0.0", 70 | "husky": "^0.14.3", 71 | "jest": "^28.1.3", 72 | "jest-environment-jsdom": "^28.1.3", 73 | "js-beautify": "^1.7.5", 74 | "json-loader": "^0.5.4", 75 | "lint-staged": "^12.1.2", 76 | "opn": "^5.4.0", 77 | "postcss-loader": "^1.3.3", 78 | "prettier": "^1.14.3", 79 | "raf": "^3.4.0", 80 | "react": "^19.0.0", 81 | "react-dom": "^19.0.0", 82 | "regenerator-runtime": "^0.14.1", 83 | "sinon": "^2.1.0", 84 | "slick-carousel": "^1.8.1", 85 | "style-loader": "^0.16.1", 86 | "uglifyjs-webpack-plugin": "^2.0.1", 87 | "webpack": "^4.21.0", 88 | "webpack-cli": "^3.1.2", 89 | "webpack-dev-server": "^3.1.9", 90 | "why-did-you-update": "^0.1.1" 91 | }, 92 | "dependencies": { 93 | "classnames": "^2.2.5", 94 | "json2mq": "^0.2.0", 95 | "lodash.debounce": "^4.0.8", 96 | "resize-observer-polyfill": "^1.5.0" 97 | }, 98 | "peerDependencies": { 99 | "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", 100 | "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 101 | }, 102 | "lint-staged": { 103 | "*.{js,json,md}": [ 104 | "prettier --write", 105 | "git add" 106 | ] 107 | }, 108 | "npmName": "react-slick", 109 | "npmFileMap": [ 110 | { 111 | "basePath": "/dist/", 112 | "files": [ 113 | "*.js" 114 | ] 115 | } 116 | ], 117 | "bugs": { 118 | "url": "https://github.com/akiran/react-slick/issues" 119 | }, 120 | "homepage": "https://react-slick.neostack.com", 121 | "collective": { 122 | "type": "opencollective", 123 | "url": "https://opencollective.com/react-slick", 124 | "logo": "https://opencollective.com/opencollective/logo.txt" 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /playwright-ct.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const { defineConfig, devices } = require("@playwright/experimental-ct-react"); 3 | /** 4 | * @see https://playwright.dev/docs/test-configuration 5 | */ 6 | module.exports = defineConfig({ 7 | testDir: "./playwright-tests", 8 | // testDir: "./tests-out", 9 | /* The base directory, relative to the config file, for snapshot files created with toMatchSnapshot and toHaveScreenshot. */ 10 | snapshotDir: "./__snapshots__", 11 | /* Maximum time one test can run for. */ 12 | timeout: 10 * 1000, 13 | /* Run tests in files in parallel */ 14 | fullyParallel: true, 15 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 16 | forbidOnly: !!process.env.CI, 17 | /* Retry on CI only */ 18 | retries: process.env.CI ? 2 : 0, 19 | /* Opt out of parallel tests on CI. */ 20 | workers: process.env.CI ? 1 : undefined, 21 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 22 | reporter: "html", 23 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 24 | use: { 25 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 26 | trace: "on-first-retry", 27 | 28 | /* Port to use for Playwright component endpoint. */ 29 | ctPort: 3100 30 | }, 31 | 32 | /* Configure projects for major browsers */ 33 | projects: [ 34 | { 35 | name: "chromium", 36 | use: { ...devices["Desktop Chrome"] } 37 | } 38 | // { 39 | // name: "firefox", 40 | // use: { ...devices["Desktop Firefox"] } 41 | // }, 42 | // { 43 | // name: "webkit", 44 | // use: { ...devices["Desktop Safari"] } 45 | // } 46 | ] 47 | }); 48 | -------------------------------------------------------------------------------- /playwright-tests/features/responsive/responsive.spec.tsx: -------------------------------------------------------------------------------- 1 | import { test, expect } from "@playwright/experimental-ct-react"; 2 | import Responsive from "./responsive.story"; 3 | 4 | test.use({ viewport: { width: 1200, height: 500 } }); 5 | 6 | async function activeSlidesCount(component) { 7 | return await component.locator(".slick-slide.slick-active").count(); 8 | } 9 | 10 | test("Responsive settings", async ({ mount, page }) => { 11 | const component = await mount(); 12 | 13 | await expect(await activeSlidesCount(component)).toEqual(4); 14 | 15 | await page.setViewportSize({ width: 1000, height: 500 }); 16 | await page.waitForTimeout(10); 17 | await expect(await activeSlidesCount(component)).toEqual(3); 18 | 19 | await page.setViewportSize({ width: 600, height: 500 }); 20 | await page.waitForTimeout(10); 21 | await expect(await activeSlidesCount(component)).toEqual(2); 22 | 23 | await page.setViewportSize({ width: 400, height: 500 }); 24 | await page.waitForTimeout(10); 25 | await expect(await activeSlidesCount(component)).toEqual(1); 26 | 27 | await page.setViewportSize({ width: 600, height: 500 }); 28 | await page.waitForTimeout(10); 29 | await expect(await activeSlidesCount(component)).toEqual(2); 30 | 31 | await page.setViewportSize({ width: 1000, height: 500 }); 32 | await page.waitForTimeout(10); 33 | await expect(await activeSlidesCount(component)).toEqual(3); 34 | 35 | await page.setViewportSize({ width: 1500, height: 500 }); 36 | await page.waitForTimeout(10); 37 | await expect(await activeSlidesCount(component)).toEqual(4); 38 | }); 39 | -------------------------------------------------------------------------------- /playwright-tests/features/responsive/responsive.story.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Slider from "../../../src-jsx"; 3 | 4 | function Responsive() { 5 | var settings = { 6 | dots: true, 7 | infinite: false, 8 | speed: 500, 9 | slidesToShow: 4, 10 | slidesToScroll: 4, 11 | initialSlide: 0, 12 | responsive: [ 13 | { 14 | breakpoint: 1024, 15 | settings: { 16 | slidesToShow: 3, 17 | slidesToScroll: 3, 18 | infinite: true, 19 | dots: true 20 | } 21 | }, 22 | { 23 | breakpoint: 600, 24 | settings: { 25 | slidesToShow: 2, 26 | slidesToScroll: 2, 27 | initialSlide: 2 28 | } 29 | }, 30 | { 31 | breakpoint: 480, 32 | settings: { 33 | slidesToShow: 1, 34 | slidesToScroll: 1 35 | } 36 | } 37 | ] 38 | }; 39 | return ( 40 |
41 | 42 |
43 |

1

44 |
45 |
46 |

2

47 |
48 |
49 |

3

50 |
51 |
52 |

4

53 |
54 |
55 |

5

56 |
57 |
58 |

6

59 |
60 |
61 |

7

62 |
63 |
64 |

8

65 |
66 |
67 |
68 | ); 69 | } 70 | 71 | export default Responsive; 72 | -------------------------------------------------------------------------------- /playwright-tests/regression/fix-1930/fix-1930.spec.tsx: -------------------------------------------------------------------------------- 1 | // Test fix of #1930: Extra height of slider in vertical mode when number of slides is less than or equal to slidesToShow 2 | 3 | import { test, expect } from "@playwright/experimental-ct-react"; 4 | import { VerticalModeFinite, VerticalModeInfinite } from "./fix-1930.story"; 5 | 6 | test.use({ viewport: { width: 500, height: 500 } }); 7 | 8 | test("height check in vertical mode when slides < slidesToShow and finite", async ({ 9 | mount 10 | }) => { 11 | const component = await mount(); 12 | 13 | const track = component.locator(".slick-track").first(); 14 | const box = await track.boundingBox(); 15 | await expect(box.height).toEqual(200); 16 | }); 17 | 18 | test("height check in vertical mode when slides < slidesToShow and infinite", async ({ 19 | mount 20 | }) => { 21 | const component = await mount(); 22 | 23 | const track = component.locator(".slick-track").first(); 24 | const box = await track.boundingBox(); 25 | await expect(box.height).toEqual(200); 26 | }); 27 | -------------------------------------------------------------------------------- /playwright-tests/regression/fix-1930/fix-1930.story.tsx: -------------------------------------------------------------------------------- 1 | import Slider from "../../../src-jsx"; 2 | import React from "react"; 3 | 4 | export function VerticalModeFinite() { 5 | const settings = { 6 | dots: true, 7 | infinite: false, 8 | vertical: true, 9 | slidesToShow: 3 10 | }; 11 | return ( 12 | 13 |
14 |
1
15 |
16 |
17 |
2
18 |
19 |
20 | ); 21 | } 22 | 23 | export function VerticalModeInfinite() { 24 | const settings = { 25 | dots: true, 26 | infinite: true, 27 | vertical: true, 28 | slidesToShow: 3 29 | }; 30 | return ( 31 | 32 |
33 |
1
34 |
35 |
36 |
2
37 |
38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /playwright-tests/sample/sample.spec.tsx: -------------------------------------------------------------------------------- 1 | //Imports the test and expect functions from the Playwright ct-react module 2 | import { test, expect } from "@playwright/experimental-ct-react"; 3 | //Imports the App component to test from the relative ../App path 4 | import App from "./sample.story"; 5 | 6 | //Configures the viewport to a 500x500 size 7 | test.use({ viewport: { width: 500, height: 500 } }); 8 | //Starts a test case named "should work" which will run asynchronously, 9 | //mount function binding is destructured from test parameter 10 | test("Sample playwright test", async ({ mount }) => { 11 | //Uses mount() to instantiate the component in isolation 12 | const component = await mount(); 13 | //Asserts that component contains expected "Learn React" text on it verifying basic render. 14 | await expect(component).toContainText("Learn React"); 15 | }); 16 | -------------------------------------------------------------------------------- /playwright-tests/sample/sample.story.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function App() { 4 | return
Learn React
; 5 | } 6 | -------------------------------------------------------------------------------- /playwright/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Testing Page 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /playwright/index.jsx: -------------------------------------------------------------------------------- 1 | // Import styles, initialize component theme here. 2 | // import '../src/common.css'; 3 | -------------------------------------------------------------------------------- /src/arrows.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import classnames from "classnames"; 5 | import { canGoNext } from "./utils/innerSliderUtils"; 6 | 7 | export class PrevArrow extends React.PureComponent { 8 | clickHandler(options, e) { 9 | if (e) { 10 | e.preventDefault(); 11 | } 12 | this.props.clickHandler(options, e); 13 | } 14 | render() { 15 | let prevClasses = { "slick-arrow": true, "slick-prev": true }; 16 | let prevHandler = this.clickHandler.bind(this, { message: "previous" }); 17 | 18 | if ( 19 | !this.props.infinite && 20 | (this.props.currentSlide === 0 || 21 | this.props.slideCount <= this.props.slidesToShow) 22 | ) { 23 | prevClasses["slick-disabled"] = true; 24 | prevHandler = null; 25 | } 26 | 27 | let prevArrowProps = { 28 | key: "0", 29 | "data-role": "none", 30 | className: classnames(prevClasses), 31 | style: { display: "block" }, 32 | onClick: prevHandler 33 | }; 34 | let customProps = { 35 | currentSlide: this.props.currentSlide, 36 | slideCount: this.props.slideCount 37 | }; 38 | let prevArrow; 39 | 40 | if (this.props.prevArrow) { 41 | prevArrow = React.cloneElement(this.props.prevArrow, { 42 | ...prevArrowProps, 43 | ...customProps 44 | }); 45 | } else { 46 | prevArrow = ( 47 | 51 | ); 52 | } 53 | 54 | return prevArrow; 55 | } 56 | } 57 | 58 | export class NextArrow extends React.PureComponent { 59 | clickHandler(options, e) { 60 | if (e) { 61 | e.preventDefault(); 62 | } 63 | this.props.clickHandler(options, e); 64 | } 65 | render() { 66 | let nextClasses = { "slick-arrow": true, "slick-next": true }; 67 | let nextHandler = this.clickHandler.bind(this, { message: "next" }); 68 | 69 | if (!canGoNext(this.props)) { 70 | nextClasses["slick-disabled"] = true; 71 | nextHandler = null; 72 | } 73 | 74 | let nextArrowProps = { 75 | key: "1", 76 | "data-role": "none", 77 | className: classnames(nextClasses), 78 | style: { display: "block" }, 79 | onClick: nextHandler 80 | }; 81 | let customProps = { 82 | currentSlide: this.props.currentSlide, 83 | slideCount: this.props.slideCount 84 | }; 85 | let nextArrow; 86 | 87 | if (this.props.nextArrow) { 88 | nextArrow = React.cloneElement(this.props.nextArrow, { 89 | ...nextArrowProps, 90 | ...customProps 91 | }); 92 | } else { 93 | nextArrow = ( 94 | 98 | ); 99 | } 100 | 101 | return nextArrow; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/default-props.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | let defaultProps = { 4 | accessibility: true, 5 | adaptiveHeight: false, 6 | afterChange: null, 7 | appendDots: dots =>
    {dots}
, 8 | arrows: true, 9 | autoplay: false, 10 | autoplaySpeed: 3000, 11 | beforeChange: null, 12 | centerMode: false, 13 | centerPadding: "50px", 14 | className: "", 15 | cssEase: "ease", 16 | customPaging: i => , 17 | dots: false, 18 | dotsClass: "slick-dots", 19 | draggable: true, 20 | easing: "linear", 21 | edgeFriction: 0.35, 22 | fade: false, 23 | focusOnSelect: false, 24 | infinite: true, 25 | initialSlide: 0, 26 | lazyLoad: null, 27 | nextArrow: null, 28 | onEdge: null, 29 | onInit: null, 30 | onLazyLoadError: null, 31 | onReInit: null, 32 | pauseOnDotsHover: false, 33 | pauseOnFocus: false, 34 | pauseOnHover: true, 35 | prevArrow: null, 36 | responsive: null, 37 | rows: 1, 38 | rtl: false, 39 | slide: "div", 40 | slidesPerRow: 1, 41 | slidesToScroll: 1, 42 | slidesToShow: 1, 43 | speed: 500, 44 | swipe: true, 45 | swipeEvent: null, 46 | swipeToSlide: false, 47 | touchMove: true, 48 | touchThreshold: 5, 49 | useCSS: true, 50 | useTransform: true, 51 | variableWidth: false, 52 | vertical: false, 53 | verticalSwiping: false, 54 | waitForAnimate: true, 55 | asNavFor: null, 56 | unslick: false 57 | }; 58 | 59 | export default defaultProps; 60 | -------------------------------------------------------------------------------- /src/dots.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import classnames from "classnames"; 5 | import { clamp } from "./utils/innerSliderUtils"; 6 | 7 | const getDotCount = spec => { 8 | let dots; 9 | 10 | if (spec.infinite) { 11 | dots = Math.ceil(spec.slideCount / spec.slidesToScroll); 12 | } else { 13 | dots = 14 | Math.ceil((spec.slideCount - spec.slidesToShow) / spec.slidesToScroll) + 15 | 1; 16 | } 17 | 18 | return dots; 19 | }; 20 | 21 | export class Dots extends React.PureComponent { 22 | clickHandler(options, e) { 23 | // In Autoplay the focus stays on clicked button even after transition 24 | // to next slide. That only goes away by click somewhere outside 25 | e.preventDefault(); 26 | this.props.clickHandler(options); 27 | } 28 | render() { 29 | const { 30 | onMouseEnter, 31 | onMouseOver, 32 | onMouseLeave, 33 | infinite, 34 | slidesToScroll, 35 | slidesToShow, 36 | slideCount, 37 | currentSlide 38 | } = this.props; 39 | let dotCount = getDotCount({ 40 | slideCount, 41 | slidesToScroll, 42 | slidesToShow, 43 | infinite 44 | }); 45 | 46 | const mouseEvents = { onMouseEnter, onMouseOver, onMouseLeave }; 47 | let dots = []; 48 | for (let i = 0; i < dotCount; i++) { 49 | let _rightBound = (i + 1) * slidesToScroll - 1; 50 | let rightBound = infinite 51 | ? _rightBound 52 | : clamp(_rightBound, 0, slideCount - 1); 53 | let _leftBound = rightBound - (slidesToScroll - 1); 54 | let leftBound = infinite 55 | ? _leftBound 56 | : clamp(_leftBound, 0, slideCount - 1); 57 | 58 | let className = classnames({ 59 | "slick-active": infinite 60 | ? currentSlide >= leftBound && currentSlide <= rightBound 61 | : currentSlide === leftBound 62 | }); 63 | 64 | let dotOptions = { 65 | message: "dots", 66 | index: i, 67 | slidesToScroll, 68 | currentSlide 69 | }; 70 | 71 | let onClick = this.clickHandler.bind(this, dotOptions); 72 | dots = dots.concat( 73 |
  • 74 | {React.cloneElement(this.props.customPaging(i), { onClick })} 75 |
  • 76 | ); 77 | } 78 | 79 | return React.cloneElement(this.props.appendDots(dots), { 80 | className: this.props.dotsClass, 81 | ...mouseEvents 82 | }); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Slider from "./slider"; 2 | 3 | export default Slider; 4 | -------------------------------------------------------------------------------- /src/initial-state.js: -------------------------------------------------------------------------------- 1 | const initialState = { 2 | animating: false, 3 | autoplaying: null, 4 | currentDirection: 0, 5 | currentLeft: null, 6 | currentSlide: 0, 7 | direction: 1, 8 | dragging: false, 9 | edgeDragged: false, 10 | initialized: false, 11 | lazyLoadedList: [], 12 | listHeight: null, 13 | listWidth: null, 14 | scrolling: false, 15 | slideCount: null, 16 | slideHeight: null, 17 | slideWidth: null, 18 | swipeLeft: null, 19 | swiped: false, // used by swipeEvent. differentites between touch and swipe. 20 | swiping: false, 21 | touchObject: { startX: 0, startY: 0, curX: 0, curY: 0 }, 22 | trackStyle: {}, 23 | trackWidth: 0, 24 | targetSlide: 0 25 | }; 26 | 27 | export default initialState; 28 | -------------------------------------------------------------------------------- /src/inner-slider.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import initialState from "./initial-state"; 5 | import debounce from "lodash.debounce"; 6 | import classnames from "classnames"; 7 | import { 8 | getOnDemandLazySlides, 9 | extractObject, 10 | initializedState, 11 | getHeight, 12 | canGoNext, 13 | slideHandler, 14 | changeSlide, 15 | keyHandler, 16 | swipeStart, 17 | swipeMove, 18 | swipeEnd, 19 | getPreClones, 20 | getPostClones, 21 | getTrackLeft, 22 | getTrackCSS 23 | } from "./utils/innerSliderUtils"; 24 | 25 | import { Track } from "./track"; 26 | import { Dots } from "./dots"; 27 | import { PrevArrow, NextArrow } from "./arrows"; 28 | import ResizeObserver from "resize-observer-polyfill"; 29 | 30 | export class InnerSlider extends React.Component { 31 | constructor(props) { 32 | super(props); 33 | this.list = null; 34 | this.track = null; 35 | this.state = { 36 | ...initialState, 37 | currentSlide: this.props.initialSlide, 38 | targetSlide: this.props.initialSlide ? this.props.initialSlide : 0, 39 | slideCount: React.Children.count(this.props.children) 40 | }; 41 | this.callbackTimers = []; 42 | this.clickable = true; 43 | this.debouncedResize = null; 44 | const ssrState = this.ssrInit(); 45 | this.state = { ...this.state, ...ssrState }; 46 | } 47 | listRefHandler = ref => (this.list = ref); 48 | trackRefHandler = ref => (this.track = ref); 49 | adaptHeight = () => { 50 | if (this.props.adaptiveHeight && this.list) { 51 | const elem = this.list.querySelector( 52 | `[data-index="${this.state.currentSlide}"]` 53 | ); 54 | this.list.style.height = getHeight(elem) + "px"; 55 | } 56 | }; 57 | componentDidMount = () => { 58 | this.props.onInit && this.props.onInit(); 59 | if (this.props.lazyLoad) { 60 | let slidesToLoad = getOnDemandLazySlides({ 61 | ...this.props, 62 | ...this.state 63 | }); 64 | if (slidesToLoad.length > 0) { 65 | this.setState(prevState => ({ 66 | lazyLoadedList: prevState.lazyLoadedList.concat(slidesToLoad) 67 | })); 68 | if (this.props.onLazyLoad) { 69 | this.props.onLazyLoad(slidesToLoad); 70 | } 71 | } 72 | } 73 | let spec = { listRef: this.list, trackRef: this.track, ...this.props }; 74 | this.updateState(spec, true, () => { 75 | this.adaptHeight(); 76 | this.props.autoplay && this.autoPlay("update"); 77 | }); 78 | if (this.props.lazyLoad === "progressive") { 79 | this.lazyLoadTimer = setInterval(this.progressiveLazyLoad, 1000); 80 | } 81 | this.ro = new ResizeObserver(() => { 82 | if (this.state.animating) { 83 | this.onWindowResized(false); // don't set trackStyle hence don't break animation 84 | this.callbackTimers.push( 85 | setTimeout(() => this.onWindowResized(), this.props.speed) 86 | ); 87 | } else { 88 | this.onWindowResized(); 89 | } 90 | }); 91 | this.ro.observe(this.list); 92 | document.querySelectorAll && 93 | Array.prototype.forEach.call( 94 | document.querySelectorAll(".slick-slide"), 95 | slide => { 96 | slide.onfocus = this.props.pauseOnFocus ? this.onSlideFocus : null; 97 | slide.onblur = this.props.pauseOnFocus ? this.onSlideBlur : null; 98 | } 99 | ); 100 | if (window.addEventListener) { 101 | window.addEventListener("resize", this.onWindowResized); 102 | } else { 103 | window.attachEvent("onresize", this.onWindowResized); 104 | } 105 | }; 106 | componentWillUnmount = () => { 107 | if (this.animationEndCallback) { 108 | clearTimeout(this.animationEndCallback); 109 | } 110 | if (this.lazyLoadTimer) { 111 | clearInterval(this.lazyLoadTimer); 112 | } 113 | if (this.callbackTimers.length) { 114 | this.callbackTimers.forEach(timer => clearTimeout(timer)); 115 | this.callbackTimers = []; 116 | } 117 | if (window.addEventListener) { 118 | window.removeEventListener("resize", this.onWindowResized); 119 | } else { 120 | window.detachEvent("onresize", this.onWindowResized); 121 | } 122 | if (this.autoplayTimer) { 123 | clearInterval(this.autoplayTimer); 124 | } 125 | this.ro.disconnect(); 126 | }; 127 | 128 | didPropsChange(prevProps) { 129 | let setTrackStyle = false; 130 | for (let key of Object.keys(this.props)) { 131 | if (!prevProps.hasOwnProperty(key)) { 132 | setTrackStyle = true; 133 | break; 134 | } 135 | if ( 136 | typeof prevProps[key] === "object" || 137 | typeof prevProps[key] === "function" || 138 | isNaN(prevProps[key]) 139 | ) { 140 | continue; 141 | } 142 | if (prevProps[key] !== this.props[key]) { 143 | setTrackStyle = true; 144 | break; 145 | } 146 | } 147 | return ( 148 | setTrackStyle || 149 | React.Children.count(this.props.children) !== 150 | React.Children.count(prevProps.children) 151 | ); 152 | } 153 | 154 | componentDidUpdate = prevProps => { 155 | this.checkImagesLoad(); 156 | this.props.onReInit && this.props.onReInit(); 157 | if (this.props.lazyLoad) { 158 | let slidesToLoad = getOnDemandLazySlides({ 159 | ...this.props, 160 | ...this.state 161 | }); 162 | if (slidesToLoad.length > 0) { 163 | this.setState(prevState => ({ 164 | lazyLoadedList: prevState.lazyLoadedList.concat(slidesToLoad) 165 | })); 166 | if (this.props.onLazyLoad) { 167 | this.props.onLazyLoad(slidesToLoad); 168 | } 169 | } 170 | } 171 | // if (this.props.onLazyLoad) { 172 | // this.props.onLazyLoad([leftMostSlide]) 173 | // } 174 | this.adaptHeight(); 175 | let spec = { 176 | listRef: this.list, 177 | trackRef: this.track, 178 | ...this.props, 179 | ...this.state 180 | }; 181 | const setTrackStyle = this.didPropsChange(prevProps); 182 | setTrackStyle && 183 | this.updateState(spec, setTrackStyle, () => { 184 | if ( 185 | this.state.currentSlide >= React.Children.count(this.props.children) 186 | ) { 187 | this.changeSlide({ 188 | message: "index", 189 | index: 190 | React.Children.count(this.props.children) - 191 | this.props.slidesToShow, 192 | currentSlide: this.state.currentSlide 193 | }); 194 | } 195 | if (this.props.autoplay) { 196 | this.autoPlay("update"); 197 | } else { 198 | this.pause("paused"); 199 | } 200 | }); 201 | }; 202 | onWindowResized = setTrackStyle => { 203 | if (this.debouncedResize) this.debouncedResize.cancel(); 204 | this.debouncedResize = debounce(() => this.resizeWindow(setTrackStyle), 50); 205 | this.debouncedResize(); 206 | }; 207 | resizeWindow = (setTrackStyle = true) => { 208 | const isTrackMounted = Boolean(this.track && this.track.node); 209 | // prevent warning: setting state on unmounted component (server side rendering) 210 | if (!isTrackMounted) return; 211 | let spec = { 212 | listRef: this.list, 213 | trackRef: this.track, 214 | ...this.props, 215 | ...this.state 216 | }; 217 | this.updateState(spec, setTrackStyle, () => { 218 | if (this.props.autoplay) this.autoPlay("update"); 219 | else this.pause("paused"); 220 | }); 221 | // animating state should be cleared while resizing, otherwise autoplay stops working 222 | this.setState({ 223 | animating: false 224 | }); 225 | clearTimeout(this.animationEndCallback); 226 | delete this.animationEndCallback; 227 | }; 228 | updateState = (spec, setTrackStyle, callback) => { 229 | let updatedState = initializedState(spec); 230 | spec = { ...spec, ...updatedState, slideIndex: updatedState.currentSlide }; 231 | let targetLeft = getTrackLeft(spec); 232 | spec = { ...spec, left: targetLeft }; 233 | let trackStyle = getTrackCSS(spec); 234 | if ( 235 | setTrackStyle || 236 | React.Children.count(this.props.children) !== 237 | React.Children.count(spec.children) 238 | ) { 239 | updatedState["trackStyle"] = trackStyle; 240 | } 241 | this.setState(updatedState, callback); 242 | }; 243 | 244 | ssrInit = () => { 245 | if (this.props.variableWidth) { 246 | let trackWidth = 0, 247 | trackLeft = 0; 248 | let childrenWidths = []; 249 | let preClones = getPreClones({ 250 | ...this.props, 251 | ...this.state, 252 | slideCount: this.props.children.length 253 | }); 254 | let postClones = getPostClones({ 255 | ...this.props, 256 | ...this.state, 257 | slideCount: this.props.children.length 258 | }); 259 | this.props.children.forEach(child => { 260 | childrenWidths.push(child.props.style.width); 261 | trackWidth += child.props.style.width; 262 | }); 263 | for (let i = 0; i < preClones; i++) { 264 | trackLeft += childrenWidths[childrenWidths.length - 1 - i]; 265 | trackWidth += childrenWidths[childrenWidths.length - 1 - i]; 266 | } 267 | for (let i = 0; i < postClones; i++) { 268 | trackWidth += childrenWidths[i]; 269 | } 270 | for (let i = 0; i < this.state.currentSlide; i++) { 271 | trackLeft += childrenWidths[i]; 272 | } 273 | let trackStyle = { 274 | width: trackWidth + "px", 275 | left: -trackLeft + "px" 276 | }; 277 | if (this.props.centerMode) { 278 | let currentWidth = `${childrenWidths[this.state.currentSlide]}px`; 279 | trackStyle.left = `calc(${trackStyle.left} + (100% - ${currentWidth}) / 2 ) `; 280 | } 281 | return { 282 | trackStyle 283 | }; 284 | } 285 | let childrenCount = React.Children.count(this.props.children); 286 | const spec = { ...this.props, ...this.state, slideCount: childrenCount }; 287 | let slideCount = getPreClones(spec) + getPostClones(spec) + childrenCount; 288 | let trackWidth = (100 / this.props.slidesToShow) * slideCount; 289 | let slideWidth = 100 / slideCount; 290 | let trackLeft = 291 | (-slideWidth * 292 | (getPreClones(spec) + this.state.currentSlide) * 293 | trackWidth) / 294 | 100; 295 | if (this.props.centerMode) { 296 | trackLeft += (100 - (slideWidth * trackWidth) / 100) / 2; 297 | } 298 | let trackStyle = { 299 | width: trackWidth + "%", 300 | left: trackLeft + "%" 301 | }; 302 | return { 303 | slideWidth: slideWidth + "%", 304 | trackStyle: trackStyle 305 | }; 306 | }; 307 | checkImagesLoad = () => { 308 | let images = 309 | (this.list && 310 | this.list.querySelectorAll && 311 | this.list.querySelectorAll(".slick-slide img")) || 312 | []; 313 | let imagesCount = images.length, 314 | loadedCount = 0; 315 | Array.prototype.forEach.call(images, image => { 316 | const handler = () => 317 | ++loadedCount && loadedCount >= imagesCount && this.onWindowResized(); 318 | if (!image.onclick) { 319 | image.onclick = () => image.parentNode.focus(); 320 | } else { 321 | const prevClickHandler = image.onclick; 322 | image.onclick = e => { 323 | prevClickHandler(e); 324 | image.parentNode.focus(); 325 | }; 326 | } 327 | if (!image.onload) { 328 | if (this.props.lazyLoad) { 329 | image.onload = () => { 330 | this.adaptHeight(); 331 | this.callbackTimers.push( 332 | setTimeout(this.onWindowResized, this.props.speed) 333 | ); 334 | }; 335 | } else { 336 | image.onload = handler; 337 | image.onerror = () => { 338 | handler(); 339 | this.props.onLazyLoadError && this.props.onLazyLoadError(); 340 | }; 341 | } 342 | } 343 | }); 344 | }; 345 | progressiveLazyLoad = () => { 346 | let slidesToLoad = []; 347 | const spec = { ...this.props, ...this.state }; 348 | for ( 349 | let index = this.state.currentSlide; 350 | index < this.state.slideCount + getPostClones(spec); 351 | index++ 352 | ) { 353 | if (this.state.lazyLoadedList.indexOf(index) < 0) { 354 | slidesToLoad.push(index); 355 | break; 356 | } 357 | } 358 | for ( 359 | let index = this.state.currentSlide - 1; 360 | index >= -getPreClones(spec); 361 | index-- 362 | ) { 363 | if (this.state.lazyLoadedList.indexOf(index) < 0) { 364 | slidesToLoad.push(index); 365 | break; 366 | } 367 | } 368 | if (slidesToLoad.length > 0) { 369 | this.setState(state => ({ 370 | lazyLoadedList: state.lazyLoadedList.concat(slidesToLoad) 371 | })); 372 | if (this.props.onLazyLoad) { 373 | this.props.onLazyLoad(slidesToLoad); 374 | } 375 | } else { 376 | if (this.lazyLoadTimer) { 377 | clearInterval(this.lazyLoadTimer); 378 | delete this.lazyLoadTimer; 379 | } 380 | } 381 | }; 382 | slideHandler = (index, dontAnimate = false) => { 383 | const { 384 | asNavFor, 385 | beforeChange, 386 | onLazyLoad, 387 | speed, 388 | afterChange 389 | } = this.props; 390 | // capture currentslide before state is updated 391 | const currentSlide = this.state.currentSlide; 392 | let { state, nextState } = slideHandler({ 393 | index, 394 | ...this.props, 395 | ...this.state, 396 | trackRef: this.track, 397 | useCSS: this.props.useCSS && !dontAnimate 398 | }); 399 | if (!state) return; 400 | beforeChange && beforeChange(currentSlide, state.currentSlide); 401 | let slidesToLoad = state.lazyLoadedList.filter( 402 | value => this.state.lazyLoadedList.indexOf(value) < 0 403 | ); 404 | onLazyLoad && slidesToLoad.length > 0 && onLazyLoad(slidesToLoad); 405 | if (!this.props.waitForAnimate && this.animationEndCallback) { 406 | clearTimeout(this.animationEndCallback); 407 | afterChange && afterChange(currentSlide); 408 | delete this.animationEndCallback; 409 | } 410 | this.setState(state, () => { 411 | // asNavForIndex check is to avoid recursive calls of slideHandler in waitForAnimate=false mode 412 | if (asNavFor && this.asNavForIndex !== index) { 413 | this.asNavForIndex = index; 414 | asNavFor.innerSlider.slideHandler(index); 415 | } 416 | if (!nextState) return; 417 | this.animationEndCallback = setTimeout(() => { 418 | const { animating, ...firstBatch } = nextState; 419 | this.setState(firstBatch, () => { 420 | this.callbackTimers.push( 421 | setTimeout(() => this.setState({ animating }), 10) 422 | ); 423 | afterChange && afterChange(state.currentSlide); 424 | delete this.animationEndCallback; 425 | }); 426 | }, speed); 427 | }); 428 | }; 429 | changeSlide = (options, dontAnimate = false) => { 430 | const spec = { ...this.props, ...this.state }; 431 | let targetSlide = changeSlide(spec, options); 432 | if (targetSlide !== 0 && !targetSlide) return; 433 | if (dontAnimate === true) { 434 | this.slideHandler(targetSlide, dontAnimate); 435 | } else { 436 | this.slideHandler(targetSlide); 437 | } 438 | this.props.autoplay && this.autoPlay("update"); 439 | if (this.props.focusOnSelect) { 440 | const nodes = this.list.querySelectorAll(".slick-current"); 441 | nodes[0] && nodes[0].focus(); 442 | } 443 | }; 444 | clickHandler = e => { 445 | if (this.clickable === false) { 446 | e.stopPropagation(); 447 | e.preventDefault(); 448 | } 449 | this.clickable = true; 450 | }; 451 | keyHandler = e => { 452 | let dir = keyHandler(e, this.props.accessibility, this.props.rtl); 453 | dir !== "" && this.changeSlide({ message: dir }); 454 | }; 455 | selectHandler = options => { 456 | this.changeSlide(options); 457 | }; 458 | disableBodyScroll = () => { 459 | const preventDefault = e => { 460 | e = e || window.event; 461 | if (e.preventDefault) e.preventDefault(); 462 | e.returnValue = false; 463 | }; 464 | window.ontouchmove = preventDefault; 465 | }; 466 | enableBodyScroll = () => { 467 | window.ontouchmove = null; 468 | }; 469 | swipeStart = e => { 470 | if (this.props.verticalSwiping) { 471 | this.disableBodyScroll(); 472 | } 473 | let state = swipeStart(e, this.props.swipe, this.props.draggable); 474 | state !== "" && this.setState(state); 475 | }; 476 | swipeMove = e => { 477 | let state = swipeMove(e, { 478 | ...this.props, 479 | ...this.state, 480 | trackRef: this.track, 481 | listRef: this.list, 482 | slideIndex: this.state.currentSlide 483 | }); 484 | if (!state) return; 485 | if (state["swiping"]) { 486 | this.clickable = false; 487 | } 488 | this.setState(state); 489 | }; 490 | swipeEnd = e => { 491 | let state = swipeEnd(e, { 492 | ...this.props, 493 | ...this.state, 494 | trackRef: this.track, 495 | listRef: this.list, 496 | slideIndex: this.state.currentSlide 497 | }); 498 | if (!state) return; 499 | let triggerSlideHandler = state["triggerSlideHandler"]; 500 | delete state["triggerSlideHandler"]; 501 | this.setState(state); 502 | if (triggerSlideHandler === undefined) return; 503 | this.slideHandler(triggerSlideHandler); 504 | if (this.props.verticalSwiping) { 505 | this.enableBodyScroll(); 506 | } 507 | }; 508 | touchEnd = e => { 509 | this.swipeEnd(e); 510 | this.clickable = true; 511 | }; 512 | slickPrev = () => { 513 | // this and fellow methods are wrapped in setTimeout 514 | // to make sure initialize setState has happened before 515 | // any of such methods are called 516 | this.callbackTimers.push( 517 | setTimeout(() => this.changeSlide({ message: "previous" }), 0) 518 | ); 519 | }; 520 | slickNext = () => { 521 | this.callbackTimers.push( 522 | setTimeout(() => this.changeSlide({ message: "next" }), 0) 523 | ); 524 | }; 525 | slickGoTo = (slide, dontAnimate = false) => { 526 | slide = Number(slide); 527 | if (isNaN(slide)) return ""; 528 | this.callbackTimers.push( 529 | setTimeout( 530 | () => 531 | this.changeSlide( 532 | { 533 | message: "index", 534 | index: slide, 535 | currentSlide: this.state.currentSlide 536 | }, 537 | dontAnimate 538 | ), 539 | 0 540 | ) 541 | ); 542 | }; 543 | play = () => { 544 | var nextIndex; 545 | if (this.props.rtl) { 546 | nextIndex = this.state.currentSlide - this.props.slidesToScroll; 547 | } else { 548 | if (canGoNext({ ...this.props, ...this.state })) { 549 | nextIndex = this.state.currentSlide + this.props.slidesToScroll; 550 | } else { 551 | return false; 552 | } 553 | } 554 | 555 | this.slideHandler(nextIndex); 556 | }; 557 | 558 | autoPlay = playType => { 559 | if (this.autoplayTimer) { 560 | clearInterval(this.autoplayTimer); 561 | } 562 | const autoplaying = this.state.autoplaying; 563 | if (playType === "update") { 564 | if ( 565 | autoplaying === "hovered" || 566 | autoplaying === "focused" || 567 | autoplaying === "paused" 568 | ) { 569 | return; 570 | } 571 | } else if (playType === "leave") { 572 | if (autoplaying === "paused" || autoplaying === "focused") { 573 | return; 574 | } 575 | } else if (playType === "blur") { 576 | if (autoplaying === "paused" || autoplaying === "hovered") { 577 | return; 578 | } 579 | } 580 | this.autoplayTimer = setInterval(this.play, this.props.autoplaySpeed + 50); 581 | this.setState({ autoplaying: "playing" }); 582 | }; 583 | pause = pauseType => { 584 | if (this.autoplayTimer) { 585 | clearInterval(this.autoplayTimer); 586 | this.autoplayTimer = null; 587 | } 588 | const autoplaying = this.state.autoplaying; 589 | if (pauseType === "paused") { 590 | this.setState({ autoplaying: "paused" }); 591 | } else if (pauseType === "focused") { 592 | if (autoplaying === "hovered" || autoplaying === "playing") { 593 | this.setState({ autoplaying: "focused" }); 594 | } 595 | } else { 596 | // pauseType is 'hovered' 597 | if (autoplaying === "playing") { 598 | this.setState({ autoplaying: "hovered" }); 599 | } 600 | } 601 | }; 602 | onDotsOver = () => this.props.autoplay && this.pause("hovered"); 603 | onDotsLeave = () => 604 | this.props.autoplay && 605 | this.state.autoplaying === "hovered" && 606 | this.autoPlay("leave"); 607 | onTrackOver = () => this.props.autoplay && this.pause("hovered"); 608 | onTrackLeave = () => 609 | this.props.autoplay && 610 | this.state.autoplaying === "hovered" && 611 | this.autoPlay("leave"); 612 | onSlideFocus = () => this.props.autoplay && this.pause("focused"); 613 | onSlideBlur = () => 614 | this.props.autoplay && 615 | this.state.autoplaying === "focused" && 616 | this.autoPlay("blur"); 617 | 618 | render = () => { 619 | var className = classnames("slick-slider", this.props.className, { 620 | "slick-vertical": this.props.vertical, 621 | "slick-initialized": true 622 | }); 623 | let spec = { ...this.props, ...this.state }; 624 | let trackProps = extractObject(spec, [ 625 | "fade", 626 | "cssEase", 627 | "speed", 628 | "infinite", 629 | "centerMode", 630 | "focusOnSelect", 631 | "currentSlide", 632 | "lazyLoad", 633 | "lazyLoadedList", 634 | "rtl", 635 | "slideWidth", 636 | "slideHeight", 637 | "listHeight", 638 | "vertical", 639 | "slidesToShow", 640 | "slidesToScroll", 641 | "slideCount", 642 | "trackStyle", 643 | "variableWidth", 644 | "unslick", 645 | "centerPadding", 646 | "targetSlide", 647 | "useCSS" 648 | ]); 649 | const { pauseOnHover } = this.props; 650 | trackProps = { 651 | ...trackProps, 652 | onMouseEnter: pauseOnHover ? this.onTrackOver : null, 653 | onMouseLeave: pauseOnHover ? this.onTrackLeave : null, 654 | onMouseOver: pauseOnHover ? this.onTrackOver : null, 655 | focusOnSelect: 656 | this.props.focusOnSelect && this.clickable ? this.selectHandler : null 657 | }; 658 | 659 | var dots; 660 | if ( 661 | this.props.dots === true && 662 | this.state.slideCount >= this.props.slidesToShow 663 | ) { 664 | let dotProps = extractObject(spec, [ 665 | "dotsClass", 666 | "slideCount", 667 | "slidesToShow", 668 | "currentSlide", 669 | "slidesToScroll", 670 | "clickHandler", 671 | "children", 672 | "customPaging", 673 | "infinite", 674 | "appendDots" 675 | ]); 676 | const { pauseOnDotsHover } = this.props; 677 | dotProps = { 678 | ...dotProps, 679 | clickHandler: this.changeSlide, 680 | onMouseEnter: pauseOnDotsHover ? this.onDotsLeave : null, 681 | onMouseOver: pauseOnDotsHover ? this.onDotsOver : null, 682 | onMouseLeave: pauseOnDotsHover ? this.onDotsLeave : null 683 | }; 684 | dots = ; 685 | } 686 | 687 | var prevArrow, nextArrow; 688 | let arrowProps = extractObject(spec, [ 689 | "infinite", 690 | "centerMode", 691 | "currentSlide", 692 | "slideCount", 693 | "slidesToShow", 694 | "prevArrow", 695 | "nextArrow" 696 | ]); 697 | arrowProps.clickHandler = this.changeSlide; 698 | 699 | if (this.props.arrows) { 700 | prevArrow = ; 701 | nextArrow = ; 702 | } 703 | 704 | var verticalHeightStyle = null; 705 | 706 | if (this.props.vertical) { 707 | verticalHeightStyle = { 708 | height: this.state.listHeight 709 | }; 710 | } 711 | 712 | var centerPaddingStyle = null; 713 | 714 | if (this.props.vertical === false) { 715 | if (this.props.centerMode === true) { 716 | centerPaddingStyle = { 717 | padding: "0px " + this.props.centerPadding 718 | }; 719 | } 720 | } else { 721 | if (this.props.centerMode === true) { 722 | centerPaddingStyle = { 723 | padding: this.props.centerPadding + " 0px" 724 | }; 725 | } 726 | } 727 | 728 | const listStyle = { ...verticalHeightStyle, ...centerPaddingStyle }; 729 | const touchMove = this.props.touchMove; 730 | let listProps = { 731 | className: "slick-list", 732 | style: listStyle, 733 | onClick: this.clickHandler, 734 | onMouseDown: touchMove ? this.swipeStart : null, 735 | onMouseMove: this.state.dragging && touchMove ? this.swipeMove : null, 736 | onMouseUp: touchMove ? this.swipeEnd : null, 737 | onMouseLeave: this.state.dragging && touchMove ? this.swipeEnd : null, 738 | onTouchStart: touchMove ? this.swipeStart : null, 739 | onTouchMove: this.state.dragging && touchMove ? this.swipeMove : null, 740 | onTouchEnd: touchMove ? this.touchEnd : null, 741 | onTouchCancel: this.state.dragging && touchMove ? this.swipeEnd : null, 742 | onKeyDown: this.props.accessibility ? this.keyHandler : null 743 | }; 744 | 745 | let innerSliderProps = { 746 | className: className, 747 | dir: "ltr", 748 | style: this.props.style 749 | }; 750 | 751 | if (this.props.unslick) { 752 | listProps = { className: "slick-list" }; 753 | innerSliderProps = { className, style: this.props.style }; 754 | } 755 | return ( 756 |
    757 | {!this.props.unslick ? prevArrow : ""} 758 |
    759 | 760 | {this.props.children} 761 | 762 |
    763 | {!this.props.unslick ? nextArrow : ""} 764 | {!this.props.unslick ? dots : ""} 765 |
    766 | ); 767 | }; 768 | } 769 | -------------------------------------------------------------------------------- /src/slider.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import { InnerSlider } from "./inner-slider"; 5 | import json2mq from "json2mq"; 6 | import defaultProps from "./default-props"; 7 | import { canUseDOM, filterSettings } from "./utils/innerSliderUtils"; 8 | 9 | export default class Slider extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | this.state = { 13 | breakpoint: null 14 | }; 15 | this._responsiveMediaHandlers = []; 16 | } 17 | 18 | innerSliderRefHandler = ref => (this.innerSlider = ref); 19 | 20 | media(query, handler) { 21 | // javascript handler for css media query 22 | const mql = window.matchMedia(query); 23 | const listener = ({ matches }) => { 24 | if (matches) { 25 | handler(); 26 | } 27 | }; 28 | mql.addListener(listener); 29 | this._responsiveMediaHandlers.push({ mql, query, listener }); 30 | } 31 | 32 | // handles responsive breakpoints 33 | componentDidMount() { 34 | // performance monitoring 35 | //if (process.env.NODE_ENV !== 'production') { 36 | //const { whyDidYouUpdate } = require('why-did-you-update') 37 | //whyDidYouUpdate(React) 38 | //} 39 | if (this.props.responsive) { 40 | let breakpoints = this.props.responsive.map( 41 | breakpt => breakpt.breakpoint 42 | ); 43 | // sort them in increasing order of their numerical value 44 | breakpoints.sort((x, y) => x - y); 45 | 46 | breakpoints.forEach((breakpoint, index) => { 47 | // media query for each breakpoint 48 | let bQuery; 49 | if (index === 0) { 50 | bQuery = json2mq({ minWidth: 0, maxWidth: breakpoint }); 51 | } else { 52 | bQuery = json2mq({ 53 | minWidth: breakpoints[index - 1] + 1, 54 | maxWidth: breakpoint 55 | }); 56 | } 57 | // when not using server side rendering 58 | canUseDOM() && 59 | this.media(bQuery, () => { 60 | this.setState({ breakpoint: breakpoint }); 61 | }); 62 | }); 63 | 64 | // Register media query for full screen. Need to support resize from small to large 65 | // convert javascript object to media query string 66 | let query = json2mq({ minWidth: breakpoints.slice(-1)[0] }); 67 | 68 | canUseDOM() && 69 | this.media(query, () => { 70 | this.setState({ breakpoint: null }); 71 | }); 72 | } 73 | } 74 | 75 | componentWillUnmount() { 76 | this._responsiveMediaHandlers.forEach(function(obj) { 77 | obj.mql.removeListener(obj.listener); 78 | }); 79 | } 80 | 81 | slickPrev = () => this.innerSlider.slickPrev(); 82 | 83 | slickNext = () => this.innerSlider.slickNext(); 84 | 85 | slickGoTo = (slide, dontAnimate = false) => 86 | this.innerSlider.slickGoTo(slide, dontAnimate); 87 | 88 | slickPause = () => this.innerSlider.pause("paused"); 89 | 90 | slickPlay = () => this.innerSlider.autoPlay("play"); 91 | 92 | render() { 93 | var settings; 94 | var newProps; 95 | if (this.state.breakpoint) { 96 | newProps = this.props.responsive.filter( 97 | resp => resp.breakpoint === this.state.breakpoint 98 | ); 99 | settings = 100 | newProps[0].settings === "unslick" 101 | ? "unslick" 102 | : { ...defaultProps, ...this.props, ...newProps[0].settings }; 103 | } else { 104 | settings = { ...defaultProps, ...this.props }; 105 | } 106 | 107 | // force scrolling by one if centerMode is on 108 | if (settings.centerMode) { 109 | if ( 110 | settings.slidesToScroll > 1 && 111 | process.env.NODE_ENV !== "production" 112 | ) { 113 | console.warn( 114 | `slidesToScroll should be equal to 1 in centerMode, you are using ${settings.slidesToScroll}` 115 | ); 116 | } 117 | settings.slidesToScroll = 1; 118 | } 119 | // force showing one slide and scrolling by one if the fade mode is on 120 | if (settings.fade) { 121 | if (settings.slidesToShow > 1 && process.env.NODE_ENV !== "production") { 122 | console.warn( 123 | `slidesToShow should be equal to 1 when fade is true, you're using ${settings.slidesToShow}` 124 | ); 125 | } 126 | if ( 127 | settings.slidesToScroll > 1 && 128 | process.env.NODE_ENV !== "production" 129 | ) { 130 | console.warn( 131 | `slidesToScroll should be equal to 1 when fade is true, you're using ${settings.slidesToScroll}` 132 | ); 133 | } 134 | settings.slidesToShow = 1; 135 | settings.slidesToScroll = 1; 136 | } 137 | 138 | // makes sure that children is an array, even when there is only 1 child 139 | let children = React.Children.toArray(this.props.children); 140 | 141 | // Children may contain false or null, so we should filter them 142 | // children may also contain string filled with spaces (in certain cases where we use jsx strings) 143 | children = children.filter(child => { 144 | if (typeof child === "string") { 145 | return !!child.trim(); 146 | } 147 | return !!child; 148 | }); 149 | 150 | // rows and slidesPerRow logic is handled here 151 | if ( 152 | settings.variableWidth && 153 | (settings.rows > 1 || settings.slidesPerRow > 1) 154 | ) { 155 | console.warn( 156 | `variableWidth is not supported in case of rows > 1 or slidesPerRow > 1` 157 | ); 158 | settings.variableWidth = false; 159 | } 160 | let newChildren = []; 161 | let currentWidth = null; 162 | for ( 163 | let i = 0; 164 | i < children.length; 165 | i += settings.rows * settings.slidesPerRow 166 | ) { 167 | let newSlide = []; 168 | for ( 169 | let j = i; 170 | j < i + settings.rows * settings.slidesPerRow; 171 | j += settings.slidesPerRow 172 | ) { 173 | let row = []; 174 | for (let k = j; k < j + settings.slidesPerRow; k += 1) { 175 | if (settings.variableWidth && children[k].props.style) { 176 | currentWidth = children[k].props.style.width; 177 | } 178 | if (k >= children.length) break; 179 | row.push( 180 | React.cloneElement(children[k], { 181 | key: 100 * i + 10 * j + k, 182 | tabIndex: -1, 183 | style: { 184 | width: `${100 / settings.slidesPerRow}%`, 185 | display: "inline-block" 186 | } 187 | }) 188 | ); 189 | } 190 | newSlide.push(
    {row}
    ); 191 | } 192 | if (settings.variableWidth) { 193 | newChildren.push( 194 |
    195 | {newSlide} 196 |
    197 | ); 198 | } else { 199 | newChildren.push(
    {newSlide}
    ); 200 | } 201 | } 202 | 203 | if (settings === "unslick") { 204 | const className = "regular slider " + (this.props.className || ""); 205 | return
    {children}
    ; 206 | } else if (newChildren.length <= settings.slidesToShow) { 207 | settings.unslick = true; 208 | } 209 | return ( 210 | 215 | {newChildren} 216 | 217 | ); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/track.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import React from "react"; 4 | import classnames from "classnames"; 5 | import { 6 | lazyStartIndex, 7 | lazyEndIndex, 8 | getPreClones, 9 | getPostClones 10 | } from "./utils/innerSliderUtils"; 11 | 12 | // given specifications/props for a slide, fetch all the classes that need to be applied to the slide 13 | const getSlideClasses = spec => { 14 | let slickActive, slickCenter, slickCloned; 15 | let centerOffset, index; 16 | 17 | if (spec.rtl) { 18 | index = spec.slideCount - 1 - spec.index; 19 | } else { 20 | index = spec.index; 21 | } 22 | slickCloned = index < 0 || index >= spec.slideCount; 23 | if (spec.centerMode) { 24 | centerOffset = Math.floor(spec.slidesToShow / 2); 25 | slickCenter = (index - spec.currentSlide) % spec.slideCount === 0; 26 | if ( 27 | index > spec.currentSlide - centerOffset - 1 && 28 | index <= spec.currentSlide + centerOffset 29 | ) { 30 | slickActive = true; 31 | } 32 | } else { 33 | slickActive = 34 | spec.currentSlide <= index && 35 | index < spec.currentSlide + spec.slidesToShow; 36 | } 37 | 38 | let focusedSlide; 39 | if (spec.targetSlide < 0) { 40 | focusedSlide = spec.targetSlide + spec.slideCount; 41 | } else if (spec.targetSlide >= spec.slideCount) { 42 | focusedSlide = spec.targetSlide - spec.slideCount; 43 | } else { 44 | focusedSlide = spec.targetSlide; 45 | } 46 | let slickCurrent = index === focusedSlide; 47 | return { 48 | "slick-slide": true, 49 | "slick-active": slickActive, 50 | "slick-center": slickCenter, 51 | "slick-cloned": slickCloned, 52 | "slick-current": slickCurrent // dubious in case of RTL 53 | }; 54 | }; 55 | 56 | const getSlideStyle = spec => { 57 | let style = {}; 58 | 59 | if (spec.variableWidth === undefined || spec.variableWidth === false) { 60 | style.width = spec.slideWidth; 61 | } 62 | 63 | if (spec.fade) { 64 | style.position = "relative"; 65 | if (spec.vertical) { 66 | style.top = -spec.index * parseInt(spec.slideHeight); 67 | } else { 68 | style.left = -spec.index * parseInt(spec.slideWidth); 69 | } 70 | style.opacity = spec.currentSlide === spec.index ? 1 : 0; 71 | style.zIndex = spec.currentSlide === spec.index ? 999 : 998; 72 | if (spec.useCSS) { 73 | style.transition = 74 | "opacity " + 75 | spec.speed + 76 | "ms " + 77 | spec.cssEase + 78 | ", " + 79 | "visibility " + 80 | spec.speed + 81 | "ms " + 82 | spec.cssEase; 83 | } 84 | } 85 | 86 | return style; 87 | }; 88 | 89 | const getKey = (child, fallbackKey) => child.key || fallbackKey; 90 | 91 | const renderSlides = spec => { 92 | let key; 93 | let slides = []; 94 | let preCloneSlides = []; 95 | let postCloneSlides = []; 96 | let childrenCount = React.Children.count(spec.children); 97 | let startIndex = lazyStartIndex(spec); 98 | let endIndex = lazyEndIndex(spec); 99 | 100 | React.Children.forEach(spec.children, (elem, index) => { 101 | let child; 102 | let childOnClickOptions = { 103 | message: "children", 104 | index: index, 105 | slidesToScroll: spec.slidesToScroll, 106 | currentSlide: spec.currentSlide 107 | }; 108 | 109 | // in case of lazyLoad, whether or not we want to fetch the slide 110 | if ( 111 | !spec.lazyLoad || 112 | (spec.lazyLoad && spec.lazyLoadedList.indexOf(index) >= 0) 113 | ) { 114 | child = elem; 115 | } else { 116 | child =
    ; 117 | } 118 | let childStyle = getSlideStyle({ ...spec, index }); 119 | let slideClass = child.props.className || ""; 120 | let slideClasses = getSlideClasses({ ...spec, index }); 121 | // push a cloned element of the desired slide 122 | slides.push( 123 | React.cloneElement(child, { 124 | key: "original" + getKey(child, index), 125 | "data-index": index, 126 | className: classnames(slideClasses, slideClass), 127 | tabIndex: "-1", 128 | "aria-hidden": !slideClasses["slick-active"], 129 | style: { outline: "none", ...(child.props.style || {}), ...childStyle }, 130 | onClick: e => { 131 | child.props && child.props.onClick && child.props.onClick(e); 132 | if (spec.focusOnSelect) { 133 | spec.focusOnSelect(childOnClickOptions); 134 | } 135 | } 136 | }) 137 | ); 138 | 139 | // if slide needs to be precloned or postcloned 140 | if ( 141 | spec.infinite && 142 | childrenCount > 1 && 143 | spec.fade === false && 144 | !spec.unslick 145 | ) { 146 | let preCloneNo = childrenCount - index; 147 | if (preCloneNo <= getPreClones(spec)) { 148 | key = -preCloneNo; 149 | if (key >= startIndex) { 150 | child = elem; 151 | } 152 | slideClasses = getSlideClasses({ ...spec, index: key }); 153 | preCloneSlides.push( 154 | React.cloneElement(child, { 155 | key: "precloned" + getKey(child, key), 156 | "data-index": key, 157 | tabIndex: "-1", 158 | className: classnames(slideClasses, slideClass), 159 | "aria-hidden": !slideClasses["slick-active"], 160 | style: { ...(child.props.style || {}), ...childStyle }, 161 | onClick: e => { 162 | child.props && child.props.onClick && child.props.onClick(e); 163 | if (spec.focusOnSelect) { 164 | spec.focusOnSelect(childOnClickOptions); 165 | } 166 | } 167 | }) 168 | ); 169 | } 170 | if (index < getPostClones(spec)) { 171 | key = childrenCount + index; 172 | if (key < endIndex) { 173 | child = elem; 174 | } 175 | slideClasses = getSlideClasses({ ...spec, index: key }); 176 | postCloneSlides.push( 177 | React.cloneElement(child, { 178 | key: "postcloned" + getKey(child, key), 179 | "data-index": key, 180 | tabIndex: "-1", 181 | className: classnames(slideClasses, slideClass), 182 | "aria-hidden": !slideClasses["slick-active"], 183 | style: { ...(child.props.style || {}), ...childStyle }, 184 | onClick: e => { 185 | child.props && child.props.onClick && child.props.onClick(e); 186 | if (spec.focusOnSelect) { 187 | spec.focusOnSelect(childOnClickOptions); 188 | } 189 | } 190 | }) 191 | ); 192 | } 193 | } 194 | }); 195 | 196 | if (spec.rtl) { 197 | return preCloneSlides.concat(slides, postCloneSlides).reverse(); 198 | } else { 199 | return preCloneSlides.concat(slides, postCloneSlides); 200 | } 201 | }; 202 | 203 | export class Track extends React.PureComponent { 204 | node = null; 205 | 206 | handleRef = ref => { 207 | this.node = ref; 208 | }; 209 | 210 | render() { 211 | const slides = renderSlides(this.props); 212 | const { onMouseEnter, onMouseOver, onMouseLeave } = this.props; 213 | const mouseEvents = { onMouseEnter, onMouseOver, onMouseLeave }; 214 | return ( 215 |
    221 | {slides} 222 |
    223 | ); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /test-setup.js: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom/extend-expect"; 2 | import "regenerator-runtime/runtime"; 3 | 4 | //Fix for "matchMedia not present, legacy browsers require a polyfill jest" error 5 | window.matchMedia = 6 | window.matchMedia || 7 | function() { 8 | return { 9 | matches: false, 10 | addListener: function() {}, 11 | removeListener: function() {} 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /test-utils.js: -------------------------------------------------------------------------------- 1 | import { render, fireEvent, waitFor, screen } from "@testing-library/react"; 2 | 3 | export function getSlidesCount(container) { 4 | return container.getElementsByClassName("slick-slide").length; 5 | } 6 | 7 | export function getSlides(container) { 8 | return container.getElementsByClassName("slick-slide"); 9 | } 10 | 11 | export function getClonesCount(container) { 12 | return container.getElementsByClassName("slick-cloned").length; 13 | } 14 | 15 | export function getActiveSlidesCount(container) { 16 | return container.querySelectorAll(".slick-slide.slick-active").length; 17 | } 18 | 19 | export function getCurrentSlide(container) { 20 | return container.querySelector(".slick-current"); 21 | } 22 | 23 | export function getCurrentSlideContent(container) { 24 | const slide = container.querySelector(".slick-current"); 25 | return slide.textContent; 26 | } 27 | 28 | export function getButtons(container) { 29 | return container.querySelectorAll(".slick-dots button"); 30 | } 31 | 32 | export function getButtonsListItem(container) { 33 | return container.querySelectorAll(".slick-dots")[0].children; 34 | } 35 | export function getButtonsLength(container) { 36 | return container.querySelectorAll(".slick-dots")[0].children.length; 37 | } 38 | export function hasClass(element, classname) { 39 | if (element.className != undefined) { 40 | return element.classList.contains(classname); 41 | } 42 | return false; 43 | } 44 | export function getActiveButton(container) { 45 | return Array.from( 46 | container.querySelectorAll(".slick-dots .slick-active button") 47 | ).map(e => e.textContent); 48 | } 49 | // export function getCurrentSlideIdState(container) { 50 | // return parseInt(getCurrentSlide(container).getAttribute("data-index")) + 1; 51 | // } 52 | // export function activeSlides(container) { 53 | // return container.querySelectorAll(".slick-slide.slick-active"); 54 | // } 55 | export function getActiveSlide(container) { 56 | return container.querySelector(".slick-slide.slick-active"); 57 | } 58 | 59 | export function getActiveSlides(container) { 60 | return container.querySelectorAll(".slick-slide.slick-active"); 61 | } 62 | 63 | export function getActiveSlidesText(container) { 64 | const slides = getActiveSlides(container); 65 | return Array.from(slides).map(e => e.textContent); 66 | } 67 | 68 | export function clickNext(container) { 69 | fireEvent( 70 | container.getElementsByClassName("slick-next")[0], 71 | new MouseEvent("click", { 72 | bubbles: true, 73 | cancelable: true 74 | }) 75 | ); 76 | } 77 | 78 | export function clickPrevious(container) { 79 | fireEvent( 80 | container.getElementsByClassName("slick-prev")[0], 81 | new MouseEvent("click", { 82 | bubbles: true, 83 | cancelable: true 84 | }) 85 | ); 86 | } 87 | 88 | export function hasDots(container) { 89 | return Boolean(container.querySelectorAll(".slick-dots")[0]); 90 | } 91 | 92 | export function hasArrows(container) { 93 | return Boolean( 94 | container.getElementsByClassName("slick-next")[0] || 95 | container.getElementsByClassName("slick-prev")[0] 96 | ); 97 | } 98 | -------------------------------------------------------------------------------- /webpack.config.dist.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var path = require("path"); 3 | 4 | module.exports = { 5 | mode: "production", 6 | 7 | entry: "./src/index", 8 | 9 | output: { 10 | library: "Slider", 11 | libraryTarget: "umd", 12 | path: path.join(__dirname, "dist") 13 | }, 14 | 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.js/, 19 | exclude: /(node_modules)/, 20 | use: { 21 | loader: "babel-loader" 22 | } 23 | } 24 | ] 25 | }, 26 | 27 | resolve: { 28 | extensions: [".js", ".jsx"] 29 | }, 30 | 31 | externals: { 32 | react: { 33 | root: "React", 34 | commonjs2: "react", 35 | commonjs: "react", 36 | amd: "react" 37 | }, 38 | "react-dom": { 39 | root: "ReactDOM", 40 | commonjs2: "react-dom", 41 | commonjs: "react-dom", 42 | amd: "react-dom" 43 | } 44 | }, 45 | 46 | node: { 47 | Buffer: false 48 | }, 49 | 50 | devtool: "source-map", 51 | 52 | performance: { 53 | hints: "warning" 54 | }, 55 | 56 | plugins: [] 57 | }; 58 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var path = require("path"); 3 | 4 | module.exports = { 5 | mode: "production", 6 | devtool: "source-map", 7 | entry: { 8 | "docs.js": "./docs/index.js" 9 | }, 10 | output: { 11 | path: path.join(__dirname, "build"), 12 | filename: "[name]" 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.jsx?/, 18 | exclude: /(node_modules)/, 19 | use: { 20 | loader: "babel-loader" 21 | } 22 | }, 23 | { 24 | test: /\.md$/, 25 | loader: "html!markdown" 26 | } 27 | ] 28 | }, 29 | resolve: { 30 | extensions: [".js", ".jsx"], 31 | alias: { 32 | "react-slick": path.resolve(__dirname, "src/index.js") 33 | } 34 | }, 35 | plugins: [new webpack.IgnorePlugin(/vertx/)], 36 | devServer: { 37 | contentBase: path.join(__dirname, "./build"), 38 | port: 8080, 39 | hot: true 40 | } 41 | }; 42 | --------------------------------------------------------------------------------