Skip to content

Commit 63b3c8e

Browse files
authored
Merge pull request #13876 from TylerAPfledderer/refactor/shadcn-quiz-widget
[ShadCN] Migrate Quiz Components to ShadCN/Tailwind
2 parents 82d6d28 + 84464b2 commit 63b3c8e

22 files changed

+425
-524
lines changed

src/components/Quiz/QuizItem.tsx

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { useTranslation } from "next-i18next"
2-
import { Box, Flex, ListItem, Stack, Text } from "@chakra-ui/react"
32

43
import type { QuizzesSection } from "@/lib/types"
54

6-
import { Button } from "../Buttons"
5+
import { cn } from "@/lib/utils/cn"
6+
77
import { GreenTickIcon } from "../icons/quiz"
88
import Tag from "../Tag"
99
import Translation from "../Translation"
10+
import { Button } from "../ui/buttons/Button"
11+
import { Flex, Stack } from "../ui/flex"
12+
import { ListItem } from "../ui/list"
1013

1114
export type QuizzesListItemProps = Omit<QuizzesSection, "id"> & {
1215
isCompleted: boolean
@@ -26,37 +29,24 @@ const QuizItem = ({
2629

2730
return (
2831
<ListItem
29-
color={isCompleted ? "body.medium" : "text"}
30-
fontWeight="bold"
31-
px={{ base: 0, lg: 4 }}
32-
py={4}
33-
borderBottom="1px solid"
34-
borderColor="disabled"
35-
mb={0}
36-
sx={{ counterIncrement: "list-counter" }}
32+
className={cn(
33+
isCompleted ? "text-body-medium" : "text-body",
34+
"mb-0 border-b border-disabled py-4 font-bold [counter-increment:_list-counter] lg:px-4"
35+
)}
3736
>
38-
<Flex
39-
justifyContent="space-between"
40-
alignItems={{ base: "flex-start", lg: "center" }}
41-
direction={{ base: "column", lg: "row" }}
42-
>
43-
<Stack mb={{ base: 5, lg: 0 }}>
44-
<Flex gap={2} alignItems="center">
45-
<Text
46-
color={isCompleted ? "body.medium" : "text"}
47-
_before={{
48-
content: 'counter(list-counter) ". "',
49-
}}
50-
>
37+
<Flex className="justify-between max-lg:flex-col lg:items-center">
38+
<Stack className="max-lg:mb-5">
39+
<Flex className="items-center gap-2">
40+
<span className="before:content-[counter(list-counter)_'._']">
5141
<Translation id={titleId} />
52-
</Text>
42+
</span>
5343

5444
{/* Show green tick if quizz was completed only */}
5545
{isCompleted && <GreenTickIcon />}
5646
</Flex>
5747

5848
{/* Labels */}
59-
<Flex gap={3}>
49+
<Flex className="gap-3">
6050
{/* number of questions - label */}
6151
<Tag
6252
label={t(`${numberOfQuestions} ${t("questions")}`)}
@@ -69,15 +59,15 @@ const QuizItem = ({
6959
</Stack>
7060

7161
{/* Start Button */}
72-
<Box w={{ base: "full", lg: "auto" }}>
62+
<div className="max-lg:w-full">
7363
<Button
7464
variant="outline"
75-
w={{ base: "full", lg: "auto" }}
65+
className="max-lg:w-full"
7666
onClick={handleStart}
7767
>
7868
<Translation id="learn-quizzes:start" />
7969
</Button>
80-
</Box>
70+
</div>
8171
</Flex>
8272
</ListItem>
8373
)
Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import * as React from "react"
2-
import { Circle, SquareProps } from "@chakra-ui/react"
32

43
import { ChildOnlyProp } from "@/lib/types"
54

5+
import { Center } from "@/components/ui/flex"
6+
7+
import { cn } from "@/lib/utils/cn"
8+
69
import { CorrectIcon, IncorrectIcon, TrophyIcon } from "../../icons/quiz"
710

811
import { AnswerStatus } from "./useQuizWidget"
@@ -17,43 +20,46 @@ type AnswerIconProps = {
1720
* Defaults to the `TrophyIcon` prior to answering a question
1821
*/
1922
export const AnswerIcon = ({ answerStatus }: AnswerIconProps) => {
20-
const commonProps = {
21-
className: "text-body-inverse",
22-
}
23+
const commonIconClasses = "fill-background"
2324

2425
const IconWrapper = (props: ChildOnlyProp) => {
25-
const getWrapperBg = (): SquareProps["bg"] => {
26+
const getWrapperBg = () => {
2627
if (!answerStatus) {
27-
return "primary.base"
28+
return "bg-primary"
2829
}
2930
if (answerStatus === "correct") {
30-
return "success.base"
31+
return "bg-success"
3132
}
32-
return "error.base"
33+
return "bg-error"
3334
}
3435

35-
return <Circle size="50px" bg={getWrapperBg()} {...props} />
36+
return (
37+
<Center
38+
className={cn("size-[50px] rounded-full", getWrapperBg())}
39+
{...props}
40+
/>
41+
)
3642
}
3743

3844
if (!answerStatus) {
3945
return (
4046
<IconWrapper>
41-
<TrophyIcon {...commonProps} />
47+
<TrophyIcon className={commonIconClasses} />
4248
</IconWrapper>
4349
)
4450
}
4551

4652
if (answerStatus === "correct") {
4753
return (
4854
<IconWrapper>
49-
<CorrectIcon {...commonProps} />
55+
<CorrectIcon className={commonIconClasses} />
5056
</IconWrapper>
5157
)
5258
}
5359

5460
return (
5561
<IconWrapper>
56-
<IncorrectIcon {...commonProps} />
62+
<IncorrectIcon className={commonIconClasses} />
5763
</IconWrapper>
5864
)
5965
}

src/components/Quiz/QuizWidget/QuizButtonGroup.tsx

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { type Dispatch, type SetStateAction, useMemo } from "react"
22
import { FaXTwitter } from "react-icons/fa6"
3-
import { Center, Icon } from "@chakra-ui/react"
43

54
import type { AnswerChoice, Question, QuizKey, QuizStatus } from "@/lib/types"
65

7-
import { Button } from "@/components/Buttons"
86
import Translation from "@/components/Translation"
7+
import { Button } from "@/components/ui/buttons/Button"
8+
import { Center } from "@/components/ui/flex"
99

1010
import { trackCustomEvent } from "@/lib/utils/matomo"
1111

@@ -141,17 +141,12 @@ export const QuizButtonGroup = ({
141141

142142
return (
143143
<>
144-
<Center
145-
flexDirection={{ base: "column", sm: "row" }}
146-
gap={{ base: 4, md: 2 }}
147-
w="100%"
148-
>
149-
<Button
150-
variant="outline-color"
151-
leftIcon={<Icon as={FaXTwitter} />}
152-
onClick={handleShare}
153-
>
154-
<Translation id="learn-quizzes:share-results" />
144+
<Center className="w-full gap-4 max-md:flex-col md:gap-2">
145+
<Button variant="outline" onClick={handleShare}>
146+
<>
147+
<FaXTwitter />
148+
<Translation id="learn-quizzes:share-results" />
149+
</>
155150
</Button>
156151

157152
{/* Show `Next Quiz` button if quiz is opened from hub page */}
@@ -170,8 +165,7 @@ export const QuizButtonGroup = ({
170165
<Button
171166
onClick={handleReset}
172167
variant="ghost"
173-
fontWeight="bold"
174-
textDecoration="underline"
168+
className="font-bold underline"
175169
>
176170
<Translation id="learn-quizzes:try-again" />
177171
</Button>
@@ -209,7 +203,7 @@ export const QuizButtonGroup = ({
209203
return (
210204
<Button
211205
onClick={handleSubmitAnswer}
212-
isDisabled={!currentQuestionAnswerChoice}
206+
disabled={!currentQuestionAnswerChoice}
213207
data-testid="check-answer-button"
214208
>
215209
<Translation id="learn-quizzes:submit-answer" />
@@ -218,15 +212,7 @@ export const QuizButtonGroup = ({
218212
}
219213

220214
return (
221-
<Center
222-
gap={{ base: 4, md: 6 }}
223-
w={{ base: "full", sm: "auto" }}
224-
flexDirection={{ base: "column", sm: "row" }}
225-
flexWrap="wrap"
226-
sx={{
227-
button: { width: { base: "100%", sm: "fit-content" } },
228-
}}
229-
>
215+
<Center className="flex-wrap gap-4 max-sm:w-full max-sm:flex-col md:gap-6 max-sm:[&>button]:w-full">
230216
<MainButtons />
231217
</Center>
232218
)
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import { StarConfettiIcon } from "@/components/icons/quiz/StarConfettiIcon"
1+
import * as React from "react"
22

33
import { cn } from "@/lib/utils/cn"
44

5+
import { StarConfettiIcon } from "../../icons/quiz"
6+
57
export const QuizConfetti = () => {
6-
const commonClasses = "h-[119px] absolute top-0"
8+
const commonClasses = "absolute"
79
return (
8-
<>
9-
<StarConfettiIcon className={cn(commonClasses, "left-0")} />
10+
<div className="relative">
11+
<StarConfettiIcon className={cn(commonClasses, "start-0")} />
1012

11-
<StarConfettiIcon className={cn(commonClasses, "right-0 -scale-x-100")} />
12-
</>
13+
<StarConfettiIcon className={cn(commonClasses, "end-0 -scale-x-100")} />
14+
</div>
1315
)
1416
}

src/components/Quiz/QuizWidget/QuizContent.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { type ReactNode, useCallback } from "react"
2-
import { Text, type TextProps, VStack } from "@chakra-ui/react"
2+
3+
import { VStack } from "@/components/ui/flex"
4+
5+
import { cn } from "@/lib/utils/cn"
36

47
import type { AnswerStatus } from "./useQuizWidget"
58

@@ -20,21 +23,19 @@ export const QuizContent = ({
2023
return answerStatus === "correct" ? "Correct!" : "Incorrect"
2124
}, [answerStatus, title])
2225

23-
const getTitleTextColor = (): TextProps["color"] => {
24-
if (!answerStatus) return "primary.hover"
25-
return answerStatus === "correct" ? "success.base" : "fail.base"
26+
const getTitleTextColor = () => {
27+
if (!answerStatus) return "text-primary-hover"
28+
return answerStatus === "correct" ? "text-success" : "text-error"
2629
}
2730

2831
return (
29-
<VStack spacing="4">
30-
<Text
31-
fontWeight="bold"
32-
textAlign="center"
32+
<VStack className="gap-4">
33+
<span
34+
className={cn(getTitleTextColor(), "text-center font-bold")}
3335
data-testid={`answer-status-${answerStatus}`}
34-
color={getTitleTextColor()}
3536
>
3637
{getTitleContent()}
37-
</Text>
38+
</span>
3839
{children}
3940
</VStack>
4041
)

src/components/Quiz/QuizWidget/QuizProgressBar.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { useCallback } from "react"
2-
import { Center, ChakraProps, Container } from "@chakra-ui/react"
32

43
import type { AnswerChoice, Question } from "@/lib/types"
54

5+
import { Center } from "@/components/ui/flex"
6+
7+
import { cn } from "@/lib/utils/cn"
8+
69
import { PROGRESS_BAR_GAP } from "@/lib/constants"
710

811
import type { AnswerStatus } from "./useQuizWidget"
@@ -21,48 +24,44 @@ export const QuizProgressBar = ({
2124
userQuizProgress,
2225
}: QuizProgressBarProps) => {
2326
const progressBarBackground = useCallback(
24-
(index: number): ChakraProps["bg"] => {
27+
(index: number) => {
2528
if (
2629
(answerStatus &&
2730
index === currentQuestionIndex &&
2831
answerStatus === "correct") ||
2932
userQuizProgress[index]?.isCorrect
3033
) {
31-
return "success.base"
34+
return "bg-success"
3235
}
3336

3437
if (
3538
(answerStatus === "incorrect" && index === currentQuestionIndex) ||
3639
(userQuizProgress[index] && !userQuizProgress[index].isCorrect)
3740
) {
38-
return "error.base"
41+
return "bg-error"
3942
}
4043

4144
if (index === currentQuestionIndex) {
42-
return "gray.400"
45+
return "bg-body-medium"
4346
}
4447

45-
return "gray.500"
48+
return "bg-disabled"
4649
},
4750
[answerStatus, currentQuestionIndex, userQuizProgress]
4851
)
4952
return (
50-
<Center gap={PROGRESS_BAR_GAP} w="full">
53+
<Center className="w-full" style={{ gap: PROGRESS_BAR_GAP }}>
5154
{questions.map(({ id }, idx, arr) => {
5255
/* Calculate width percent based on number of questions */
5356
const width = `calc(${Math.floor(
5457
100 / arr.length
5558
)}% - ${PROGRESS_BAR_GAP})`
5659

5760
return (
58-
<Container
61+
<div
5962
key={id}
60-
bg={progressBarBackground(idx)}
61-
h="4px"
62-
w={width}
63-
maxW={`min(${width}, 2rem)`}
64-
marginInline={0}
65-
p={0}
63+
className={cn(progressBarBackground(idx), "mx-0 h-[4px] p-0")}
64+
style={{ width, maxWidth: `min(${width}, 2rem)` }}
6665
/>
6766
)
6867
})}

0 commit comments

Comments
 (0)