Skip to content

Commit

Permalink
Merge pull request #13456 from ethereum/shad-feedback-widget
Browse files Browse the repository at this point in the history
Migrate FeedbackWidget to shadcn
  • Loading branch information
pettinarip authored Jul 26, 2024
2 parents af516e8 + ca5da4c commit 8feec78
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 209 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@hookform/resolvers": "^3.8.0",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-navigation-menu": "^1.2.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@socialgouv/matomo-next": "^1.8.0",
"chart.js": "^4.4.2",
Expand Down Expand Up @@ -120,7 +121,8 @@
"tsconfig-paths-webpack-plugin": "4.1.0",
"typescript": "^5.5.2",
"unified": "^10.0.0",
"unist-util-visit": "^5.0.0"
"unist-util-visit": "^5.0.0",
"usehooks-ts": "^3.1.0"
},
"resolutions": {
"jackspeak": "2.1.1",
Expand Down
103 changes: 51 additions & 52 deletions src/components/FeedbackWidget/FixedDot.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,60 @@
import { forwardRef } from "react"
import { useTranslation } from "next-i18next"
import { Button, ButtonProps, ScaleFade, Text } from "@chakra-ui/react"
import type { ButtonHTMLAttributes } from "react"

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

import { FeedbackGlyphIcon } from "../icons"

type FixedDotProps = ButtonProps & {
bottomOffset: number
import { Button } from "@/../tailwind/ui/buttons/Button"

type FixedDotProps = ButtonHTMLAttributes<HTMLButtonElement> & {
isExpanded: boolean
offsetBottom?: boolean
suppressScale?: boolean
}
const FixedDot = ({ bottomOffset, isExpanded, ...props }: FixedDotProps) => {
const { t } = useTranslation("common")
const size = "12"
return (
<Button
data-testid="feedback-widget-button"
h={size}
w={{ base: size, lg: isExpanded ? "15rem" : size }}
borderRadius="full"
boxShadow="tableItemBox"
position="sticky"
bottom={{ base: `${bottomOffset + 1}rem`, lg: 4 }}
color="white"
ms="auto"
me="4"
mt={{ lg: "inherit" }}
zIndex={98} /* Below the mobile menu */
display="flex"
alignItems="center"
_hover={{
transform: "scale(1.1)",
transition: "transform 0.2s ease-in-out",
}}
transition="transform 0.2s ease-in-out, width 0.25s ease-in-out,
border-radius 0.25s linear"
aria-label={t("feedback-widget")}
leftIcon={<FeedbackGlyphIcon color="white" />}
iconSpacing={{ base: 0, lg: "3" }}
sx={{
".chakra-button__icon": {
me: !isExpanded ? 0 : undefined,
},
}}
{...props}
>
<ScaleFade in={isExpanded} delay={0.25}>
<Text
as="span"
fontWeight="bold"
noOfLines={2}
height="100%"
alignItems="center"
display={{ base: "none", lg: isExpanded ? "flex" : "none" }}

const FixedDot = forwardRef<HTMLButtonElement, FixedDotProps>(
({ offsetBottom, isExpanded, suppressScale, className, ...props }, ref) => {
const { t } = useTranslation("common")
return (
<Button
ref={ref}
data-testid="feedback-widget-button"
aria-label={t("feedback-widget")}
className={cn(
"lg:mt-inherit sticky bottom-4 z-20 me-4 ms-auto flex size-12 items-center gap-0 rounded-full text-white shadow-table-item-box",
"transition-all duration-200 hover:shadow-none hover:transition-transform hover:duration-200",
!suppressScale && "hover:scale-110",
offsetBottom && "bottom-31 lg:bottom-4",
isExpanded ? "lg:w-60 lg:gap-3" : "lg:w-12",
className
)}
{...props}
>
<FeedbackGlyphIcon
className={cn("text-white", !isExpanded && "-mx-1")}
/>
<div
className={cn(
"duration-250 transform transition-all",
isExpanded ? "scale-100 opacity-100" : "scale-95 opacity-0"
)}
>
{t("feedback-widget-prompt")}
</Text>
</ScaleFade>
</Button>
)
}
<span
className={cn(
"line-clamp-2 hidden h-full items-center font-bold text-white",
isExpanded && "lg:flex"
)}
>
{t("feedback-widget-prompt")}
</span>
</div>
</Button>
)
}
)

FixedDot.displayName = "FixedDot"

export default FixedDot
178 changes: 75 additions & 103 deletions src/components/FeedbackWidget/index.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
import { useTranslation } from "next-i18next"
import { MdClose } from "react-icons/md"

import {
AlertDialog,
AlertDialogBody,
AlertDialogCloseButton,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogOverlay,
Box,
Button,
HStack,
} from "@chakra-ui/react"
Popover,
PopoverClose,
PopoverContent,
PopoverTrigger,
} from "../ui/popover"

import FixedDot from "./FixedDot"
import { useFeedbackWidget } from "./useFeedbackWidget"

import { Button } from "@/../tailwind/ui/buttons/Button"

const FeedbackWidget = () => {
const { t } = useTranslation("common")
const {
bottomOffset,
offsetBottom,
cancelRef,
feedbackSubmitted,
getButtonProps,
handleClose,
handleOpen,
handleSubmit,
Expand All @@ -31,99 +28,74 @@ const FeedbackWidget = () => {
} = useFeedbackWidget()
return (
<>
<FixedDot
{...getButtonProps()}
onClick={handleOpen}
bottomOffset={bottomOffset}
isExpanded={isExpanded}
/>

<AlertDialog
isOpen={isOpen}
leastDestructiveRef={cancelRef}
onClose={handleClose}
isCentered
<Popover
onOpenChange={(open) => (open ? handleOpen : handleClose)()}
open={isOpen}
>
<AlertDialogOverlay>
<AlertDialogContent
position="fixed"
maxW={1504}
m="auto"
alignItems="flex-end"
backgroundColor="transparent"
me={24}
bottom={{ base: `${bottomOffset + 5}rem`, lg: 20 }}
data-testid="feedback-widget-modal"
padding={0}
>
<Box
w="min(320px, calc(100% - 1rem))"
mx="2"
bg="background.base"
borderRadius="base"
padding={{ base: "4", sm: "8" }}
>
<HStack>
<AlertDialogHeader fontSize="xl" fontWeight="bold" me="0">
{feedbackSubmitted
? t("feedback-widget-thank-you-title")
: t("feedback-widget-prompt")}
</AlertDialogHeader>
<AlertDialogCloseButton alignSelf="start" />
</HStack>
<PopoverTrigger asChild>
<FixedDot
offsetBottom={offsetBottom}
isExpanded={isExpanded}
suppressScale={isOpen}
/>
</PopoverTrigger>

<PopoverContent className="mx-2 w-80 max-w-[calc(100vw_-_1rem)] rounded bg-background p-4 sm:p-8">
<div className="flex items-start gap-2">
<header className="me-0 flex-1 p-0 text-xl font-bold">
{feedbackSubmitted
? t("feedback-widget-thank-you-title")
: t("feedback-widget-prompt")}
</header>
<PopoverClose asChild>
<Button
variant="ghost"
className="w-8 py-0 text-body"
size="sm"
ref={cancelRef}
>
<MdClose className="h-fit w-5" />
</Button>
</PopoverClose>
</div>

{/* Body: */}
{feedbackSubmitted && (
<>
<AlertDialogBody
fontWeight="normal"
fontSize="md"
lineHeight="5"
textAlign="center"
>
{t("feedback-widget-thank-you-subtitle")}
</AlertDialogBody>
<AlertDialogBody
fontWeight="bold"
fontSize="xs"
lineHeight="4"
letterSpacing="wide"
color="searchBorder"
textAlign="center"
>
{t("feedback-widget-thank-you-timing")}
</AlertDialogBody>
</>
)}
{feedbackSubmitted && (
<>
<div className="text-center text-md font-normal leading-5">
{t("feedback-widget-thank-you-subtitle")}
</div>
<div className="text-center text-xs font-bold leading-4 tracking-wide text-body-medium">
{t("feedback-widget-thank-you-timing")}
</div>
</>
)}

<AlertDialogFooter display="flex" gap="6">
{feedbackSubmitted ? (
<Button onClick={handleSurveyOpen} flex={1}>
{t("feedback-widget-thank-you-cta")}
</Button>
) : (
<>
<Button
variant="solid"
onClick={() => handleSubmit(true)}
flex={1}
>
{t("yes")}
</Button>
<Button
variant="solid"
onClick={() => handleSubmit(false)}
flex={1}
>
{t("no")}
</Button>
</>
)}
</AlertDialogFooter>
</Box>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
<footer className="mt-8 flex gap-6">
{feedbackSubmitted ? (
<Button onClick={handleSurveyOpen} className="flex-1">
{t("feedback-widget-thank-you-cta")}
</Button>
) : (
<>
<Button
variant="solid"
onClick={() => handleSubmit(true)}
className="flex-1"
>
{t("yes")}
</Button>
<Button
variant="solid"
onClick={() => handleSubmit(false)}
className="flex-1"
>
{t("no")}
</Button>
</>
)}
</footer>
</PopoverContent>
</Popover>
</>
)
}
Expand Down
16 changes: 7 additions & 9 deletions src/components/FeedbackWidget/useFeedbackWidget.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useEffect, useMemo, useRef, useState } from "react"
import { useRouter } from "next/router"
import { useDisclosure } from "@chakra-ui/react"

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

import useDisclosure from "@/hooks/useDisclosure"
import { useSurvey } from "@/hooks/useSurvey"

export const useFeedbackWidget = () => {
Expand All @@ -12,7 +12,7 @@ export const useFeedbackWidget = () => {
const [isExpanded, setIsExpanded] = useState(false)
const [feedbackSubmitted, setFeedbackSubmitted] = useState(false)

const { getButtonProps, isOpen, onClose, onOpen } = useDisclosure()
const { isOpen, onClose, onOpen } = useDisclosure()

const cancelRef = useRef<HTMLButtonElement>(null)

Expand All @@ -29,16 +29,15 @@ export const useFeedbackWidget = () => {

const surveyUrl = useSurvey(feedbackSubmitted)

const bottomOffset = useMemo(() => {
const offsetBottom = useMemo(() => {
const pathsWithBottomNav = ["/staking", "/dao", "/defi", "/nft"]
const CONDITIONAL_OFFSET = 6.75
let offset = 0
let shouldOffset = false
pathsWithBottomNav.forEach((path) => {
if (asPath.includes(path)) {
offset = CONDITIONAL_OFFSET
shouldOffset = true
}
})
return offset
return shouldOffset
}, [asPath])

const handleClose = (): void => {
Expand Down Expand Up @@ -80,10 +79,9 @@ export const useFeedbackWidget = () => {
}

return {
bottomOffset,
offsetBottom,
cancelRef,
feedbackSubmitted,
getButtonProps,
handleClose,
handleOpen,
handleSubmit,
Expand Down
Loading

0 comments on commit 8feec78

Please sign in to comment.