diff --git a/src/components/tab-navigation/TabNavigation.style.ts b/src/components/tab-navigation/TabNavigation.style.ts new file mode 100644 index 0000000000..507ac12709 --- /dev/null +++ b/src/components/tab-navigation/TabNavigation.style.ts @@ -0,0 +1,17 @@ +export const styles = { + tabs: { + display: 'flex', + borderBottom: '1px solid', + borderColor: 'primary.100', + mb: '24px' + }, + titleBox: { + display: 'flex', + alignItems: 'center', + gap: '16px', + '& > svg': { + width: '16px', + height: '16px' + } + } +} diff --git a/src/components/tab-navigation/TabNavigation.tsx b/src/components/tab-navigation/TabNavigation.tsx new file mode 100644 index 0000000000..22f4a530f7 --- /dev/null +++ b/src/components/tab-navigation/TabNavigation.tsx @@ -0,0 +1,46 @@ +import React, { ReactElement } from 'react' +import { useTranslation } from 'react-i18next' + +import Tab from '~/components/tab/Tab' +import Box from '@mui/material/Box' + +import { styles } from '~/components/tab-navigation/TabNavigation.style' + +interface TabsData { + [key: string]: { + title: string + content: ReactElement + icon: ReactElement + } +} + +interface TabNavigationProps { + activeTab: string + tabsData: TabsData + handleClick: (tab: string) => void +} + +const TabNavigation: React.FC = ({ + activeTab, + tabsData, + handleClick +}) => { + const { t } = useTranslation() + + const tabs = Object.keys(tabsData).map((key) => ( + handleClick(key)} + > + + {tabsData[key].icon} + {t(tabsData[key].title)} + + + )) + + return {tabs} +} + +export default TabNavigation diff --git a/src/constants/translations/en/my-resources-page.json b/src/constants/translations/en/my-resources-page.json index 8ef225f7ed..039a9eee15 100644 --- a/src/constants/translations/en/my-resources-page.json +++ b/src/constants/translations/en/my-resources-page.json @@ -35,7 +35,11 @@ "updated": "Last updates", "emptyItems": "You have no quizzes yet", "confirmDeletionTitle":"Do you confirm quiz deletion?", - "successDeletion":"Quiz was deleted successfully" + "successDeletion":"Quiz was deleted successfully", + "defaultNewTitle":"Untitled", + "defaultNewDescription":"Description...", + "createNewQuestion":"Create new question", + "addQuestion":"Add question" }, "questions": { "addBtn": "New question", diff --git a/src/constants/translations/ua/my-resources-page.json b/src/constants/translations/ua/my-resources-page.json index d882d4155e..ef025cbd1b 100644 --- a/src/constants/translations/ua/my-resources-page.json +++ b/src/constants/translations/ua/my-resources-page.json @@ -35,7 +35,11 @@ "updated": "Останнє оновлення", "emptyItems": "У вас ще немає тестів", "confirmDeletionTitle":"Ви підтверджуєте видалення тесту?", - "successDeletion":"Тест було успішно видалено" + "successDeletion":"Тест було успішно видалено", + "defaultNewTitle":"Без назви", + "defaultNewDescription":"Опис...", + "createNewQuestion":"Створити нове питання", + "addQuestion":"Додати питання" }, "questions": { "addBtn": "Нове запитання", diff --git a/src/containers/my-quizzes/edit-quiz-container/EditQuizContainer.styles.ts b/src/containers/my-quizzes/edit-quiz-container/EditQuizContainer.styles.ts new file mode 100644 index 0000000000..9ec5336ab8 --- /dev/null +++ b/src/containers/my-quizzes/edit-quiz-container/EditQuizContainer.styles.ts @@ -0,0 +1,63 @@ +import { TypographyVariantEnum, VisibilityEnum } from '~/types' + +export const styles = { + container: { + p: { sm: '0' } + }, + root: { + display: 'flex', + gap: '24px', + flexDirection: 'column', + p: '0' + }, + input: { + style: { + padding: 0, + margin: 0 + } + }, + titleInput: { + disableUnderline: true, + style: { + fontSize: '35px', + maxHeight: '35px', + fontWeight: 500, + marginTop: 0 + } + }, + descriptionInput: { + style: { fontSize: '16px', maxHeight: '16px', marginTop: 0 }, + disableUnderline: true + }, + titleLabel: (title: string) => ({ + style: { + visibility: title ? VisibilityEnum.Hidden : VisibilityEnum.Visible + }, + shrink: false, + sx: { typography: TypographyVariantEnum.H4, top: -23 } + }), + descriptionLabel: (description: string) => ({ + style: { + visibility: description ? VisibilityEnum.Hidden : VisibilityEnum.Visible + }, + sx: { typography: TypographyVariantEnum.Body1, top: -20 }, + shrink: false + }), + divider: { + color: 'primary.300' + }, + buttons: { + display: 'flex', + gap: { xs: '24px', sm: '30px' }, + justifyContent: 'space-between', + alignSelf: { xs: 'center', sm: 'end' } + }, + functionalButtons: { + display: 'flex', + gap: { xs: '24px', sm: '30px' }, + '& button': { + gap: '12px', + width: '100%' + } + } +} diff --git a/src/containers/my-quizzes/edit-quiz-container/EditQuizContainer.tsx b/src/containers/my-quizzes/edit-quiz-container/EditQuizContainer.tsx new file mode 100644 index 0000000000..a4625fe07f --- /dev/null +++ b/src/containers/my-quizzes/edit-quiz-container/EditQuizContainer.tsx @@ -0,0 +1,96 @@ +import { ChangeEvent, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { Link } from 'react-router-dom' +import Box from '@mui/material/Box' +import Divider from '@mui/material/Divider' +import EditIcon from '@mui/icons-material/Edit' +import AddIcon from '@mui/icons-material/Add' + +import AppButton from '~/components/app-button/AppButton' +import AppTextField from '~/components/app-text-field/AppTextField' +import PageWrapper from '~/components/page-wrapper/PageWrapper' + +import { myResourcesPath } from '~/pages/create-or-edit-lesson/CreateOrEditLesson.constants' +import { styles } from '~/containers/my-quizzes/edit-quiz-container/EditQuizContainer.styles' +import { + ButtonTypeEnum, + ButtonVariantEnum, + ComponentEnum, + SizeEnum, + TextFieldVariantEnum +} from '~/types' + +const EditQuizContainer = () => { + const { t } = useTranslation() + const [title, setTitle] = useState('') + const [description, setDescription] = useState('') + + const handleTitleChange = ( + e: ChangeEvent + ) => { + setTitle(e.currentTarget.value) + } + + const handleDescriptionChange = ( + e: ChangeEvent + ) => { + setDescription(e.currentTarget.value) + } + + return ( + + + handleTitleChange(e)} + variant={TextFieldVariantEnum.Standard} + /> + handleDescriptionChange(e)} + variant={TextFieldVariantEnum.Standard} + /> + + + + {t('myResourcesPage.quizzes.createNewQuestion')} + + + + {t('myResourcesPage.quizzes.addQuestion')} + + + + + + {t('common.cancel')} + + + {t('common.save')} + + + + + ) +} + +export default EditQuizContainer diff --git a/src/containers/my-quizzes/quiz-settings-container/QuizSettingsContainer.tsx b/src/containers/my-quizzes/quiz-settings-container/QuizSettingsContainer.tsx new file mode 100644 index 0000000000..5a372f846f --- /dev/null +++ b/src/containers/my-quizzes/quiz-settings-container/QuizSettingsContainer.tsx @@ -0,0 +1,7 @@ +import { Box } from '@mui/material' + +const QuizSettingsContainer = () => { + return Quiz settings +} + +export default QuizSettingsContainer diff --git a/src/containers/my-quizzes/view-quiz-container/ViewQuizContainer.tsx b/src/containers/my-quizzes/view-quiz-container/ViewQuizContainer.tsx new file mode 100644 index 0000000000..8597382542 --- /dev/null +++ b/src/containers/my-quizzes/view-quiz-container/ViewQuizContainer.tsx @@ -0,0 +1,7 @@ +import { Box } from '@mui/material' + +const ViewQuizContainer = () => { + return View quiz +} + +export default ViewQuizContainer diff --git a/src/pages/my-resources/MyResources.styles.ts b/src/pages/my-resources/MyResources.styles.ts index 78b288eb4d..4af36e718c 100644 --- a/src/pages/my-resources/MyResources.styles.ts +++ b/src/pages/my-resources/MyResources.styles.ts @@ -4,20 +4,5 @@ export const styles = { title: { typography: TypographyVariantEnum.H4, mb: '40px' - }, - tabs: { - display: 'flex', - borderBottom: '1px solid', - borderColor: 'primary.100', - mb: '24px' - }, - titleBox: { - display: 'flex', - alignItems: 'center', - gap: '16px', - '& > svg': { - width: '16px', - height: '16px' - } } } diff --git a/src/pages/my-resources/MyResources.tsx b/src/pages/my-resources/MyResources.tsx index 9a48a1ccb7..ce130d76b9 100644 --- a/src/pages/my-resources/MyResources.tsx +++ b/src/pages/my-resources/MyResources.tsx @@ -1,14 +1,12 @@ import { useState } from 'react' import { useTranslation } from 'react-i18next' -import Box from '@mui/material/Box' - import PageWrapper from '~/components/page-wrapper/PageWrapper' -import Tab from '~/components/tab/Tab' import Typography from '@mui/material/Typography' import { tabsData } from '~/pages/my-resources/MyResources.constants' import { styles } from '~/pages/my-resources/MyResources.styles' +import TabNavigation from '~/components/tab-navigation/TabNavigation' const MyResources = () => { const [activeTab, setActiveTab] = useState('lessons') @@ -18,25 +16,16 @@ const MyResources = () => { setActiveTab(tab) } - const tabs = Object.keys(tabsData).map((key) => ( - handleClick(key)} - > - - {tabsData[key].icon} - {t(tabsData[key].title)} - - - )) - const tabContent = activeTab && tabsData[activeTab].content return ( {t(tabsData[activeTab].title)} - {tabs} + {tabContent} ) diff --git a/src/pages/new-quiz/NewQuiz.constants.tsx b/src/pages/new-quiz/NewQuiz.constants.tsx new file mode 100644 index 0000000000..11d91a6165 --- /dev/null +++ b/src/pages/new-quiz/NewQuiz.constants.tsx @@ -0,0 +1,33 @@ +import { ReactElement } from 'react' +import EditIcon from '@mui/icons-material/Edit' +import VisibilityIcon from '@mui/icons-material/Visibility' +import SettingsIcon from '@mui/icons-material/Settings' + +import EditQuizContainer from '~/containers/my-quizzes/edit-quiz-container/EditQuizContainer' +import ViewQuizContainer from '~/containers/my-quizzes/view-quiz-container/ViewQuizContainer' +import QuizSettingsContainer from '~/containers/my-quizzes/quiz-settings-container/QuizSettingsContainer' + +interface TabsData { + [key: string]: { + title: string + content: ReactElement + icon: ReactElement + } +} +export const tabsData: TabsData = { + edit: { + title: 'Edit', + content: , + icon: + }, + quizzes: { + title: 'View', + content: , + icon: + }, + settings: { + title: 'Settings', + content: , + icon: + } +} diff --git a/src/pages/new-quiz/NewQuiz.styles.ts b/src/pages/new-quiz/NewQuiz.styles.ts new file mode 100644 index 0000000000..49225d4851 --- /dev/null +++ b/src/pages/new-quiz/NewQuiz.styles.ts @@ -0,0 +1,5 @@ +export const styles = { + container: { + px: { sm: '70px' } + } +} diff --git a/src/pages/new-quiz/NewQuiz.tsx b/src/pages/new-quiz/NewQuiz.tsx index 84bd21c6a0..e4154a9bf8 100644 --- a/src/pages/new-quiz/NewQuiz.tsx +++ b/src/pages/new-quiz/NewQuiz.tsx @@ -1,9 +1,28 @@ +import { useState } from 'react' + import PageWrapper from '~/components/page-wrapper/PageWrapper' +import { tabsData } from '~/pages/new-quiz/NewQuiz.constants' +import { styles } from '~/pages/new-quiz/NewQuiz.styles' +import TabNavigation from '~/components/tab-navigation/TabNavigation' + const NewQuiz = () => { + const [activeTab, setActiveTab] = useState('edit') + + const handleClick = (tab: string) => { + setActiveTab(tab) + } + + const tabContent = activeTab && tabsData[activeTab].content + return ( - -

New Quiz

+ + + {tabContent} ) } diff --git a/tests/unit/components/tab-navigation/TabNavigation.spec.jsx b/tests/unit/components/tab-navigation/TabNavigation.spec.jsx new file mode 100644 index 0000000000..057cadd4ad --- /dev/null +++ b/tests/unit/components/tab-navigation/TabNavigation.spec.jsx @@ -0,0 +1,47 @@ +import React from 'react' +import { render, fireEvent } from '@testing-library/react' +import TabNavigation from '~/components/tab-navigation/TabNavigation' + +describe('TabNavigation', () => { + const tabsData = { + tab1: { + title: 'Tab 1', + content:
Tab 1 Content
, + icon: Icon 1 + }, + tab2: { + title: 'Tab 2', + content:
Tab 2 Content
, + icon: Icon 2 + } + } + + it('renders TabNavigation correctly', () => { + const { getByText } = render( + {}} + tabsData={tabsData} + /> + ) + + expect(getByText('Tab 1')).toBeInTheDocument() + expect(getByText('Tab 2')).toBeInTheDocument() + expect(getByText('Icon 1')).toBeInTheDocument() + expect(getByText('Icon 2')).toBeInTheDocument() + }) + + it('calls handleClick when a tab is clicked', () => { + const handleClick = vi.fn() + const { getByText } = render( + + ) + + fireEvent.click(getByText('Tab 2')) + expect(handleClick).toHaveBeenCalledWith('tab2') + }) +})