From fe4277546d9bef79d085bfef1506260c2b6fb1cd Mon Sep 17 00:00:00 2001 From: Tran Manh Date: Wed, 21 Jun 2023 11:02:32 +0700 Subject: [PATCH 01/17] [CHORE] Bump version to 0.3.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 629bab2..07905b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "manh-react-survey", - "version": "0.2.0", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "manh-react-survey", - "version": "0.2.0", + "version": "0.3.0", "dependencies": { "@reduxjs/toolkit": "1.9.5", "@types/lodash": "4.14.195", diff --git a/package.json b/package.json index 4b1d04c..393166c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "manh-react-survey", - "version": "0.2.0", + "version": "0.3.0", "private": true, "dependencies": { "@reduxjs/toolkit": "1.9.5", From 2845300c8f89afb0a4cbcc5ed4828b68967a3bde Mon Sep 17 00:00:00 2001 From: Tran Manh Date: Wed, 21 Jun 2023 16:03:21 +0700 Subject: [PATCH 02/17] [#10] Create Shimmer loading --- src/components/Dashboard/Content/index.tsx | 20 +++++++++++++ src/components/Dashboard/Header/index.tsx | 35 ++++++++++++++++++---- src/components/Shimmer/index.tsx | 18 +++++++++++ src/screens/Dashboard/index.tsx | 4 +-- tailwind.config.js | 7 +++++ 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 src/components/Dashboard/Content/index.tsx create mode 100644 src/components/Shimmer/index.tsx diff --git a/src/components/Dashboard/Content/index.tsx b/src/components/Dashboard/Content/index.tsx new file mode 100644 index 0000000..e929ee4 --- /dev/null +++ b/src/components/Dashboard/Content/index.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +import Shimmer from 'components/Shimmer'; + +const DashboardContent = (): JSX.Element => { + return ( +
+ +
+
+ + +
+ +
+
+ ); +}; + +export default DashboardContent; diff --git a/src/components/Dashboard/Header/index.tsx b/src/components/Dashboard/Header/index.tsx index 74c9b12..af06730 100644 --- a/src/components/Dashboard/Header/index.tsx +++ b/src/components/Dashboard/Header/index.tsx @@ -1,30 +1,53 @@ import React from 'react'; import { ReactComponent as NimbleLogoWhite } from 'assets/images/icons/nimble-logo-white.svg'; +import Shimmer from 'components/Shimmer'; interface DashboardHeaderProps { dateTime: string; daysAgo: string; profileUrl: string; + shouldShowShimmer?: boolean; children: React.ReactNode; 'data-test-id'?: string; } -const DashboardHeader = ({ dateTime, daysAgo, profileUrl, children, ...rest }: DashboardHeaderProps): JSX.Element => { +const DashboardHeader = ({ + dateTime, + daysAgo, + profileUrl, + shouldShowShimmer = true, + children, + ...rest +}: DashboardHeaderProps): JSX.Element => { return (
- - user avatar + {shouldShowShimmer ? : } + {shouldShowShimmer ? ( + + ) : ( + user avatar + )}
-

{dateTime}

-

{daysAgo}

+ {shouldShowShimmer ? ( + + ) : ( +

{dateTime}

+ )} + {shouldShowShimmer ? ( +
+ +
+ ) : ( +

{daysAgo}

+ )}
-
+
{children}
diff --git a/src/components/Shimmer/index.tsx b/src/components/Shimmer/index.tsx new file mode 100644 index 0000000..e3ad5ce --- /dev/null +++ b/src/components/Shimmer/index.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +interface ShimmerProps { + classAttributes: string; +} + +// Ref: https://delba.dev/blog/animated-loading-skeletons-with-tailwind +const Shimmer = ({ classAttributes }: ShimmerProps): JSX.Element => { + return ( +
+
+
+ ); +}; + +export default Shimmer; diff --git a/src/screens/Dashboard/index.tsx b/src/screens/Dashboard/index.tsx index b02a0a2..de85c38 100644 --- a/src/screens/Dashboard/index.tsx +++ b/src/screens/Dashboard/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import DashboardEmpty from 'components/Dashboard/Empty'; +import DashboardContent from 'components/Dashboard/Content'; import DashboardHeader from 'components/Dashboard/Header'; const DashBoardScreen = (): JSX.Element => { @@ -8,7 +8,7 @@ const DashBoardScreen = (): JSX.Element => {
- +
diff --git a/tailwind.config.js b/tailwind.config.js index 5170301..53183bd 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -28,6 +28,13 @@ module.exports = { 'survey-wide': '-0.08px', 'survey-wider': '0.38px', }, + keyframes: { + shimmer: { + '100%': { + transform: 'translateX(100%)', + }, + }, + }, }, }, plugins: [], From 10cfe6715223c3f3e1d64e775d4f76cb3acfa2ae Mon Sep 17 00:00:00 2001 From: Tran Manh Date: Tue, 27 Jun 2023 17:27:13 +0700 Subject: [PATCH 03/17] [#10] Update unittest for components --- .../Dashboard/Content/index.test.tsx | 16 ++++++++++ src/components/Dashboard/Content/index.tsx | 8 +++-- .../Dashboard/Header/index.test.tsx | 30 ++++++++++++++++--- src/components/Shimmer/index.test.tsx | 18 +++++++++++ src/components/Shimmer/index.tsx | 4 ++- 5 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 src/components/Dashboard/Content/index.test.tsx create mode 100644 src/components/Shimmer/index.test.tsx diff --git a/src/components/Dashboard/Content/index.test.tsx b/src/components/Dashboard/Content/index.test.tsx new file mode 100644 index 0000000..a33e771 --- /dev/null +++ b/src/components/Dashboard/Content/index.test.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +import { render, screen } from '@testing-library/react'; + +import DashboardContent from '.'; + +describe('DashboardContent', () => { + const dataTestId = 'dashboard-content'; + it('renders DashboardContent and its components', () => { + render(); + + const dashboardContent = screen.getByTestId(dataTestId); + + expect(dashboardContent).toBeVisible(); + }); +}); diff --git a/src/components/Dashboard/Content/index.tsx b/src/components/Dashboard/Content/index.tsx index e929ee4..97f91c3 100644 --- a/src/components/Dashboard/Content/index.tsx +++ b/src/components/Dashboard/Content/index.tsx @@ -2,9 +2,13 @@ import React from 'react'; import Shimmer from 'components/Shimmer'; -const DashboardContent = (): JSX.Element => { +interface DashboardContentProps { + 'data-test-id'?: string; +} + +const DashboardContent = ({ ...attributes }: DashboardContentProps): JSX.Element => { return ( -
+
diff --git a/src/components/Dashboard/Header/index.test.tsx b/src/components/Dashboard/Header/index.test.tsx index ee05ac2..b1b7fce 100644 --- a/src/components/Dashboard/Header/index.test.tsx +++ b/src/components/Dashboard/Header/index.test.tsx @@ -6,12 +6,18 @@ import DashboardHeader from '.'; describe('DashboardHeader', () => { const dataTestId = 'dashboard-header'; - it('renders DashboardHeader and its components', () => { + it('renders DashboardHeader and its components without shimmers', () => { const dateTime = 'Monday, JUNE 15'; const daysAgo = 'Today'; const profileUrl = 'test url'; render( - + Dashboard Header ); @@ -21,10 +27,26 @@ describe('DashboardHeader', () => { expect(dashboardHeader).toBeVisible(); expect(dashboardHeader).toHaveTextContent(dateTime); - expect(dashboardHeader).toHaveTextContent(dateTime); - expect(dashboardHeader).toHaveTextContent(dateTime); + expect(dashboardHeader).toHaveTextContent(daysAgo); expect(avatar).toBeVisible(); expect(avatar).toHaveAttribute('src', profileUrl); }); + + it('does NOT renders text components', () => { + const dateTime = 'Monday, JUNE 15'; + const daysAgo = 'Today'; + const profileUrl = 'test url'; + render( + + Dashboard Header + + ); + + const dashboardHeader = screen.getByTestId(dataTestId); + + expect(dashboardHeader).toBeVisible(); + expect(dashboardHeader).not.toHaveTextContent(dateTime); + expect(dashboardHeader).not.toHaveTextContent(daysAgo); + }); }); diff --git a/src/components/Shimmer/index.test.tsx b/src/components/Shimmer/index.test.tsx new file mode 100644 index 0000000..ae4dc70 --- /dev/null +++ b/src/components/Shimmer/index.test.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import { render, screen } from '@testing-library/react'; + +import Shimmer from '.'; + +describe('Shimmer', () => { + const dataTestId = 'shimmer'; + it('renders Shimmer and its components', () => { + const testClass = 'test-class'; + render(); + + const shimmer = screen.getByTestId(dataTestId); + + expect(shimmer).toBeVisible(); + expect(shimmer).toHaveClass(testClass); + }); +}); diff --git a/src/components/Shimmer/index.tsx b/src/components/Shimmer/index.tsx index e3ad5ce..7dbe178 100644 --- a/src/components/Shimmer/index.tsx +++ b/src/components/Shimmer/index.tsx @@ -2,13 +2,15 @@ import React from 'react'; interface ShimmerProps { classAttributes: string; + 'data-test-id'?: string; } // Ref: https://delba.dev/blog/animated-loading-skeletons-with-tailwind -const Shimmer = ({ classAttributes }: ShimmerProps): JSX.Element => { +const Shimmer = ({ classAttributes, ...attributes }: ShimmerProps): JSX.Element => { return (
From 12170845565d5a49fae18d0a559885cc87f25cc6 Mon Sep 17 00:00:00 2001 From: Tran Manh Date: Mon, 24 Jul 2023 11:09:21 +0700 Subject: [PATCH 04/17] [#10] Fix issues by comments --- .../Dashboard/Content/index.test.tsx | 7 +++---- src/components/Dashboard/Content/index.tsx | 10 +++++----- src/components/Shimmer/index.test.tsx | 7 +++---- src/components/Shimmer/index.tsx | 18 +++++++++++------- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/components/Dashboard/Content/index.test.tsx b/src/components/Dashboard/Content/index.test.tsx index a33e771..5a81ccb 100644 --- a/src/components/Dashboard/Content/index.test.tsx +++ b/src/components/Dashboard/Content/index.test.tsx @@ -2,14 +2,13 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import DashboardContent from '.'; +import DashboardContent, { dashboardDataTestIds } from '.'; describe('DashboardContent', () => { - const dataTestId = 'dashboard-content'; it('renders DashboardContent and its components', () => { - render(); + render(); - const dashboardContent = screen.getByTestId(dataTestId); + const dashboardContent = screen.getByTestId(dashboardDataTestIds.content); expect(dashboardContent).toBeVisible(); }); diff --git a/src/components/Dashboard/Content/index.tsx b/src/components/Dashboard/Content/index.tsx index 97f91c3..4063583 100644 --- a/src/components/Dashboard/Content/index.tsx +++ b/src/components/Dashboard/Content/index.tsx @@ -2,13 +2,13 @@ import React from 'react'; import Shimmer from 'components/Shimmer'; -interface DashboardContentProps { - 'data-test-id'?: string; -} +export const dashboardDataTestIds = { + content: 'dashboard__content', +}; -const DashboardContent = ({ ...attributes }: DashboardContentProps): JSX.Element => { +const DashboardContent = (): JSX.Element => { return ( -
+
diff --git a/src/components/Shimmer/index.test.tsx b/src/components/Shimmer/index.test.tsx index ae4dc70..2290e4a 100644 --- a/src/components/Shimmer/index.test.tsx +++ b/src/components/Shimmer/index.test.tsx @@ -2,15 +2,14 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import Shimmer from '.'; +import Shimmer, { shimmerDataTestIds } from '.'; describe('Shimmer', () => { - const dataTestId = 'shimmer'; it('renders Shimmer and its components', () => { const testClass = 'test-class'; - render(); + render(); - const shimmer = screen.getByTestId(dataTestId); + const shimmer = screen.getByTestId(shimmerDataTestIds.content); expect(shimmer).toBeVisible(); expect(shimmer).toHaveClass(testClass); diff --git a/src/components/Shimmer/index.tsx b/src/components/Shimmer/index.tsx index 7dbe178..0462435 100644 --- a/src/components/Shimmer/index.tsx +++ b/src/components/Shimmer/index.tsx @@ -1,18 +1,22 @@ import React from 'react'; +import classNames from 'classnames'; + interface ShimmerProps { classAttributes: string; - 'data-test-id'?: string; } +export const shimmerDataTestIds = { + content: 'shimmer__content', +}; + // Ref: https://delba.dev/blog/animated-loading-skeletons-with-tailwind -const Shimmer = ({ classAttributes, ...attributes }: ShimmerProps): JSX.Element => { +const Shimmer = ({ classAttributes }: ShimmerProps): JSX.Element => { + const DEFAULT_CLASSNAME_ATTRIBUTES = + 'relative before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite] before:bg-gradient-to-r before:from-transparent before:from-30% before:via-white/50 before:to-transparent before:to-70% isolate overflow-hidden shadow-xl shadow-black/5'; return ( -
-
+
+
); }; From 2e3592bd5e8c0ac8c30c5fcd723c5933783b62f5 Mon Sep 17 00:00:00 2001 From: Tran Manh Date: Fri, 23 Jun 2023 14:55:02 +0700 Subject: [PATCH 05/17] [#11] Create Surveys on Dashboard page --- src/assets/images/icons/arrow-right.svg | 3 + src/components/BackgroundImage/index.tsx | 19 +++++++ src/components/Dashboard/Content/index.tsx | 65 ++++++++++++++++++++-- src/components/Dashboard/Header/index.tsx | 42 ++++++-------- src/screens/Dashboard/index.tsx | 21 +++++-- src/store/index.tsx | 2 + src/store/reducers/Surveys/actions.ts | 13 +++++ src/store/reducers/Surveys/index.ts | 45 +++++++++++++++ src/types/survey.ts | 7 +++ tailwind.config.js | 1 + 10 files changed, 185 insertions(+), 33 deletions(-) create mode 100644 src/assets/images/icons/arrow-right.svg create mode 100644 src/components/BackgroundImage/index.tsx create mode 100644 src/store/reducers/Surveys/actions.ts create mode 100644 src/store/reducers/Surveys/index.ts create mode 100644 src/types/survey.ts diff --git a/src/assets/images/icons/arrow-right.svg b/src/assets/images/icons/arrow-right.svg new file mode 100644 index 0000000..a5a058e --- /dev/null +++ b/src/assets/images/icons/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/BackgroundImage/index.tsx b/src/components/BackgroundImage/index.tsx new file mode 100644 index 0000000..4c4ee6b --- /dev/null +++ b/src/components/BackgroundImage/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +interface BackgroundImageProps { + children: React.ReactNode; + backgroundUrl?: string; +} + +const BackgroundImage = ({ backgroundUrl, children }: BackgroundImageProps): JSX.Element => { + return backgroundUrl ? ( +
+ background +
{children}
+
+ ) : ( +
{children}
+ ); +}; + +export default BackgroundImage; diff --git a/src/components/Dashboard/Content/index.tsx b/src/components/Dashboard/Content/index.tsx index 4063583..109ee18 100644 --- a/src/components/Dashboard/Content/index.tsx +++ b/src/components/Dashboard/Content/index.tsx @@ -1,14 +1,39 @@ -import React from 'react'; +import React, { useEffect } from 'react'; +import { ReactComponent as ArrowRight } from 'assets/images/icons/arrow-right.svg'; import Shimmer from 'components/Shimmer'; +import { Survey } from 'types/survey'; export const dashboardDataTestIds = { content: 'dashboard__content', }; -const DashboardContent = (): JSX.Element => { - return ( -
+interface DashboardContentProps { + surveys: Survey[]; + currentPosition: number; + shouldShowShimmer: boolean; + onNextSurvey: () => void; + onIndicatorTapped: (position: number) => void; +} + +const DashboardContent = ({ + surveys, + currentPosition, + shouldShowShimmer, + onNextSurvey, + onIndicatorTapped, +}: DashboardContentProps): JSX.Element => { + useEffect(() => { + const interval = setInterval(() => { + onNextSurvey(); + }, 3000); + return () => { + clearInterval(interval); + }; + }); + + return shouldShowShimmer ? ( +
@@ -18,6 +43,38 @@ const DashboardContent = (): JSX.Element => {
+ ) : ( +
+
+
+
+

{surveys[currentPosition].title}

+

{surveys[currentPosition].description}

+
+ +
+ {/* */} +
+ {surveys.map((surveyItem, index) => { + return ( + + ); + })} +
+
); }; diff --git a/src/components/Dashboard/Header/index.tsx b/src/components/Dashboard/Header/index.tsx index af06730..9586ba6 100644 --- a/src/components/Dashboard/Header/index.tsx +++ b/src/components/Dashboard/Header/index.tsx @@ -15,13 +15,13 @@ const DashboardHeader = ({ dateTime, daysAgo, profileUrl, - shouldShowShimmer = true, + shouldShowShimmer = false, children, ...rest }: DashboardHeaderProps): JSX.Element => { return ( -
-
+
+
{shouldShowShimmer ? : } {shouldShowShimmer ? ( @@ -29,29 +29,21 @@ const DashboardHeader = ({ user avatar )}
-
-
-
- {shouldShowShimmer ? ( - - ) : ( -

{dateTime}

- )} - {shouldShowShimmer ? ( -
- -
- ) : ( -

{daysAgo}

- )} -
-
-
-
-
-
{children}
-
+
+ {shouldShowShimmer ? ( + + ) : ( +

{dateTime}

+ )} + {shouldShowShimmer ? ( +
+ +
+ ) : ( +

{daysAgo}

+ )}
+
{children}
); }; diff --git a/src/screens/Dashboard/index.tsx b/src/screens/Dashboard/index.tsx index de85c38..3b53e15 100644 --- a/src/screens/Dashboard/index.tsx +++ b/src/screens/Dashboard/index.tsx @@ -1,17 +1,30 @@ import React from 'react'; +import BackgroundImage from 'components/BackgroundImage'; import DashboardContent from 'components/Dashboard/Content'; import DashboardHeader from 'components/Dashboard/Header'; +import { useAppDispatch, useAppSelector } from 'hooks'; +import { surveysAction } from 'store/reducers/Surveys'; const DashBoardScreen = (): JSX.Element => { + const { surveys, currentPosition } = useAppSelector((state) => state.surveys); + + const dispatch = useAppDispatch(); + return ( -
+ -
- +
+ dispatch(surveysAction.nextSurvey())} + onIndicatorTapped={(position) => dispatch(surveysAction.selectSurvey(position))} + />
-
+
); }; diff --git a/src/store/index.tsx b/src/store/index.tsx index a95f0d0..9d1d61c 100644 --- a/src/store/index.tsx +++ b/src/store/index.tsx @@ -1,9 +1,11 @@ import { configureStore } from '@reduxjs/toolkit'; import { authSlice } from './reducers/Authentication'; +import { surveysSlice } from './reducers/Surveys'; export const reducers = { auth: authSlice.reducer, + surveys: surveysSlice.reducer, }; export const store = configureStore({ diff --git a/src/store/reducers/Surveys/actions.ts b/src/store/reducers/Surveys/actions.ts new file mode 100644 index 0000000..246b657 --- /dev/null +++ b/src/store/reducers/Surveys/actions.ts @@ -0,0 +1,13 @@ +import { PayloadAction } from '@reduxjs/toolkit'; + +import { SurveysState } from '.'; + +export const surveysReducers = { + nextSurvey: (state: SurveysState) => { + const nextPosition = state.currentPosition + 1; + state.currentPosition = nextPosition >= state.surveys.length ? 0 : nextPosition; + }, + selectSurvey: (state: SurveysState, action: PayloadAction) => { + state.currentPosition = action.payload; + }, +}; diff --git a/src/store/reducers/Surveys/index.ts b/src/store/reducers/Surveys/index.ts new file mode 100644 index 0000000..5b7ffd9 --- /dev/null +++ b/src/store/reducers/Surveys/index.ts @@ -0,0 +1,45 @@ +import { createSlice } from '@reduxjs/toolkit'; + +import { Survey } from 'types/survey'; + +import { surveysReducers } from './actions'; + +export interface SurveysState { + surveys: Survey[]; + currentPosition: number; +} + +export const initialState: SurveysState = { + surveys: [ + { + id: '1', + resourceType: 'survey', + imageUrl: 'https://dhdbhh0jsld0o.cloudfront.net/m/1ea51560991bcb7d00d0_', + title: 'Working from home Check-In', + description: 'We would like to know how you feel about our work from home.', + }, + { + id: '2', + resourceType: 'survey', + imageUrl: 'https://dhdbhh0jsld0o.cloudfront.net/m/287db81c5e4242412cc0_', + title: 'I would like to know your life', + description: 'Please let us know if you are having problems in life', + }, + { + id: '3', + resourceType: 'survey', + imageUrl: 'https://dhdbhh0jsld0o.cloudfront.net/m/0221e768b99dc3576210_', + title: 'We will rock you!', + description: 'La la la la la la la la l al al la lalal', + }, + ], + currentPosition: 0, +}; + +export const surveysSlice = createSlice({ + name: 'surveys', + initialState, + reducers: surveysReducers, +}); + +export const surveysAction = surveysSlice.actions; diff --git a/src/types/survey.ts b/src/types/survey.ts new file mode 100644 index 0000000..b3c29be --- /dev/null +++ b/src/types/survey.ts @@ -0,0 +1,7 @@ +import { Resource } from 'types/resource'; + +export interface Survey extends Resource { + imageUrl: string; + title: string; + description: string; +} diff --git a/tailwind.config.js b/tailwind.config.js index 53183bd..7cc8a8e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -14,6 +14,7 @@ module.exports = { 'x-small': ['13px', '18px'], small: ['15px', '20px'], regular: ['17px', '22px'], + 'x-regular': ['22px', '28px'], large: ['28px', '34px'], 'x-large': ['34px', '41px'], }, From 689b125a198fd691f12a0583a616b5c059ea1076 Mon Sep 17 00:00:00 2001 From: Tran Manh Date: Sat, 24 Jun 2023 11:23:50 +0700 Subject: [PATCH 06/17] [#11] Fix leading to tracking attribute tailwind --- src/components/Dashboard/Content/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Dashboard/Content/index.tsx b/src/components/Dashboard/Content/index.tsx index 109ee18..1cb656f 100644 --- a/src/components/Dashboard/Content/index.tsx +++ b/src/components/Dashboard/Content/index.tsx @@ -52,7 +52,7 @@ const DashboardContent = ({

{surveys[currentPosition].title}

-

{surveys[currentPosition].description}

+

{surveys[currentPosition].description}

@@ -65,7 +70,7 @@ const DashboardContent = ({