diff --git a/README.md b/README.md index 512e715..d48683a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ ![React Quiz App Template Cover Image](./src/assets/images/Xeven-Quiz-ReactJS-Quiz-App-Template.jpg) # Xeven Quiz - ReactJS Quiz App Template -With **Xeven Quiz**, you don't have to spend hours coding from scratch. Our template provides a solid foundation, eliminating the need to reinvent the wheel. You'll spend less time developing your app, which lets you focus on the unique. Xeven Quiz helps you follow industry best practices and coding conventions as a beginner. +With **Xeven Quiz**, you don't have to spend hours on coding from scratch. This Quiz App template provides a solid foundation, eliminating the need to reinvent the wheel. You'll spend less time developing your app, which lets you focus on the unique. -As a beginner developer, a quiz app is a common project. But without guidance, this simple project can become difficult. Xeven Quiz is here to change that narrative and give you the tools and knowledge you need to create a successful and efficient quiz app. +Xeven Quiz helps you follow industry best practices and coding conventions as a beginner. + +As a beginner developer, a quiz app is a common project. But without guidance, this simple project can become difficult. + +Xeven Quiz is here to change that narrative and give you the tools and knowledge you need to create a successful and efficient quiz app. With a strong architecture and modular design, you can easily add new features, expand your question database, and accommodate a growing user base without experiencing any setbacks. @@ -21,23 +25,39 @@ With **ReactJS** at its core, it lets you design dynamic interfaces that automat ## React Quiz App's Theme Features -Before I explain the technical aspects of the quiz app, let me share the wonderful features of the quiz app that is available in demo GitHub version. You can get all these features in a template and mold them according to your choice. +Before I explain the technical aspects of the quiz app, let me share the wonderful features of the quiz app. You can get all these features in a template and mold them according to your choice. + +- Seamlessly switch between Light and Dark modes with just a single click from the top menu. + +- The user can pick a quiz topic on the first screen, like JavaScript, React, or General Knowledge. -- Each time the quiz starts, questions will be shuffled or randomized. - There will be a timer running when the quiz starts. If the timer finishes, the quiz will be stopped, and the user will be asked to see the result. -- Each question has a score. For example, a difficult question has 10 marks, and an easier one has 5. + - The template also supports three types of questions, MCQs, True/False, and MAQs. + +- The template allows for adding code snippets in questions. You can easily assess the users' programming knowledge and skills. + +- The template allows you to create questions with images to enhance user engagement. + +- Each question has a score. For example, a difficult question has 10 marks, and an easier one has 5. + - The result screen shows how many questions the user attempted, how much he scored, how long it took, and whether he passed or failed. + - In result screen user can see which question had the right answer and which was wrong. The user can find the correct answer in case of a wrong answer. ## React Quiz App's Code Features -- **TypeScript powered Components:** All components are TypeScript-built for enhanced development productivity with intelligent code completion and compile-time error checking. -- **Easy Theme Customization:** The template provides easy theme management with IntelliSense support via Styled Components and Typescript. It allows you to customize the app's appearance without relying on hard-coded colors. -- **Flexible Question Data Structure:** The template uses Javascript/TypeScript files to define quiz questions. This format provides a structured and flexible approach. The same format can also be used to fetch questions from an API if desired. -- **Modular and Context Pattern:** The template follows a javascript modular and React Context pattern, promoting component reusability and maintainability. -- **Custom Hooks for Logic Sharing:** The hook pattern lets you share logic across components. It also promotes code reuse and minimizes code clutter. -- **Built with React Best Practices:** App follows industry-leading React practices. Our template ensures optimal structure, scalability, and maintainability. +- **TypeScript powered Components**: All components are TypeScript-built for enhanced development productivity with intelligent code completion and compile-time error checking. + +- **Easy Theme Customization**: The template provides easy theme management with IntelliSense support via Styled Components and Typescript. It allows you to customize the app's appearance without relying on hard-coded colors. + +- **Flexible Question Data Structure**: The template uses Javascript/TypeScript files to define quiz questions. This format provides a structured and flexible approach. The same format can also be used to fetch questions from an API if desired. + +- **Modular and Context Pattern**: The template follows a javascript modular and React Context pattern, promoting component reusability and maintainability. + +- **Custom Hooks for Logic Sharing**: The hook pattern lets you share logic across components. It also promotes code reuse and minimizes code clutter. + +- **Built with React Best Practices**: App follows industry-leading React practices. Our template ensures optimal structure, scalability, and maintainability. ## Xeven Quiz - ReactJS Quiz App Template Code Documentations @@ -74,18 +94,19 @@ Understanding the folder structure is essential for working with the app. Here's ### Components Architecture -The **Xeven Quiz App** consists of 4 main screens/components that are displayed conditionally (in light version): +The **Xeven Quiz App** consists of 5 main screens/components that are displayed conditionally: 1. Splash Screen -2. Quiz Details Screen -3. Questions Screen -4. Result Screen +2. Quiz Topics Screen +3. Quiz Details Screen +4. Questions Screen +5. Result Screen -The screens are organized in the **`components`** folder since the app does not utilize routing. If a component is reusable and can be used in multiple places within the app (e.g., Button, ModalWrapper, and CodeSnippet), it is placed in the **`components/UI`** folder. On the other hand, if a component is screen-specific and separated just to make other components smaller and more manageable, it is placed in the relevant components folder. For example, the components `QuizHeader`, `Question`, and `Answer` are inside the **`QuestionScreen`** folder. +The screens are organized in the **`components`** folder since the app does not utilize routing. If a component is reusable and can be used in multiple places within the app (e.g., Button, ModalWrapper, and CodeSnippet), it is placed in the **`components/UI`** folder. On the other hand, if a component is screen-specific and separated just to make other components smaller and more manageable, it is placed in the relevant components folder. For example, the components `**QuizHeader**`, `**Question**`, and `**Answer**` are inside the **`QuestionScreen`** folder. ## How to customize the quiz layout and styling -### **Changing the App Theme** +### Changing the App Theme To change the theme of the app, follow these steps: @@ -102,7 +123,7 @@ To change the font of the app, follow these steps: 4. Go to the **`theme`** file and change the font name. 5. Go to the global styles and update the font in the **`body`** section. -### **Modifying the Quiz Topic Screen or Adding New Categories** +### Modifying the Quiz Topic Screen or Adding New Categories To modify the Quiz Topics Screen or add new categories of topics/icons, follow these steps: @@ -198,6 +219,38 @@ function Main() { export default Main ``` +### Adding Pictures/Images to Questions + +In addition to text questions, you can also include images or pictures to enhance your questions. To add an image to a question, follow these steps: + +**1. Upload the Image** + +Start by placing your image file in the **`src/assets/images`** folder within your project directory. + +**2. Import the Image** + +Import the image in the quiz category data where you want to show it. For more details see `src/data/QuizQuestions/generalKnowledge.ts` + +**3. Link the Image to Your Question** + +Within your question object, add an image key. Then, reference the image you imported in step 2 at the top of your question. + +![add question with picture](./src/assets/images/add-images-to-questions.png) + +### How to Add Code Snippets in Questions + +Just like images, each question supports a **`code`** key, which is conditionally shown only if the question contains a code snippet. + +### How to format code snippet + +In the Xeven Quiz App, code snippets are pieces of code represented as text. To make them look nice and readable, we use an npm package called **`prismjs`**. This tool highlights the code with different colors so that it stands out and is easy to understand. + +To display code correctly, we need to pay attention to the spaces and how the code is structured, just like we do with the existing questions. This way, the code will appear neatly formatted and will be easier for users to read and comprehend. + +Here's an example image to illustrate the correct format for displaying code snippets: + +![code snippet format](./src/assets/images/code-snippet-format.png) + ### Implementing Different Types of Quiz Questions The Code Quiz App supports various types of quiz questions, including Multiple Choice Questions (MCQs), Multiple Answer Questions (MAQs), and True/False questions. To add different question types, you can modify the question components and their associated data structures. You can refer to the existing question formats in the **`data/QuizQuestions`** folder as examples when creating new questions. @@ -218,10 +271,11 @@ Discover the Premium Version! Elevate your experience with the same amazing feat ### Premium Version Highlights -- Elevate user experience by seamlessly toggling between light and dark modes with a single click from the top menu. -- The user can pick a quiz topic on the first screen, like JavaScript, React, or Python. +Almost all the features I have added in this GitHub open source repo. + +- Every time the quiz starts, the questions will be shuffled or randomized + - Users can skip a question if they are unsure of the answer. They can return to it later and answer it before submitting the quiz. -- The PRO version also allows for adding code snippets in questions. You can easily assess the users' programming knowledge and skills. ## **Deploying the Quiz App to a Production Environment** diff --git a/src/App.tsx b/src/App.tsx index 6e59183..5fd742b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,17 +1,41 @@ +import { useState } from 'react' import { ThemeProvider } from 'styled-components' import Main from './components/Main' -import QuizProvider from './context/QuizContext' +import ToggleTheme from './components/ui/ToggleTheme' +import QuizProvider from './context/QuizProvider' import { GlobalStyles } from './styles/Global' -import { theme as AppTheme } from './styles/Theme' +import { themes } from './styles/Theme' -const App = () => ( - - - -
- - -) +function App() { + const [currentTheme, setCurrentTheme] = useState(() => { + const savedTheme = localStorage.getItem('theme') + return savedTheme || 'light' + }) + + const toggleTheme = (e: React.ChangeEvent) => { + const { checked } = e.target + setCurrentTheme(checked ? 'dark' : 'light') + localStorage.setItem('theme', checked ? 'dark' : 'light') + } + + const theme = currentTheme === 'light' ? themes.light : themes.dark + + return ( + + + + +
+ + + ) +} export default App diff --git a/src/assets/icons/app-logo.svg b/src/assets/icons/app-logo.svg index 7964adc..151adbc 100644 --- a/src/assets/icons/app-logo.svg +++ b/src/assets/icons/app-logo.svg @@ -1,21 +1,5 @@ - - - - - - - - - - - - - - - - - - - - + + + + diff --git a/src/assets/icons/bulb.svg b/src/assets/icons/bulb.svg new file mode 100644 index 0000000..9cad1f2 --- /dev/null +++ b/src/assets/icons/bulb.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/assets/icons/colored-logo.svg b/src/assets/icons/colored-logo.svg deleted file mode 100644 index 6d642ff..0000000 --- a/src/assets/icons/colored-logo.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/icons/logo.svg b/src/assets/icons/logo.svg deleted file mode 100644 index ec16e54..0000000 --- a/src/assets/icons/logo.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/icons/moon.svg b/src/assets/icons/moon.svg new file mode 100644 index 0000000..85bc89b --- /dev/null +++ b/src/assets/icons/moon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/sun.svg b/src/assets/icons/sun.svg new file mode 100644 index 0000000..b4ebe6f --- /dev/null +++ b/src/assets/icons/sun.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/images/Xeven-Quiz-ReactJS-Quiz-App-Template.jpg b/src/assets/images/Xeven-Quiz-ReactJS-Quiz-App-Template.jpg deleted file mode 100644 index 1656627..0000000 Binary files a/src/assets/images/Xeven-Quiz-ReactJS-Quiz-App-Template.jpg and /dev/null differ diff --git a/src/assets/images/add-images-to-questions.png b/src/assets/images/add-images-to-questions.png new file mode 100644 index 0000000..03e525d Binary files /dev/null and b/src/assets/images/add-images-to-questions.png differ diff --git a/src/assets/images/brand-logo.jpg b/src/assets/images/brand-logo.jpg new file mode 100644 index 0000000..a17880e Binary files /dev/null and b/src/assets/images/brand-logo.jpg differ diff --git a/src/assets/images/car.jpg b/src/assets/images/car.jpg new file mode 100644 index 0000000..eaefdb9 Binary files /dev/null and b/src/assets/images/car.jpg differ diff --git a/src/assets/images/code-snippet-1.png b/src/assets/images/code-snippet-1.png new file mode 100644 index 0000000..b061505 Binary files /dev/null and b/src/assets/images/code-snippet-1.png differ diff --git a/src/assets/images/dish.jpg b/src/assets/images/dish.jpg new file mode 100644 index 0000000..605f2da Binary files /dev/null and b/src/assets/images/dish.jpg differ diff --git a/src/assets/images/mosque.jpg b/src/assets/images/mosque.jpg new file mode 100644 index 0000000..235f9ad Binary files /dev/null and b/src/assets/images/mosque.jpg differ diff --git a/src/assets/images/place.jpg b/src/assets/images/place.jpg new file mode 100644 index 0000000..6da6f66 Binary files /dev/null and b/src/assets/images/place.jpg differ diff --git a/src/assets/images/reptile.jpg b/src/assets/images/reptile.jpg new file mode 100644 index 0000000..4991bd1 Binary files /dev/null and b/src/assets/images/reptile.jpg differ diff --git a/src/components/Main/index.tsx b/src/components/Main/index.tsx index b130dd8..a2e65fe 100644 --- a/src/components/Main/index.tsx +++ b/src/components/Main/index.tsx @@ -5,6 +5,7 @@ import { ScreenTypes } from '../../types' import QuestionScreen from '../QuestionScreen' import QuizDetailsScreen from '../QuizDetailsScreen' +import QuizTopicsScreen from '../QuizTopicsScreen' import ResultScreen from '../ResultScreen' import SplashScreen from '../SplashScreen' @@ -13,12 +14,13 @@ function Main() { useEffect(() => { setTimeout(() => { - setCurrentScreen(ScreenTypes.QuizDetailsScreen) + setCurrentScreen(ScreenTypes.QuizTopicsScreen) }, 1000) }, [setCurrentScreen]) const screenComponents = { [ScreenTypes.SplashScreen]: , + [ScreenTypes.QuizTopicsScreen]: , [ScreenTypes.QuizDetailsScreen]: , [ScreenTypes.QuestionScreen]: , [ScreenTypes.ResultScreen]: , diff --git a/src/components/QuestionScreen/Answer/index.tsx b/src/components/QuestionScreen/Answer/index.tsx index bd55968..9a30218 100644 --- a/src/components/QuestionScreen/Answer/index.tsx +++ b/src/components/QuestionScreen/Answer/index.tsx @@ -8,9 +8,9 @@ const AnswerStyle = styled.div<{ highlightAnswer: boolean }>` font-weight: 400; border: 1px solid ${({ highlightAnswer, theme }) => - highlightAnswer ? `${theme.colors.themeColor}` : `${theme.colors.lightGray}`}; + highlightAnswer ? `${theme.colors.themeColor}` : `${theme.colors.border}`}; background-color: ${({ highlightAnswer, theme }) => - highlightAnswer ? `${theme.colors.lightPink}` : `${theme.colors.white}`}; + highlightAnswer ? `${theme.colors.selectedAnswer}` : `${theme.colors.answerBg}`}; border-radius: 16px; margin-top: clamp(13px, calc(10px + 6 * ((100vw - 600px) / 1320)), 16px); cursor: pointer; @@ -29,7 +29,7 @@ const AnswerStyle = styled.div<{ highlightAnswer: boolean }>` ` const AnswerLabel = styled.label` - padding: 16px; + padding: 18px; display: flex; cursor: pointer; @media ${device.md} { diff --git a/src/components/QuestionScreen/Question/index.tsx b/src/components/QuestionScreen/Question/index.tsx index 3da6775..5c5acd1 100644 --- a/src/components/QuestionScreen/Question/index.tsx +++ b/src/components/QuestionScreen/Question/index.tsx @@ -3,7 +3,9 @@ import styled from 'styled-components' import { device } from '../../../styles/BreakPoints' +import CodeSnippet from '../../ui/CodeSnippet' import Answer from '../Answer' +import QuizImage from '../../ui/QuizImage' const QuestionContainer = styled.div` margin-top: 30px; @@ -31,6 +33,8 @@ const QuestionStyle = styled.h2` interface QuestionTypes { question: string + code?: string + image?: string type: string choices: string[] selectedAnswer: string[] @@ -39,6 +43,8 @@ interface QuestionTypes { const Question: FC = ({ question, + code, + image, type, choices, selectedAnswer, @@ -47,6 +53,10 @@ const Question: FC = ({ return ( {question} + {/* if question contains code snippet then show code */} + {code && } + {/* if question contains an image */} + {image && } {choices.map((choice, index) => ( ` width: 900px; min-height: 500px; - background: ${({ theme }) => theme.colors.white}; + background: ${({ theme }) => theme.colors.cardBackground}; border-radius: 4px; padding: 30px 60px 80px 60px; margin-bottom: 70px; @@ -30,7 +30,7 @@ const QuizContainer = styled.div<{ selectedAnswer: boolean }>` svg { path { fill: ${({ selectedAnswer, theme }) => - selectedAnswer ? `${theme.colors.white}` : `${theme.colors.darkGrayText}`}; + selectedAnswer ? `${theme.colors.buttonText}` : `${theme.colors.darkGray}`}; } } } @@ -71,7 +71,6 @@ const QuestionScreen: FC = () => { const { questions, - setQuestions, quizDetails, result, setResult, @@ -83,7 +82,7 @@ const QuestionScreen: FC = () => { const currentQuestion = questions[activeQuestion] - const { question, type, choices, correctAnswers } = currentQuestion + const { question, type, choices, code, image, correctAnswers } = currentQuestion const onClickNext = () => { const isMatch: boolean = @@ -107,6 +106,16 @@ const QuestionScreen: FC = () => { const handleAnswerSelection = (e: React.ChangeEvent) => { const { name, checked } = e.target + if (type === 'MAQs') { + if (selectedAnswer.includes(name)) { + setSelectedAnswer((prevSelectedAnswer) => + prevSelectedAnswer.filter((element) => element !== name) + ) + } else { + setSelectedAnswer((prevSelectedAnswer) => [...prevSelectedAnswer, name]) + } + } + if (type === 'MCQs' || type === 'boolean') { if (checked) { setSelectedAnswer([name]) @@ -142,6 +151,8 @@ const QuestionScreen: FC = () => { /> theme.colors.themeColor}; - margin-top: 34px; ` const DetailTextContainer = styled.div` @@ -41,13 +44,12 @@ const QuizDetailsScreen = () => { setCurrentScreen(ScreenTypes.QuestionScreen) } - // to shuffle or randomize quiz questions - useShuffleQuestions() - return ( - + + + XEVEN QUIZ @@ -63,12 +65,17 @@ const QuizDetailsScreen = () => { Total time: {convertSeconds(totalTime)} + + To save time, you can skip questions. Skipped questions will show up at the + end of the quiz. + } iconPosition="left" onClick={goToQuestionScreen} + bold /> diff --git a/src/components/QuizTopicsScreen/index.tsx b/src/components/QuizTopicsScreen/index.tsx new file mode 100644 index 0000000..2313fbc --- /dev/null +++ b/src/components/QuizTopicsScreen/index.tsx @@ -0,0 +1,117 @@ +import styled from 'styled-components' + +import { AppLogo } from '../../config/icons' +import { useQuiz } from '../../context/QuizContext' +import { quizTopics } from '../../data/quizTopics' +import { device } from '../../styles/BreakPoints' +import { + CenterCardContainer, + HighlightedText, + LogoContainer, + PageCenter, +} from '../../styles/Global' +import { ScreenTypes } from '../../types' + +import Button from '../ui/Button' + +const Heading = styled.h2` + font-size: 32px; + font-weight: 700; + margin-bottom: 20px; + text-align: center; +` + +const DetailText = styled.p` + font-weight: 500; + font-size: 20px; + line-height: 29px; + text-align: center; +` + +const SelectButtonContainer = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: center; + max-width: 60%; + gap: 30px; + margin-top: 40px; + margin-bottom: 45px; + @media ${device.md} { + row-gap: 20px; + column-gap: 20px; + max-width: 100%; + } +` + +interface SelectButtonProps { + active: boolean + disabled?: boolean +} + +const SelectButton = styled.div` + background-color: ${({ disabled, theme }) => + disabled ? `${theme.colors.disabledCard}` : `${theme.colors.selectTopicBg}`}; + border: ${({ active, theme }) => + active + ? `2px solid ${theme.colors.themeColor}` + : `1px solid ${theme.colors.disabledButton}`}; + transition: background-color 0.4s ease-out; + border-radius: 10px; + padding: 14px 10px; + display: flex; + align-items: center; + cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; + @media ${device.md} { + padding: 10px; + tap-highlight-color: transparent; + -webkit-tap-highlight-color: transparent; + } +` + +const SelectButtonText = styled.span` + font-size: 18px; + font-weight: 600; + margin-left: 10px; + @media ${device.md} { + font-size: 16px; + font-weight: 500; + } +` + +const QuizTopicsScreen: React.FC = () => { + const { quizTopic, selectQuizTopic, setCurrentScreen } = useQuiz() + + const goToQuizDetailsScreen = () => { + setCurrentScreen(ScreenTypes.QuizDetailsScreen) + } + + return ( + + + + + + + WELCOME TO XEVEN QUIZ + + Select topic below to start your Quiz. + + {quizTopics.map(({ title, icon, disabled }) => ( + !disabled && selectQuizTopic(title)} + disabled={disabled} + > + {icon} + {title} + + ))} + + + + + ) +} + +export default QuizTopicsScreen diff --git a/src/components/ResultScreen/RightAnswer/index.tsx b/src/components/ResultScreen/RightAnswer/index.tsx index 0627b11..091f754 100644 --- a/src/components/ResultScreen/RightAnswer/index.tsx +++ b/src/components/ResultScreen/RightAnswer/index.tsx @@ -2,7 +2,6 @@ import { FC } from 'react' import styled from 'styled-components' import { HighlightedText } from '../../../styles/Global' -import { theme } from '../../../styles/Theme' interface RightAnswerProps { correctAnswers: string[] @@ -25,7 +24,7 @@ const RightAnswer: FC = ({ correctAnswers, choices }) => { const label = String.fromCharCode(65 + choices.indexOf(item)) return ( - + {`${label} (${item})${index !== correctAnswers.length - 1 ? ', ' : ''}`} ) diff --git a/src/components/ResultScreen/index.tsx b/src/components/ResultScreen/index.tsx index c33a6e4..4dfd4a9 100644 --- a/src/components/ResultScreen/index.tsx +++ b/src/components/ResultScreen/index.tsx @@ -1,35 +1,30 @@ import { FC } from 'react' import styled, { css } from 'styled-components' -import { AppColoredLogo, Refresh } from '../../config/icons' +import { AppLogo, Refresh } from '../../config/icons' import { useQuiz } from '../../context/QuizContext' import { device } from '../../styles/BreakPoints' -import { Flex, ResizableBox } from '../../styles/Global' +import { Flex, LogoContainer, ResizableBox } from '../../styles/Global' import { refreshPage } from '../../utils/helpers' import Button from '../ui/Button' +import CodeSnippet from '../ui/CodeSnippet' +import QuizImage from '../ui/QuizImage' import ResultOverview from './ResultOverview' import RightAnswer from './RightAnswer' const ResultScreenContainer = styled.div` max-width: 900px; - margin: 80px auto; + margin: 60px auto; @media ${device.md} { width: 90%; margin: 30px auto; - } -` - -const LogoContainer = styled.div` - text-align: center; - margin-bottom: 50px; - @media ${device.md} { - margin-bottom: 30px; + padding-top: 40px; } ` const InnerContainer = styled.div` - background: ${({ theme }) => theme.colors.white}; + background: ${({ theme }) => theme.colors.cardBackground}; border-radius: 4px; margin: 0 auto; margin-bottom: 40px; @@ -72,11 +67,12 @@ interface AnswerProps { } const Answer = styled.li` - border: 1px solid ${({ theme }) => theme.colors.lightGray}; + border: 1px solid ${({ theme }) => theme.colors.border}; width: 90%; @media ${device.md} { width: 100%; } + background: ${({ theme }) => theme.colors.answerBg}; border-radius: 16px; font-size: clamp(16px, 5vw, 18px); font-weight: 600; @@ -132,13 +128,22 @@ const ResultScreen: FC = () => { return ( - + {result.map( ( - { question, choices, correctAnswers, selectedAnswer, score, isMatch }, + { + question, + choices, + code, + image, + correctAnswers, + selectedAnswer, + score, + isMatch, + }, index: number ) => { return ( @@ -149,6 +154,8 @@ const ResultScreen: FC = () => { {question}
+ {code && } + {image && }
    {choices.map((ans: string, index: number) => { // Convert index to alphabet character @@ -184,6 +191,7 @@ const ResultScreen: FC = () => { onClick={onClickRetry} icon={} iconPosition="left" + bold /> diff --git a/src/components/ui/Button/index.tsx b/src/components/ui/Button/index.tsx index f389f47..4d7be54 100644 --- a/src/components/ui/Button/index.tsx +++ b/src/components/ui/Button/index.tsx @@ -7,6 +7,8 @@ interface ButtonTypes { icon?: ReactNode iconPosition?: 'left' | 'right' outline?: boolean + bold?: boolean + big?: boolean disabled?: boolean } @@ -16,10 +18,18 @@ const Button: FC = ({ icon, iconPosition, outline, + bold, + big, disabled, }) => { return ( - + {icon && iconPosition === 'left' && {icon}} {text} {icon && iconPosition === 'right' && {icon}} diff --git a/src/components/ui/Button/styled.tsx b/src/components/ui/Button/styled.tsx index 7bcfba7..8275ea3 100644 --- a/src/components/ui/Button/styled.tsx +++ b/src/components/ui/Button/styled.tsx @@ -2,31 +2,36 @@ import styled from 'styled-components' import { device } from '../../../styles/BreakPoints' interface ButtonType { outline?: boolean + bold?: boolean + big?: boolean } -export const ButtonStyle = styled.button.attrs(({ outline }: ButtonType) => ({ +export const ButtonStyle = styled.button.attrs(({ outline, bold, big }: ButtonType) => ({ outline, + bold, + big, }))` width: 195px; min-height: 50px; color: ${({ theme, outline }) => - outline ? theme.colors.themeColor : theme.colors.white}; + outline ? theme.colors.outlineButtonText : theme.colors.buttonText}; background: ${({ theme, outline }) => - outline ? theme.colors.white : theme.colors.themeGradient}; + outline ? theme.colors.cardBackground : theme.colors.buttonBackground}; font-size: clamp(16px, 5vw, 24px); border: 1px solid ${({ theme, outline }) => (!outline ? 'none' : theme.colors.themeColor)}; - font-weight: 400; + font-weight: ${({ bold }) => (bold ? '700' : '400')}; border-radius: 9px; display: flex; justify-content: center; align-items: center; @media ${device.md} { - width: 150px; + width: ${({ big }) => (big ? '180px' : '150px')}; min-height: 40px; tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent; } + &:active { transform: scale(0.98); box-shadow: ${({ theme }) => theme.shadows.activeButton}; @@ -34,7 +39,7 @@ export const ButtonStyle = styled.button.attrs(({ outline }: ButtonType) => ({ } &:disabled { background: ${({ theme }) => theme.colors.disabledButton}; - color: ${({ theme }) => theme.colors.darkGrayText}; + color: ${({ theme }) => theme.colors.darkGray}; cursor: not-allowed; transform: unset; box-shadow: unset; @@ -44,9 +49,19 @@ export const ButtonStyle = styled.button.attrs(({ outline }: ButtonType) => ({ export const IconLeft = styled.span` margin-right: 10px; display: flex; + svg { + path { + fill: ${({ theme }) => theme.colors.buttonText}; + } + } ` export const IconRight = styled.span` margin-left: 20px; display: flex; + svg { + path { + fill: ${({ theme }) => theme.colors.buttonText}; + } + } ` diff --git a/src/components/ui/CodeSnippet/index.tsx b/src/components/ui/CodeSnippet/index.tsx new file mode 100644 index 0000000..b01b304 --- /dev/null +++ b/src/components/ui/CodeSnippet/index.tsx @@ -0,0 +1,29 @@ +import Prism from 'prismjs' +import 'prismjs/themes/prism-solarizedlight.css' + +import React, { useEffect, useRef } from 'react' + +interface CodeSnippetProps { + code: string + language: string +} + +const CodeSnippet: React.FC = ({ code, language }) => { + const codeRef = useRef(null) + + useEffect(() => { + if (codeRef.current) { + Prism.highlightElement(codeRef.current) + } + }, [code]) + + return ( +
    +      
    +        {code}
    +      
    +    
    + ) +} + +export default CodeSnippet diff --git a/src/components/ui/ModalWrapper/index.tsx b/src/components/ui/ModalWrapper/index.tsx index 8c5b475..77ad7b0 100644 --- a/src/components/ui/ModalWrapper/index.tsx +++ b/src/components/ui/ModalWrapper/index.tsx @@ -19,7 +19,7 @@ const ModalContainer = styled.div` const ModalContent = styled.div` width: 600px; padding: 50px 25px; - background-color: ${({ theme }) => theme.colors.white}; + background: ${({ theme }) => theme.colors.cardBackground}; border-radius: 10px; display: flex; align-items: center; @@ -65,7 +65,7 @@ const ModalWrapper: FC = ({ {icon} {title} {subtitle} - +
+ ); +}; + +const App = () => { + return ; +}; + +export default App;`, + choices: ['Count: 0', 'Count: 1', 'Count: undefined', 'An error will occur'], + type: 'MCQs', + correctAnswers: ['Count: 0'], + score: 10, + }, { question: 'In React, props are used to pass data from parent components to child components.', @@ -53,6 +96,31 @@ export const react: Topic = { correctAnswers: ['True'], score: 5, }, + { + question: 'What is the output of the following code snippet?', + image: CodeSnippet1, + choices: ['0', '1', '2', 'undefined'], + type: 'MCQs', + correctAnswers: ['0'], + score: 10, + }, + { + question: + 'Which of the following are valid ways to conditionally render content in React? (Select all that apply)', + choices: [ + 'Using the if-else statement', + 'Using the ternary operator', + 'Using the switch statement', + 'Using the && operator', + ], + type: 'MAQs', + correctAnswers: [ + 'Using the if-else statement', + 'Using the ternary operator', + 'Using the && operator', + ], + score: 10, + }, { question: 'In React, what is the purpose of keys in lists?', choices: [ @@ -65,6 +133,37 @@ export const react: Topic = { correctAnswers: ['To provide a unique identifier for each item in the list'], score: 10, }, + { + question: 'What will be the result of the following React code?', + code: `import React from 'react'; + +class Button extends React.Component { + handleClick() { + console.log('Button clicked'); + } + + render() { + return ; + } +} + +const App = () => { + return