diff --git a/package-lock.json b/package-lock.json
index 0bafecc..628bb7c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "manh-react-survey",
- "version": "0.5.0",
+ "version": "0.6.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "manh-react-survey",
- "version": "0.5.0",
+ "version": "0.6.0",
"dependencies": {
"@reduxjs/toolkit": "1.9.5",
"@travelperksl/fabricator": "7.0.1",
@@ -15,6 +15,7 @@
"classnames": "2.3.2",
"date-fns": "2.30.0",
"lodash": "4.17.21",
+ "rc-slider": "10.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-redux": "8.1.0",
@@ -21259,6 +21260,41 @@
"node": ">=0.10.0"
}
},
+ "node_modules/rc-slider": {
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-10.2.1.tgz",
+ "integrity": "sha512-l355C/65iV4UFp7mXq5xBTNX2/tF2g74VWiTVlTpNp+6vjE/xaHHNiQq5Af+Uu28uUiqCuH/QXs5HfADL9KJ/A==",
+ "dependencies": {
+ "@babel/runtime": "^7.10.1",
+ "classnames": "^2.2.5",
+ "rc-util": "^5.27.0"
+ },
+ "engines": {
+ "node": ">=8.x"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util": {
+ "version": "5.34.1",
+ "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.34.1.tgz",
+ "integrity": "sha512-SqiUT8Ssgh5C+hu4y887xwCrMNcxLm6ScOo8AFlWYYF3z9uNNiPpwwSjvicqOlWd79rNw1g44rnP7tz9MrO1ZQ==",
+ "dependencies": {
+ "@babel/runtime": "^7.18.3",
+ "react-is": "^16.12.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.9.0",
+ "react-dom": ">=16.9.0"
+ }
+ },
+ "node_modules/rc-util/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
diff --git a/package.json b/package.json
index 588ef3b..e4fec65 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "manh-react-survey",
- "version": "0.5.0",
+ "version": "0.6.0",
"private": true,
"dependencies": {
"@reduxjs/toolkit": "1.9.5",
@@ -10,6 +10,7 @@
"classnames": "2.3.2",
"date-fns": "2.30.0",
"lodash": "4.17.21",
+ "rc-slider": "10.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-redux": "8.1.0",
diff --git a/src/App.tsx b/src/App.tsx
index 0d5cf81..7bacb1d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { useRoutes } from 'react-router-dom';
import 'dummy.scss';
import 'assets/stylesheets/application.scss';
+import 'rc-slider/assets/index.css';
import { ToastContainer } from 'react-toastify';
import routes from './routes';
diff --git a/src/assets/images/icons/arrow-dropdown.svg b/src/assets/images/icons/arrow-dropdown.svg
new file mode 100644
index 0000000..3656b08
--- /dev/null
+++ b/src/assets/images/icons/arrow-dropdown.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/images/icons/close-btn.svg b/src/assets/images/icons/close-btn.svg
new file mode 100644
index 0000000..04b9f73
--- /dev/null
+++ b/src/assets/images/icons/close-btn.svg
@@ -0,0 +1,14 @@
+
diff --git a/src/components/AppSlider/index.test.tsx b/src/components/AppSlider/index.test.tsx
new file mode 100644
index 0000000..680fe17
--- /dev/null
+++ b/src/components/AppSlider/index.test.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+
+import { render, screen } from '@testing-library/react';
+
+import AppSlider, { appSliderDataTestIds } from '.';
+
+describe('AppSlider', () => {
+ it('renders AppSlider component', () => {
+ render();
+
+ const appSlider = screen.getByTestId(appSliderDataTestIds.base);
+ const slider = screen.getByRole('slider');
+
+ expect(appSlider).toBeVisible();
+ expect(slider).toBeVisible();
+ });
+});
diff --git a/src/components/AppSlider/index.tsx b/src/components/AppSlider/index.tsx
new file mode 100644
index 0000000..3eb2217
--- /dev/null
+++ b/src/components/AppSlider/index.tsx
@@ -0,0 +1,54 @@
+import React from 'react';
+
+import Slider from 'rc-slider';
+
+export const appSliderDataTestIds = {
+ base: 'app-slider__base',
+};
+
+interface SliderProps {
+ min?: number;
+ max?: number;
+ step?: number;
+ onValueChanged: (position: number) => void;
+}
+
+const AppSlider = ({ min = 1, max = 100, step = 1, onValueChanged }: SliderProps): JSX.Element => {
+ const handleOnChange = (value: number) => {
+ onValueChanged(value);
+ };
+
+ return (
+
+ handleOnChange(value as number)}
+ />
+
+ );
+};
+
+export default AppSlider;
diff --git a/src/components/Checkbox/index.test.tsx b/src/components/Checkbox/index.test.tsx
new file mode 100644
index 0000000..3ed75e5
--- /dev/null
+++ b/src/components/Checkbox/index.test.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+
+import { render, screen, within } from '@testing-library/react';
+
+import Checkbox, { checkboxDataTestIds } from '.';
+
+describe('Checkbox', () => {
+ describe('given the checkbox is checked', () => {
+ it('renders a checkbox component as checked', () => {
+ render();
+
+ const checkBox = screen.getByTestId(checkboxDataTestIds.base);
+
+ expect(checkBox).toBeVisible();
+ expect(within(checkBox).getByRole('checkbox')).toBeChecked();
+ });
+ });
+
+ describe('given the checkbox is unchecked', () => {
+ it('renders a checkbox component as unchecked', () => {
+ render();
+
+ const checkBox = screen.getByTestId(checkboxDataTestIds.base);
+
+ expect(checkBox).toBeVisible();
+ expect(within(checkBox).getByRole('checkbox')).not.toBeChecked();
+ });
+ });
+});
diff --git a/src/components/Checkbox/index.tsx b/src/components/Checkbox/index.tsx
new file mode 100644
index 0000000..11c7247
--- /dev/null
+++ b/src/components/Checkbox/index.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+
+import CheckboxSvg from 'components/CheckboxSvg';
+
+export const checkboxDataTestIds = {
+ base: 'checkbox__base',
+};
+
+interface CheckboxProps {
+ isValueChecked: boolean;
+}
+
+const Checkbox = ({ isValueChecked }: CheckboxProps): JSX.Element => {
+ return (
+
+ {
+ // Do nothing
+ }}
+ className="peer relative shrink-0 appearance-none w-[28px] h-[30px] border-[1px] border-white rounded-full mt-1 checked:bg-white"
+ />
+
+
+
+ );
+};
+
+export default Checkbox;
diff --git a/src/components/CheckboxSvg/index.test.tsx b/src/components/CheckboxSvg/index.test.tsx
new file mode 100644
index 0000000..548075c
--- /dev/null
+++ b/src/components/CheckboxSvg/index.test.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+
+import { render, screen } from '@testing-library/react';
+
+import CheckboxSvg, { checkboxSvgDataTestIds } from '.';
+
+describe('CheckboxSvg', () => {
+ it('renders a checkbox svg component', () => {
+ render();
+
+ const checkBoxSvg = screen.getByTestId(checkboxSvgDataTestIds.base);
+
+ expect(checkBoxSvg).toBeVisible();
+ });
+});
diff --git a/src/components/CheckboxSvg/index.tsx b/src/components/CheckboxSvg/index.tsx
new file mode 100644
index 0000000..e7bc4e2
--- /dev/null
+++ b/src/components/CheckboxSvg/index.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+
+export const checkboxSvgDataTestIds = {
+ base: 'checkbox-svg__base',
+};
+
+interface CheckboxSvgProps {
+ className?: string;
+}
+
+const DEFAULT_CLASSNAMES = 'absolute pointer-events-none hidden peer-checked:block mt-1 outline-none';
+
+const CheckboxSvg = ({ className = DEFAULT_CLASSNAMES }: CheckboxSvgProps): JSX.Element => {
+ return (
+
+ );
+};
+
+export default CheckboxSvg;
diff --git a/src/components/Dropdown/index.test.tsx b/src/components/Dropdown/index.test.tsx
new file mode 100644
index 0000000..aee601b
--- /dev/null
+++ b/src/components/Dropdown/index.test.tsx
@@ -0,0 +1,63 @@
+import React from 'react';
+
+import { act, render, screen, within } from '@testing-library/react';
+
+import { answerFabricator } from 'tests/fabricator';
+import { Answer } from 'types/answer';
+
+import Dropdown, { dropdownDataTestIds } from '.';
+
+describe('Dropdown', () => {
+ const answers = answerFabricator.times(5);
+
+ it('renders a dropdown component', () => {
+ const dropdownProps = {
+ questionId: 'question-id',
+ items: answers,
+ onValueChanged: () => {
+ // Do nothing
+ },
+ };
+
+ render();
+
+ const dropdown = screen.getByTestId(dropdownDataTestIds.base);
+
+ expect(dropdown).toBeVisible();
+ });
+
+ describe('given a value is clicked', () => {
+ it('returns the proper value', () => {
+ let selectedValue = '';
+ const onValueChanged = (answer: Answer) => {
+ selectedValue = answer.text ?? '';
+ };
+ const dropdownProps = {
+ questionId: 'question-id',
+ items: answers,
+ onValueChanged: onValueChanged,
+ };
+
+ render();
+
+ const dropdown = screen.getByTestId(dropdownDataTestIds.base);
+
+ const firstValue = answers[0].text;
+ const thirdValue = answers[2].text;
+
+ act(() => {
+ within(dropdown).getByText(firstValue).click();
+ });
+
+ act(() => {
+ within(dropdown)
+ .getByRole('button', {
+ name: thirdValue,
+ })
+ .click();
+ });
+
+ expect(selectedValue).toBe(thirdValue);
+ });
+ });
+});
diff --git a/src/components/Dropdown/index.tsx b/src/components/Dropdown/index.tsx
new file mode 100644
index 0000000..3e5f167
--- /dev/null
+++ b/src/components/Dropdown/index.tsx
@@ -0,0 +1,59 @@
+import React, { useEffect, useState } from 'react';
+
+import { ReactComponent as ArrowDropdown } from 'assets/images/icons/arrow-dropdown.svg';
+import { Answer } from 'types/answer';
+
+export const dropdownDataTestIds = {
+ base: 'dropdown__base',
+};
+
+interface DropdownProps {
+ questionId: string;
+ items: Answer[];
+ onValueChanged: (value: Answer) => void;
+}
+const Dropdown = ({ questionId, items, onValueChanged }: DropdownProps): JSX.Element => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [selectedValue, setSelectedValue] = useState(items[0].text);
+
+ const toggleDropdown = () => {
+ setIsOpen(!isOpen);
+ };
+
+ const handleOnSelectValue = (value: Answer) => {
+ setSelectedValue(value.text);
+ onValueChanged(value);
+ toggleDropdown();
+ };
+
+ useEffect(() => {
+ setSelectedValue(items[0].text);
+ }, [questionId, items]);
+
+ return (
+
+
+ {isOpen && (
+
+ {items.map((item) => (
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default Dropdown;
diff --git a/src/components/MultiChoice/index.test.tsx b/src/components/MultiChoice/index.test.tsx
new file mode 100644
index 0000000..7941ba5
--- /dev/null
+++ b/src/components/MultiChoice/index.test.tsx
@@ -0,0 +1,82 @@
+import React from 'react';
+
+import { act, render, screen, within } from '@testing-library/react';
+
+import { answerFabricator } from 'tests/fabricator';
+import { Answer } from 'types/answer';
+
+import MultiChoice, { multiChoiceDataTestIds } from '.';
+
+describe('MultiChoice', () => {
+ const answers = answerFabricator.times(3);
+
+ it('renders a multi choice component', () => {
+ render(
+ {
+ // Do nothing
+ }}
+ />
+ );
+
+ const multiChoice = screen.getByTestId(multiChoiceDataTestIds.base);
+
+ expect(multiChoice).toBeVisible();
+ expect(within(multiChoice).getAllByRole('presentation')).toHaveLength(3);
+ });
+
+ describe('given can pick many choices', () => {
+ describe('given the first two items is clicked', () => {
+ it('renders the first two components as checked state', () => {
+ let selectedValues: Answer[] = [];
+
+ const handleValuesChanged = (answers: Answer[]) => {
+ selectedValues = answers;
+ };
+
+ render( handleValuesChanged(answers)} />);
+
+ const multiChoice = screen.getByTestId(multiChoiceDataTestIds.base);
+
+ act(() => {
+ within(multiChoice).getAllByRole('presentation').at(0)?.click();
+ });
+
+ act(() => {
+ within(multiChoice).getAllByRole('presentation').at(1)?.click();
+ });
+
+ // The checked state of an item should NOT have `opacity-50` class
+ expect(within(multiChoice).getByText(answers[0].text)).not.toHaveClass('opacity-50');
+ expect(within(multiChoice).getByText(answers[1].text)).not.toHaveClass('opacity-50');
+ expect(selectedValues).toHaveLength(2);
+ });
+ });
+ });
+
+ describe('given can pick one choice', () => {
+ describe('given the first item is clicked', () => {
+ it('renders the first component as checked state', () => {
+ let selectedValues: Answer[] = [];
+
+ const handleValuesChanged = (answers: Answer[]) => {
+ selectedValues = answers;
+ };
+
+ render( handleValuesChanged(answers)} />);
+
+ const multiChoice = screen.getByTestId(multiChoiceDataTestIds.base);
+
+ act(() => {
+ within(multiChoice).getAllByRole('presentation').at(0)?.click();
+ });
+
+ // The checked state of an item should NOT have `opacity-50` class
+ expect(within(multiChoice).getByText(answers[0].text)).not.toHaveClass('opacity-50');
+ expect(selectedValues).toHaveLength(1);
+ });
+ });
+ });
+});
diff --git a/src/components/MultiChoice/index.tsx b/src/components/MultiChoice/index.tsx
new file mode 100644
index 0000000..980b540
--- /dev/null
+++ b/src/components/MultiChoice/index.tsx
@@ -0,0 +1,63 @@
+import React, { useState } from 'react';
+
+import classNames from 'classnames';
+
+import Checkbox from 'components/Checkbox';
+import { Answer } from 'types/answer';
+
+export const multiChoiceDataTestIds = {
+ base: 'multi-choice__base',
+};
+
+interface MultiChoiceProps {
+ items: Answer[];
+ isPickOne: boolean;
+ onValuesChanged: (answers: Answer[]) => void;
+}
+
+const MultiChoice = ({ items, isPickOne, onValuesChanged }: MultiChoiceProps): JSX.Element => {
+ const [selectedValues, setSelectedValues] = useState([]);
+
+ const isSelected = (answer: Answer): boolean => {
+ return !!selectedValues.find((item) => item.id === answer.id);
+ };
+
+ const getExtraClassNames = (answer: Answer): string => {
+ return isSelected(answer) ? '' : 'opacity-50';
+ };
+
+ const DEFAULT_TEXT_CLASSNAMES = 'text-white text-[20px] leading-[25px] tracking-survey-wider self-end';
+
+ const toggleCheckbox = (answer: Answer) => {
+ let newSelectedValues = [];
+
+ if (isSelected(answer)) {
+ newSelectedValues = selectedValues.filter((currentAnswer) => currentAnswer.id !== answer.id);
+ } else {
+ if (isPickOne) {
+ newSelectedValues = [answer];
+ } else {
+ newSelectedValues = [...selectedValues, answer];
+ }
+ }
+
+ setSelectedValues(newSelectedValues);
+ onValuesChanged(newSelectedValues);
+ };
+
+ return (
+
+ {items.map((item, index) => (
+
+
toggleCheckbox(item)}>
+
{item.text}
+
+
+ {index !== items.length - 1 &&
}
+
+ ))}
+
+ );
+};
+
+export default MultiChoice;
diff --git a/src/components/Rating/index.test.tsx b/src/components/Rating/index.test.tsx
new file mode 100644
index 0000000..a85c2b5
--- /dev/null
+++ b/src/components/Rating/index.test.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+
+import { act, render, screen, within } from '@testing-library/react';
+
+import { answerFabricator } from 'tests/fabricator';
+import { Answer } from 'types/answer';
+import { DisplayType } from 'types/question';
+
+import Rating, { ratingDataTestIds } from '.';
+
+describe('Rating', () => {
+ const answers = answerFabricator.times(5);
+
+ it('renders a rating component', () => {
+ const ratingProps = {
+ questionId: 'question-id',
+ items: answers,
+ displayType: DisplayType.Star,
+ };
+
+ const onValueChanged = () => {
+ // Do nothing
+ };
+
+ render();
+
+ const rating = screen.getByTestId(ratingDataTestIds.base);
+
+ expect(rating).toBeVisible();
+ });
+
+ describe('given the last value is clicked', () => {
+ it('returns the proper value', () => {
+ let selectedValue = '';
+ const onValueChanged = (answer: Answer) => {
+ selectedValue = answer.id;
+ };
+ const ratingProps = {
+ questionId: 'question-id',
+ items: answers,
+ displayType: DisplayType.Star,
+ onValueChanged: onValueChanged,
+ };
+
+ render();
+
+ const rating = screen.getByTestId(ratingDataTestIds.base);
+
+ act(() =>
+ within(rating)
+ .getAllByRole('button', {
+ name: '⭐️',
+ })
+ .at(4)
+ ?.click()
+ );
+
+ expect(selectedValue).toBe('5');
+ });
+ });
+});
diff --git a/src/components/Rating/index.tsx b/src/components/Rating/index.tsx
new file mode 100644
index 0000000..fdabde6
--- /dev/null
+++ b/src/components/Rating/index.tsx
@@ -0,0 +1,70 @@
+import React, { useEffect, useState } from 'react';
+
+import { Answer } from 'types/answer';
+import { DisplayType } from 'types/question';
+
+export const ratingDataTestIds = {
+ base: 'rating__base',
+};
+
+interface RatingProps {
+ questionId: string;
+ items: Answer[];
+ displayType: DisplayType;
+ onValueChanged: (answer: Answer) => void;
+}
+
+const Rating = ({ questionId, items, displayType, onValueChanged }: RatingProps): JSX.Element => {
+ const [selectedIndex, setSelectedIndex] = useState(0);
+
+ const handleOnSelectRating = (index: number, answer: Answer) => {
+ setSelectedIndex(index);
+
+ onValueChanged(answer);
+ };
+
+ const faceModes = ['😡', '😕', '😐', '🙂', '😄'];
+
+ const generateDisplayTypeContent = (index: number): string => {
+ switch (displayType) {
+ case DisplayType.Heart:
+ return '❤️';
+ case DisplayType.Star:
+ return '⭐️';
+ case DisplayType.Smiley:
+ return faceModes.at(index) ?? faceModes[4];
+ case DisplayType.Thumbs:
+ default:
+ return '👍🏻';
+ }
+ };
+
+ const buildClassAttribute = (index: number): string => {
+ if (displayType === DisplayType.Smiley) {
+ return selectedIndex === index ? '' : 'opacity-50';
+ } else {
+ return selectedIndex >= index ? '' : 'opacity-50';
+ }
+ };
+
+ useEffect(() => {
+ setSelectedIndex(0);
+ }, [questionId]);
+
+ return (
+
+ {items.map((item, index) => {
+ return (
+
+ );
+ })}
+
+ );
+};
+
+export default Rating;
diff --git a/src/components/TextArea/index.test.tsx b/src/components/TextArea/index.test.tsx
new file mode 100644
index 0000000..b548c91
--- /dev/null
+++ b/src/components/TextArea/index.test.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+
+import { render, screen } from '@testing-library/react';
+
+import { answerFabricator } from 'tests/fabricator';
+import { Answer } from 'types/answer';
+
+import TextArea, { textAreaDataTestIds } from '.';
+
+describe('TextArea', () => {
+ it('renders a text area component', () => {
+ const answers: Answer[] = answerFabricator.times(2);
+
+ render();
+
+ const textArea = screen.getByTestId(textAreaDataTestIds.base);
+
+ expect(textArea).toBeVisible();
+ });
+});
diff --git a/src/components/TextArea/index.tsx b/src/components/TextArea/index.tsx
new file mode 100644
index 0000000..6297657
--- /dev/null
+++ b/src/components/TextArea/index.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+
+import _get from 'lodash/get';
+
+import { Answer } from 'types/answer';
+
+export const textAreaDataTestIds = {
+ base: 'text-area__base',
+};
+
+type TextAreaProps = {
+ items: Answer[];
+ onValueChange?: (id: string, value: string) => void;
+};
+
+const TextArea = ({ items, onValueChange }: TextAreaProps): JSX.Element => {
+ const currentAnswer = _get(items, 0);
+
+ const handleOnChange = (event: React.ChangeEvent) => {
+ event.preventDefault();
+
+ const answerId = currentAnswer?.id ?? '';
+ onValueChange?.(answerId, event.target.value);
+ };
+
+ return (
+
+ );
+};
+
+export default TextArea;
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index fa7b901..1f9418d 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -2,15 +2,19 @@ import React from 'react';
import { RouteObject } from 'react-router-dom';
import DashBoardScreen from 'screens/Dashboard';
+import QuestionScreen from 'screens/Question';
import SignInScreen from 'screens/SignIn';
import SurveyScreen from 'screens/Survey';
import ProtectedRoute from './ProtectedRoute';
+export const questionPath = 'questions';
+
export const paths = {
root: '/',
signIn: '/sign-in',
survey: '/surveys/:id',
+ question: `/surveys/:id/${questionPath}`,
};
const routes: RouteObject[] = [
@@ -25,6 +29,10 @@ const routes: RouteObject[] = [
path: paths.survey,
element: ,
},
+ {
+ path: paths.question,
+ element: ,
+ },
],
},
{
diff --git a/src/screens/Question/index.test.tsx b/src/screens/Question/index.test.tsx
new file mode 100644
index 0000000..d831dcc
--- /dev/null
+++ b/src/screens/Question/index.test.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+
+import { render, screen } from '@testing-library/react';
+
+import TestWrapper from 'tests/TestWrapper';
+
+import QuestionScreen, { questionScreenTestIds } from '.';
+
+describe('QuestionScreen', () => {
+ const TestComponent = (): JSX.Element => {
+ return (
+
+
+
+ );
+ };
+
+ it('renders Question screen and its components', () => {
+ render();
+
+ const currentIndex = screen.getByTestId(questionScreenTestIds.index);
+ const questionTitle = screen.getByTestId(questionScreenTestIds.title);
+ const closeButton = screen.getByTestId(questionScreenTestIds.closeButton);
+ const nextButton = screen.getByTestId(questionScreenTestIds.nextButton);
+
+ expect(currentIndex).toBeVisible();
+ expect(questionTitle).toBeVisible();
+ expect(closeButton).toBeVisible();
+ expect(nextButton).toBeVisible();
+ });
+});
diff --git a/src/screens/Question/index.tsx b/src/screens/Question/index.tsx
new file mode 100644
index 0000000..cd5d7bb
--- /dev/null
+++ b/src/screens/Question/index.tsx
@@ -0,0 +1,53 @@
+import React from 'react';
+
+import { ReactComponent as ArrowRight } from 'assets/images/icons/arrow-right.svg';
+import { ReactComponent as CloseButton } from 'assets/images/icons/close-btn.svg';
+import AppSlider from 'components/AppSlider';
+import MainView from 'components/MainView';
+
+export const questionScreenTestIds = {
+ index: 'question__index',
+ title: 'question__title',
+ closeButton: 'question__close-button',
+ nextButton: 'question__next-button',
+};
+
+const QuestionScreen = (): JSX.Element => {
+ return (
+
+
+
+
+
+ 1/5
+
+
+ How fulfilled did you feel during this WFH period?
+
+
{
+ // TODO
+ }}
+ />
+
+
+
+
+ );
+};
+
+export default QuestionScreen;
diff --git a/src/screens/Survey/index.tsx b/src/screens/Survey/index.tsx
index 035f3cb..b85d463 100644
--- a/src/screens/Survey/index.tsx
+++ b/src/screens/Survey/index.tsx
@@ -1,5 +1,5 @@
import React, { useEffect } from 'react';
-import { useNavigate, useParams } from 'react-router-dom';
+import { Link, useNavigate, useParams } from 'react-router-dom';
import { toast } from 'react-toastify';
import { ReactComponent as ArrowBack } from 'assets/images/icons/arrow-back.svg';
@@ -7,6 +7,7 @@ import ElevatedButton from 'components/ElevatedButton';
import LoadingDialog from 'components/LoadingDialog';
import MainView from 'components/MainView';
import { useAppDispatch, useAppSelector } from 'hooks';
+import { questionPath } from 'routes';
import { getSurveyAsyncThunk } from 'store/reducers/Survey';
export const surveyScreenTestIds = {
@@ -71,9 +72,11 @@ const SurveyScreen = (): JSX.Element => {
{survey?.description}
-
- Start Survey
-
+
+
+ Start Survey
+
+
diff --git a/src/tests/fabricator.ts b/src/tests/fabricator.ts
index bea02bf..58cc8c3 100644
--- a/src/tests/fabricator.ts
+++ b/src/tests/fabricator.ts
@@ -78,6 +78,12 @@ export const surveyFabricator = Fabricator({
coverImageUrl: faker.image.url(),
});
+export const answerFabricator = Fabricator({
+ id: () => sequence().toString(),
+ resourceType: 'answer',
+ text: () => faker.string.sample(),
+});
+
// States
export const surveyStateFabricator = Fabricator({
survey: () => surveyFabricator(),
diff --git a/src/types/question.ts b/src/types/question.ts
index d4ae116..0bf2e06 100644
--- a/src/types/question.ts
+++ b/src/types/question.ts
@@ -16,3 +16,22 @@ export interface Question extends Resource {
coverBackgroundColor?: string;
answers: Answer[];
}
+
+export enum DisplayType {
+ Intro = 'intro',
+ Star = 'star',
+ Heart = 'heart',
+ Smiley = 'smiley',
+ Thumbs = 'thumbs',
+ Choice = 'choice',
+ Nps = 'nps',
+ Textarea = 'textarea',
+ Textfield = 'textfield',
+ Dropdown = 'dropdown',
+ Outro = 'outro',
+ Unknown = '',
+}
+
+export const getDisplayTypeEnum = (question: Question): DisplayType => {
+ return question.displayType ? (DisplayType)[question.displayType] : DisplayType.Unknown;
+};