From a59be6538d331fbd8570e6aea167607de62fc7c9 Mon Sep 17 00:00:00 2001 From: gcarron <gcarron@grandlyon.com> Date: Mon, 30 Nov 2020 16:22:53 +0100 Subject: [PATCH] Add desktop compatibility of slider + buttons --- src/components/Seasons/SeasonCard.tsx | 2 +- src/components/Seasons/SeasonCardLocked.tsx | 1 + src/components/Seasons/SeasonCardOnGoing.tsx | 1 + src/components/Seasons/SeasonCardUnlocked.tsx | 1 + src/components/Seasons/SeasonView.spec.tsx | 28 +++++ src/components/Seasons/SeasonView.tsx | 96 +++++++++++----- .../Seasons/_Seasons.scss} | 18 +++ .../__snapshots__/SeasonView.spec.tsx.snap | 104 ++++++++++++++++++ src/styles/index.scss | 1 - 9 files changed, 223 insertions(+), 29 deletions(-) create mode 100644 src/components/Seasons/SeasonView.spec.tsx rename src/{styles/components/_seasons.scss => components/Seasons/_Seasons.scss} (77%) create mode 100644 src/components/Seasons/__snapshots__/SeasonView.spec.tsx.snap diff --git a/src/components/Seasons/SeasonCard.tsx b/src/components/Seasons/SeasonCard.tsx index 5f918353a..576d61806 100644 --- a/src/components/Seasons/SeasonCard.tsx +++ b/src/components/Seasons/SeasonCard.tsx @@ -1,6 +1,6 @@ import React from 'react' import SeasonCardUnlocked from './SeasonCardUnlocked' - +import './_Seasons.scss' interface Season { saison: number name: string diff --git a/src/components/Seasons/SeasonCardLocked.tsx b/src/components/Seasons/SeasonCardLocked.tsx index b4ca33e5d..b0ab9d798 100644 --- a/src/components/Seasons/SeasonCardLocked.tsx +++ b/src/components/Seasons/SeasonCardLocked.tsx @@ -1,4 +1,5 @@ import React from 'react' +import './_Seasons.scss' const SeasonCardLocked: React.FC = () => { return <></> diff --git a/src/components/Seasons/SeasonCardOnGoing.tsx b/src/components/Seasons/SeasonCardOnGoing.tsx index 71f13f4d1..8f564d750 100644 --- a/src/components/Seasons/SeasonCardOnGoing.tsx +++ b/src/components/Seasons/SeasonCardOnGoing.tsx @@ -1,4 +1,5 @@ import React from 'react' +import './_Seasons.scss' const SeasonCardOnGoing: React.FC = () => { return <></> diff --git a/src/components/Seasons/SeasonCardUnlocked.tsx b/src/components/Seasons/SeasonCardUnlocked.tsx index 1b7e2ff5a..4a6f5ecd0 100644 --- a/src/components/Seasons/SeasonCardUnlocked.tsx +++ b/src/components/Seasons/SeasonCardUnlocked.tsx @@ -1,4 +1,5 @@ import React from 'react' +import './_Seasons.scss' interface Season { saison: number diff --git a/src/components/Seasons/SeasonView.spec.tsx b/src/components/Seasons/SeasonView.spec.tsx new file mode 100644 index 000000000..30ce0e6e3 --- /dev/null +++ b/src/components/Seasons/SeasonView.spec.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import { shallow } from 'enzyme' +import SeasonView from 'components/Seasons/SeasonView' + +const mockaHandleTouchStart = jest.fn() +const mockaHandleTouchMove = jest.fn() +const mockaHandleTouchEnd = jest.fn() + +describe('SeasonView component', () => { + it('should be rendered correctly', () => { + const component = shallow(<SeasonView />).getElement() + expect(component).toMatchSnapshot() + }) + it('should detect user swipe on slider', () => { + const wrapper = shallow(<SeasonView />) + // TODO how to simulate Touch event in jest + wrapper.find('.seasonSlider').simulate('touchStart', { + targetTouches: [ + { + clientX: 50, + }, + ], + }) + mockaHandleTouchStart.mockReturnValueOnce({ nativeEvent: '' }) + + expect(mockaHandleTouchStart).toBeCalledTimes(1) + }) +}) diff --git a/src/components/Seasons/SeasonView.tsx b/src/components/Seasons/SeasonView.tsx index 8c66ce98f..3f9a09914 100644 --- a/src/components/Seasons/SeasonView.tsx +++ b/src/components/Seasons/SeasonView.tsx @@ -1,17 +1,23 @@ -import React, { useEffect, useState } from 'react' +import React, { useState } from 'react' import SeasonCard from './SeasonCard' import CozyBar from 'components/Header/CozyBar' import Content from 'components/Content/Content' import Header from 'components/Header/Header' -const SeasonView: React.FC = () => { - const [headerHeight, setHeaderHeight] = useState<number>(0) - const [touchStart, setTouchStart] = React.useState(0) - const [touchEnd, setTouchEnd] = React.useState(0) - const [index, setindex] = useState(0) - const [containerTranslation, setcontainerTranslation] = useState<any>(0) +import StyledIconbutton from 'components/CommonKit/IconButton/StyledIconButton' +import LeftArrowIcon from 'assets/icons/ico/left-arrow.svg' +import RightArrowIcon from 'assets/icons/ico/right-arrow.svg' +import './_Seasons.scss' +const SeasonView: React.FC = () => { const cardWitdh = 285 const marginPx = 16 + const [headerHeight, setHeaderHeight] = useState<number>(0) + const [touchStart, setTouchStart] = useState<number>() + const [touchEnd, setTouchEnd] = useState<number>() + const [index, setindex] = useState<number>(0) + const [containerTranslation, setcontainerTranslation] = useState<number>( + marginPx + ) const defineHeaderHeight = (height: number) => { setHeaderHeight(height) @@ -29,10 +35,19 @@ const SeasonView: React.FC = () => { { saison: 4, name: 'saison4' }, { saison: 5, name: 'saison5' }, ]) + const resetValues = () => { + //Method used to cancel a swipe on a simple click + setTouchEnd(0) + setTouchStart(0) + } const moveSliderRight = () => { if (index < seasonList.length - 1) { - if (index >= 1) + if (index === 0) + setcontainerTranslation( + (prev: number) => prev - cardWitdh - marginPx * 1.2 + ) + else if (index >= 1) setcontainerTranslation((prev: number) => prev - cardWitdh - marginPx) else setcontainerTranslation((prev: number) => prev - cardWitdh) setindex(prev => prev + 1) @@ -46,27 +61,36 @@ const SeasonView: React.FC = () => { setindex(prev => prev - 1) } if (index <= 1) { - setcontainerTranslation(0) + setcontainerTranslation(marginPx) } } - const handleTouchStart = (e: React.TouchEvent) => { - setTouchStart(e.targetTouches[0].clientX) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleClickOrTouchStart = (e: any) => { + if (e.nativeEvent instanceof TouchEvent) + setTouchStart(e.targetTouches[0].clientX) + if (e.nativeEvent instanceof MouseEvent) setTouchStart(e.clientX) } - const handleTouchEnd = () => { - //Change the following value in order to change the swipe sensibilyy - if (touchStart - touchEnd > 75) { - //If swipe left move slider right and add positive translation - moveSliderRight() - } - if (touchStart - touchEnd < -75) { - //If swipe right move slider left and add negative translation - moveSliderLeft() + const handleClickOrTouchEnd = () => { + //if the swipe is too small and can be taken for a touch + if (touchStart && touchEnd) { + if (touchStart - touchEnd < 5 && -5 < touchStart - touchEnd) return + //Change the following value in order to change the swipe sensibilyy + if (touchStart - touchEnd > 75) { + //If swipe left move slider right and add positive translation + moveSliderRight() + } + if (touchStart - touchEnd < -75) { + //If swipe right move slider left and add negative translation + moveSliderLeft() + } } } - - const handleTouchMove = (e: React.TouchEvent) => { - setTouchEnd(e.targetTouches[0].clientX) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleClickOrTouchMove = (e: any) => { + if (e.nativeEvent instanceof TouchEvent) + setTouchEnd(e.targetTouches[0].clientX) + if (e.nativeEvent instanceof MouseEvent) setTouchEnd(e.clientX) } return ( @@ -79,19 +103,37 @@ const SeasonView: React.FC = () => { <Content height={headerHeight}> <div className="seasonSlider" - onTouchStart={handleTouchStart} - onTouchMove={handleTouchMove} - onTouchEnd={handleTouchEnd} + onClick={resetValues} + onTouchStart={handleClickOrTouchStart} + onTouchMove={handleClickOrTouchMove} + onTouchEnd={handleClickOrTouchEnd} + onMouseDown={handleClickOrTouchStart} + onMouseMove={handleClickOrTouchMove} + onMouseUp={handleClickOrTouchEnd} > <div className="container" - style={{ transform: `translateX(${containerTranslation}px)` }} + style={{ + transform: `translateX(${containerTranslation}px)`, + }} > {seasonList.map(season => ( <SeasonCard key={season.saison} season={season} index={index} /> ))} </div> </div> + <div className="sliderButtons"> + <StyledIconbutton + onClick={moveSliderLeft} + icon={LeftArrowIcon} + size={16} + /> + <StyledIconbutton + onClick={moveSliderRight} + icon={RightArrowIcon} + size={16} + /> + </div> </Content> </> ) diff --git a/src/styles/components/_seasons.scss b/src/components/Seasons/_Seasons.scss similarity index 77% rename from src/styles/components/_seasons.scss rename to src/components/Seasons/_Seasons.scss index fa6f6bc7c..c888e246f 100644 --- a/src/styles/components/_seasons.scss +++ b/src/components/Seasons/_Seasons.scss @@ -1,9 +1,19 @@ +@import '../../styles/base/typography'; + .seasonSlider { min-height: inherit; margin-top: -2rem; overflow-x: hidden; padding: 2rem; position: relative; + max-width: 850px; + user-select: none; + @media all and (min-width: $width-tablet) { + margin: auto; + margin-top: 2rem; + min-height: 0; + } + .container { min-height: inherit; width: 100%; @@ -49,3 +59,11 @@ } } } +.sliderButtons { + text-align: center; + margin: auto; + margin-top: 1.5rem; + @media all and (max-width: $width-tablet) { + display: none; + } +} diff --git a/src/components/Seasons/__snapshots__/SeasonView.spec.tsx.snap b/src/components/Seasons/__snapshots__/SeasonView.spec.tsx.snap new file mode 100644 index 000000000..3cc771eb9 --- /dev/null +++ b/src/components/Seasons/__snapshots__/SeasonView.spec.tsx.snap @@ -0,0 +1,104 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SeasonView component should be rendered correctly 1`] = ` +<React.Fragment> + <CozyBar + titleKey="COMMON.APP_OPTIONS_TITLE" + /> + <Header + desktopTitleKey="COMMON.APP_OPTIONS_TITLE" + setHeaderHeight={[Function]} + /> + <Content + height={0} + > + <div + className="seasonSlider" + onMouseDown={[Function]} + onMouseMove={[Function]} + onMouseUp={[Function]} + onTouchEnd={[Function]} + onTouchMove={[Function]} + onTouchStart={[Function]} + > + <div + className="container" + style={ + Object { + "transform": "translateX(16px)", + } + } + > + <SeasonCard + index={0} + season={ + Object { + "name": "saison0", + "saison": 0, + } + } + /> + <SeasonCard + index={0} + season={ + Object { + "name": "saison1", + "saison": 1, + } + } + /> + <SeasonCard + index={0} + season={ + Object { + "name": "saison2", + "saison": 2, + } + } + /> + <SeasonCard + index={0} + season={ + Object { + "name": "saison3", + "saison": 3, + } + } + /> + <SeasonCard + index={0} + season={ + Object { + "name": "saison4", + "saison": 4, + } + } + /> + <SeasonCard + index={0} + season={ + Object { + "name": "saison5", + "saison": 5, + } + } + /> + </div> + </div> + <div + className="sliderButtons" + > + <StyledIconButton + icon="test-file-stub" + onClick={[Function]} + size={16} + /> + <StyledIconButton + icon="test-file-stub" + onClick={[Function]} + size={16} + /> + </div> + </Content> +</React.Fragment> +`; diff --git a/src/styles/index.scss b/src/styles/index.scss index 06daec7f0..adfc221e3 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -23,7 +23,6 @@ @import 'components/faq'; @import 'components/legalnotice'; @import 'components/splash'; -@import 'components/seasons'; @import 'components/auth'; @import 'components/feedback'; @import 'components/version'; -- GitLab