From e4d4b81a03f411f48385413df5870514ec896221 Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Mon, 4 Mar 2024 19:24:44 +0100 Subject: [PATCH 1/5] New Gov Actions --- .../frontend/public/icons/CopyBlueThin.svg | 11 + govtool/frontend/public/icons/Share.svg | 5 + .../src/components/atoms/CopyButton.tsx | 6 +- .../components/atoms/ExternalModalButton.tsx | 48 +++ .../src/components/atoms/IconLink.tsx | 45 +++ .../frontend/src/components/atoms/Radio.tsx | 44 ++- .../src/components/atoms/SliderArrow.tsx | 40 +++ .../frontend/src/components/atoms/Tooltip.tsx | 2 +- .../src/components/atoms/VotePill.tsx | 3 +- .../frontend/src/components/atoms/index.ts | 3 + .../components/molecules/DataActionsBar.tsx | 4 +- .../molecules/DataMissingInfoBox.tsx | 60 ++++ .../molecules/GovernanceActionCard.tsx | 235 ++++----------- .../molecules/GovernanceActionCardElement.tsx | 140 +++++++++ .../molecules/GovernanceActionCardHeader.tsx | 62 ++++ .../molecules/GovernanceActionCardMyVote.tsx | 56 ++++ .../GovernanceActionCardStatePill.tsx | 50 +++ .../GovernanceActionDetailsCardHeader.tsx | 56 ++++ .../GovernanceActionDetailsCardLinks.tsx | 47 +++ ...GovernanceActionDetailsCardOnChainData.tsx | 79 +++++ .../GovernanceActionDetailsCardVotes.tsx | 61 ++++ .../molecules/GovernanceActionsDatesBox.tsx | 124 ++++++++ .../molecules/GovernanceVotedOnCard.tsx | 285 +++++------------- .../src/components/molecules/Share.tsx | 99 ++++++ .../src/components/molecules/SliderArrows.tsx | 44 +++ .../components/molecules/VoteActionForm.tsx | 187 ++++++++---- .../components/molecules/VotesSubmitted.tsx | 57 ++-- .../src/components/molecules/index.ts | 12 + .../DashboardGovernanceActionDetails.tsx | 17 +- .../organisms/DashboardGovernanceActions.tsx | 40 +-- .../DashboardGovernanceActionsVotedOn.tsx | 25 +- .../organisms/GovernanceActionDetailsCard.tsx | 266 ++++------------ .../GovernanceActionDetailsCardData.tsx | 121 ++++++++ .../organisms/GovernanceActionsToVote.tsx | 70 ++--- .../src/components/organisms/Slider.tsx | 185 +++++------- .../src/components/organisms/index.ts | 1 + govtool/frontend/src/consts/icons.ts | 2 + govtool/frontend/src/hooks/useSlider.ts | 27 +- govtool/frontend/src/i18n/locales/en.ts | 20 +- .../DashboardGovernanceActionsCategory.tsx | 2 + .../src/pages/GovernanceActionDetails.tsx | 8 +- .../src/pages/GovernanceActionsCategory.tsx | 2 + govtool/frontend/src/theme.ts | 55 ++++ 43 files changed, 1796 insertions(+), 910 deletions(-) create mode 100644 govtool/frontend/public/icons/CopyBlueThin.svg create mode 100644 govtool/frontend/public/icons/Share.svg create mode 100644 govtool/frontend/src/components/atoms/ExternalModalButton.tsx create mode 100644 govtool/frontend/src/components/atoms/IconLink.tsx create mode 100644 govtool/frontend/src/components/atoms/SliderArrow.tsx create mode 100644 govtool/frontend/src/components/molecules/DataMissingInfoBox.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionCardStatePill.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionDetailsCardHeader.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx create mode 100644 govtool/frontend/src/components/molecules/GovernanceActionsDatesBox.tsx create mode 100644 govtool/frontend/src/components/molecules/Share.tsx create mode 100644 govtool/frontend/src/components/molecules/SliderArrows.tsx create mode 100644 govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx diff --git a/govtool/frontend/public/icons/CopyBlueThin.svg b/govtool/frontend/public/icons/CopyBlueThin.svg new file mode 100644 index 000000000..2050c9abe --- /dev/null +++ b/govtool/frontend/public/icons/CopyBlueThin.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/govtool/frontend/public/icons/Share.svg b/govtool/frontend/public/icons/Share.svg new file mode 100644 index 000000000..a8b9eac2b --- /dev/null +++ b/govtool/frontend/public/icons/Share.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/govtool/frontend/src/components/atoms/CopyButton.tsx b/govtool/frontend/src/components/atoms/CopyButton.tsx index 65601c718..2783d0fb7 100644 --- a/govtool/frontend/src/components/atoms/CopyButton.tsx +++ b/govtool/frontend/src/components/atoms/CopyButton.tsx @@ -7,7 +7,7 @@ import { useTranslation } from "@hooks"; interface Props { isChecked?: boolean; text: string; - variant?: string; + variant?: "blueThin" | "blue"; } export const CopyButton = ({ isChecked, text, variant }: Props) => { @@ -19,6 +19,10 @@ export const CopyButton = ({ isChecked, text, variant }: Props) => { return ICONS.copyBlueIcon; } + if (variant === "blueThin") { + return ICONS.copyBlueThinIcon; + } + if (isChecked) { return ICONS.copyWhiteIcon; } diff --git a/govtool/frontend/src/components/atoms/ExternalModalButton.tsx b/govtool/frontend/src/components/atoms/ExternalModalButton.tsx new file mode 100644 index 000000000..c4debddc5 --- /dev/null +++ b/govtool/frontend/src/components/atoms/ExternalModalButton.tsx @@ -0,0 +1,48 @@ +import { Typography } from "@mui/material"; + +import { Button } from "@atoms"; +import { ICONS } from "@consts"; +import { useModal } from "@context"; + +export const ExternalModalButton = ({ + label, + url, +}: { + label: string; + url: string; +}) => { + const { openModal } = useModal(); + + return ( + + ); +}; diff --git a/govtool/frontend/src/components/atoms/IconLink.tsx b/govtool/frontend/src/components/atoms/IconLink.tsx new file mode 100644 index 000000000..0c12fcd1e --- /dev/null +++ b/govtool/frontend/src/components/atoms/IconLink.tsx @@ -0,0 +1,45 @@ +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; +import { openInNewTab } from "@utils"; +import { ICONS } from "@consts"; + +type IconLinkProps = { + label: string; + navTo: string; + isSmall?: boolean; +}; + +export const IconLink = ({ label, navTo, isSmall }: IconLinkProps) => { + const openLink = () => openInNewTab(navTo); + + return ( + + link + + {label} + + + ); +}; diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index 538fa7deb..bf4184681 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Box, Typography } from "@mui/material"; +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; import { UseFormRegister, UseFormSetValue } from "react-hook-form"; type RadioProps = { @@ -10,11 +12,20 @@ type RadioProps = { setValue: UseFormSetValue; register: UseFormRegister; dataTestId?: string; + disabled?: boolean; }; export const Radio = ({ ...props }: RadioProps) => { - const { isChecked, name, setValue, title, value, dataTestId, register } = - props; + const { + isChecked, + name, + setValue, + title, + value, + dataTestId, + register, + disabled, + } = props; const handleClick = () => { setValue(name, value); @@ -23,13 +34,17 @@ export const Radio = ({ ...props }: RadioProps) => { return ( { + if (!disabled) handleClick(); + }} + borderRadius={isChecked ? "15px" : "12px"} + p={isChecked ? "2px" : 0} + border={isChecked ? 2 : 0} + borderColor={isChecked ? "specialCyanBorder" : undefined} sx={[{ "&:hover": { color: "blue", cursor: "pointer" } }]} - flex={1} + boxShadow={ + "0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)" + } > { checked={isChecked} /> {title} diff --git a/govtool/frontend/src/components/atoms/SliderArrow.tsx b/govtool/frontend/src/components/atoms/SliderArrow.tsx new file mode 100644 index 000000000..2ab59999a --- /dev/null +++ b/govtool/frontend/src/components/atoms/SliderArrow.tsx @@ -0,0 +1,40 @@ +import { Box } from "@mui/material"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; + +import { theme } from "@/theme"; + +interface Props { + disabled: boolean; + onClick: (e: any) => void; + left?: boolean; +} + +export const SliderArrow = ({ disabled, onClick, left }: Props) => { + const { + palette: { primaryBlue, arcticWhite, lightBlue }, + } = theme; + + return ( + + + + ); +}; diff --git a/govtool/frontend/src/components/atoms/Tooltip.tsx b/govtool/frontend/src/components/atoms/Tooltip.tsx index 4b8b7a90a..4e2ccdc51 100644 --- a/govtool/frontend/src/components/atoms/Tooltip.tsx +++ b/govtool/frontend/src/components/atoms/Tooltip.tsx @@ -2,7 +2,7 @@ import { styled } from "@mui/material"; import * as TooltipMUI from "@mui/material/Tooltip"; import Typography from "@mui/material/Typography"; -type TooltipProps = Omit & { +export type TooltipProps = Omit & { heading?: string; paragraphOne?: string; paragraphTwo?: string; diff --git a/govtool/frontend/src/components/atoms/VotePill.tsx b/govtool/frontend/src/components/atoms/VotePill.tsx index d28ed9da4..de29ac900 100644 --- a/govtool/frontend/src/components/atoms/VotePill.tsx +++ b/govtool/frontend/src/components/atoms/VotePill.tsx @@ -1,6 +1,7 @@ -import { Vote } from "@models"; import { Box, Typography } from "@mui/material"; +import { Vote } from "@models"; + export const VotePill = ({ vote, width, diff --git a/govtool/frontend/src/components/atoms/index.ts b/govtool/frontend/src/components/atoms/index.ts index 7072b7635..1939a0971 100644 --- a/govtool/frontend/src/components/atoms/index.ts +++ b/govtool/frontend/src/components/atoms/index.ts @@ -5,9 +5,11 @@ export * from "./Checkbox"; export * from "./ClickOutside"; export * from "./CopyButton"; export * from "./DrawerLink"; +export * from "./ExternalModalButton"; export * from "./FormErrorMessage"; export * from "./FormHelpfulText"; export * from "./HighlightedText"; +export * from "./IconLink"; export * from "./InfoText"; export * from "./Input"; export * from "./Link"; @@ -19,6 +21,7 @@ export * from "./modal/ModalWrapper"; export * from "./Radio"; export * from "./ScrollToManage"; export * from "./ScrollToTop"; +export * from "./SliderArrow"; export * from "./Spacer"; export * from "./StakeRadio"; export * from "./TextArea"; diff --git a/govtool/frontend/src/components/molecules/DataActionsBar.tsx b/govtool/frontend/src/components/molecules/DataActionsBar.tsx index 86613a8bc..c54010f5a 100644 --- a/govtool/frontend/src/components/molecules/DataActionsBar.tsx +++ b/govtool/frontend/src/components/molecules/DataActionsBar.tsx @@ -50,7 +50,7 @@ export const DataActionsBar: FC = ({ ...props }) => { return ( <> - + setSearchText(e.target.value)} @@ -76,7 +76,7 @@ export const DataActionsBar: FC = ({ ...props }) => { fontWeight: 500, height: 48, padding: "16px 24px", - width: 231, + width: 500, }} /> { + const { t } = useTranslation(); + + return ( + + + {/* TODO: Text to confirm/change */} + The data that was originally used when this Governance Action was + created has been formatted incorrectly. + + + {/* TODO: Text to confirm/change */} + GovTool uses external sources for Governance Action data, and these + sources are maintained by the proposers of the Actions. This error means + that GovTool cannot locate the data on the URL specified when the + Governance Action was originally posted. + + + // TODO: Add the correct link + openInNewTab( + "https://docs.sanchogov.tools/how-to-use-the-govtool/getting-started/get-a-compatible-wallet" + ) + } + sx={{ + fontFamily: "Poppins", + fontSize: "16px", + lineHeight: "24px", + cursor: "pointer", + }} + > + {t("learnMore")} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx index 567a3fb50..c9f6bbeb3 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx @@ -1,16 +1,22 @@ import { FC } from "react"; import { Box } from "@mui/material"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { Button, Typography, Tooltip } from "@atoms"; +import { Button } from "@atoms"; +import { + GovernanceActionCardElement, + GovernanceActionCardHeader, + GovernanceActionsDatesBox, +} from "@molecules"; + import { useScreenDimension, useTranslation } from "@hooks"; import { formatDisplayDate, getFullGovActionId, getProposalTypeLabel, - getShortenedGovActionId, } from "@utils"; -import { theme } from "@/theme"; + +const mockedLongText = + "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit, distinctio culpa minus eaque illo quidem voluptates quisquam mollitia consequuntur ex, sequi saepe? Ad ex adipisci molestiae sed."; interface ActionTypeProps extends Omit< @@ -29,6 +35,7 @@ interface ActionTypeProps inProgress?: boolean; txHash: string; index: number; + isDataMissing: boolean; } export const GovernanceActionCard: FC = ({ ...props }) => { @@ -40,14 +47,11 @@ export const GovernanceActionCard: FC = ({ ...props }) => { createdDate, txHash, index, + isDataMissing, } = props; const { isMobile, screenWidth } = useScreenDimension(); const { t } = useTranslation(); - const { - palette: { lightBlue }, - } = theme; - const govActionId = getFullGovActionId(txHash, index); const proposalTypeNoEmptySpaces = getProposalTypeLabel(type).replace( / /g, @@ -56,188 +60,79 @@ export const GovernanceActionCard: FC = ({ ...props }) => { return ( - {inProgress && ( - - - {t("inProgress")} - - - )} - - - {t("govActions.governanceActionType")} - - - - - {getProposalTypeLabel(type)} - - - - - - - {t("govActions.governanceActionId")} - - - - - {getShortenedGovActionId(txHash, index)} - - - - + + + + + - {createdDate ? ( - - - {t("govActions.submissionDate")} - - - {formatDisplayDate(createdDate)} - - - - - - ) : null} - {expiryDate ? ( - - - {t("govActions.expiryDate")} - - - {formatDisplayDate(expiryDate)} - - - - - - ) : null} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx new file mode 100644 index 000000000..fe6baf8c2 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx @@ -0,0 +1,140 @@ +import { Box } from "@mui/material"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; + +import { Typography, Tooltip, CopyButton, TooltipProps } from "@atoms"; + +type BaseProps = { + label: string; + text: string; + dataTestId?: string; + isSliderCard?: boolean; + tooltipProps?: Omit; + marginBottom?: number; +}; + +type PillVariantProps = BaseProps & { + textVariant: "pill"; + isCopyButton?: false; +}; + +type OtherVariantsProps = BaseProps & { + textVariant?: "oneLine" | "twoLines" | "longText"; + isCopyButton?: boolean; +}; + +type GovernanceActionCardElementProps = PillVariantProps | OtherVariantsProps; + +export const GovernanceActionCardElement = ({ + label, + text, + dataTestId, + isSliderCard, + textVariant = "oneLine", + isCopyButton, + tooltipProps, + marginBottom, +}: GovernanceActionCardElementProps) => { + return ( + + + + {label} + + {tooltipProps && ( + + + + )} + + + {textVariant === "pill" ? ( + + + {text} + + + ) : ( + + + {text} + + {isCopyButton && ( + + + + )} + + )} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx new file mode 100644 index 000000000..148377b7e --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx @@ -0,0 +1,62 @@ +import { Box } from "@mui/material"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; + +import { Tooltip, Typography } from "@atoms"; +import { useTranslation } from "@hooks"; + +const mockedLongText = + "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit, distinctio culpa minus eaque illo quidem voluptates quisquam mollitia consequuntur ex, sequi saepe? Ad ex adipisci molestiae sed."; + +type GovernanceActionCardHeaderProps = { + title: string; + isDataMissing: boolean; +}; + +export const GovernanceActionCardHeader = ({ + title, + isDataMissing, +}: GovernanceActionCardHeaderProps) => { + const { t } = useTranslation(); + + return ( + + + {isDataMissing ? t("govActions.dataMissing") : title} + + {isDataMissing && ( + + + + )} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx new file mode 100644 index 000000000..2beba72c8 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx @@ -0,0 +1,56 @@ +import { Box } from "@mui/material"; + +import { Button, Typography, VotePill } from "@atoms"; +import { openInNewTab } from "@utils"; +import { useTranslation } from "@hooks"; +import { Vote } from "@models"; + +export const GovernanceActionCardMyVote = ({ vote }: { vote: Vote }) => { + const { t } = useTranslation(); + + return ( + + + {t("govActions.myVote")} + + + + + + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardStatePill.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardStatePill.tsx new file mode 100644 index 000000000..d2144ea85 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardStatePill.tsx @@ -0,0 +1,50 @@ +import { Box } from "@mui/material"; +import CheckIcon from "@mui/icons-material/Check"; + +import { Typography } from "@atoms"; +import { useTranslation } from "@hooks"; + +export const GovernanceActionCardStatePill = ({ + variant = "voteSubmitted", +}: { + variant?: "inProgress" | "voteSubmitted"; +}) => { + const { t } = useTranslation(); + + return ( + + + {variant === "voteSubmitted" && ( + + )} + {variant === "inProgress" + ? t("inProgress") + : t("govActions.voteSubmitted")} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardHeader.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardHeader.tsx new file mode 100644 index 000000000..a54b65431 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardHeader.tsx @@ -0,0 +1,56 @@ +import { useLocation } from "react-router-dom"; +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; +import { Share } from "@molecules"; +import { useTranslation } from "@hooks"; + +type GovernanceActionDetailsCardHeaderProps = { + title: string; + isDataMissing: boolean; +}; + +export const GovernanceActionDetailsCardHeader = ({ + title, + isDataMissing, +}: GovernanceActionDetailsCardHeaderProps) => { + const { t } = useTranslation(); + const { pathname, hash } = useLocation(); + + const govActionLinkToShare = `${window.location.protocol}//${ + window.location.hostname + }${window.location.port ? `:${window.location.port}` : ""}${pathname}${hash}`; + + return ( + + + + {isDataMissing ? t("govActions.dataMissing") : title} + + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx new file mode 100644 index 000000000..795205e36 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx @@ -0,0 +1,47 @@ +import { Box } from "@mui/material"; + +import { IconLink, Typography } from "@atoms"; +import { useScreenDimension, useTranslation } from "@hooks"; + +const LINKS = [ + "https://www.google.com", + "https://www.google.com", + "https://www.google.com", + "https://www.google.com", +]; + +export const GovernanceActionDetailsCardLinks = () => { + const { isMobile } = useScreenDimension(); + const { t } = useTranslation(); + + return ( + <> + + {t("govActions.supportingLinks")} + + + {LINKS.map((link, index) => ( + + ))} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx new file mode 100644 index 000000000..ae8872ad4 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx @@ -0,0 +1,79 @@ +import { Box } from "@mui/material"; + +import { Typography } from "@atoms"; +import { useTranslation } from "@hooks"; + +type GovernanceActionDetailsCardOnChainDataProps = { + data: { + label: string; + content: string; + }[]; +}; + +export const GovernanceActionDetailsCardOnChainData = ({ + data, +}: GovernanceActionDetailsCardOnChainDataProps) => { + const { t } = useTranslation(); + + return ( + + + + {t("govActions.onChainTransactionDetails")} + + + {data.map(({ label, content }) => ( + + + {label} + {":"} + + + {content} + + + ))} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx new file mode 100644 index 000000000..2abf51ca9 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx @@ -0,0 +1,61 @@ +import { Dispatch, SetStateAction } from "react"; +import { Box } from "@mui/material"; + +import { useScreenDimension } from "@hooks"; +import { VoteActionForm, VotesSubmitted } from "@molecules"; + +type GovernanceActionCardVotesProps = { + setIsVoteSubmitted: Dispatch>; + abstainVotes: number; + noVotes: number; + yesVotes: number; + isOneColumn: boolean; + isVoter?: boolean; + voteFromEP?: string; + isDashboard?: boolean; + isInProgress?: boolean; +}; + +export const GovernanceActionDetailsCardVotes = ({ + setIsVoteSubmitted, + abstainVotes, + noVotes, + yesVotes, + isOneColumn, + isVoter, + voteFromEP, + isDashboard, + isInProgress, +}: GovernanceActionCardVotesProps) => { + const { screenWidth } = useScreenDimension(); + + const isModifiedPadding = + (isDashboard && screenWidth < 1368) ?? screenWidth < 1100; + + return ( + + {isVoter ? ( + + ) : ( + + )} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsDatesBox.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsDatesBox.tsx new file mode 100644 index 000000000..8db7ef999 --- /dev/null +++ b/govtool/frontend/src/components/molecules/GovernanceActionsDatesBox.tsx @@ -0,0 +1,124 @@ +import { Box } from "@mui/material"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; +import { Trans } from "react-i18next"; + +import { Tooltip, Typography } from "@atoms"; +import { useScreenDimension, useTranslation } from "@hooks"; + +type GovernanceActionsDatesBoxProps = { + createdDate: string; + expiryDate: string; + isSliderCard?: boolean; +}; + +export const GovernanceActionsDatesBox = ({ + createdDate, + expiryDate, + isSliderCard, +}: GovernanceActionsDatesBoxProps) => { + const { t } = useTranslation(); + const { screenWidth } = useScreenDimension(); + + const isFontSizeSmaller = screenWidth < 420; + + return ( + + + + , + , + ]} + /> + + + + + + + + , + , + ]} + /> + + + + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx index 817c8495d..a6e56f200 100644 --- a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx @@ -1,9 +1,7 @@ import { useNavigate } from "react-router-dom"; import { Box } from "@mui/material"; -import CheckIcon from "@mui/icons-material/Check"; -import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; -import { Button, VotePill, Typography, Tooltip } from "@atoms"; +import { Button } from "@atoms"; import { PATHS } from "@consts"; import { useScreenDimension, useTranslation } from "@hooks"; import { VotedProposal } from "@models"; @@ -11,23 +9,34 @@ import { formatDisplayDate, getFullGovActionId, getProposalTypeLabel, - getShortenedGovActionId, - openInNewTab, } from "@utils"; -import { theme } from "@/theme"; +import { + GovernanceActionCardElement, + GovernanceActionCardHeader, + GovernanceActionCardMyVote, + GovernanceActionCardStatePill, + GovernanceActionsDatesBox, +} from "@molecules"; + +const mockedLongText = + "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit, distinctio culpa minus eaque illo quidem voluptates quisquam mollitia consequuntur ex, sequi saepe? Ad ex adipisci molestiae sed."; interface Props { votedProposal: VotedProposal; + isDataMissing: boolean; + searchPhrase?: string; inProgress?: boolean; } -export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { +export const GovernanceVotedOnCard = ({ + votedProposal, + isDataMissing, + inProgress, +}: Props) => { const navigate = useNavigate(); const { proposal, vote } = votedProposal; - const { - palette: { lightBlue }, - } = theme; - const { isMobile } = useScreenDimension(); + + const { isMobile, screenWidth } = useScreenDimension(); const { t } = useTranslation(); const proposalTypeNoEmptySpaces = getProposalTypeLabel(proposal.type).replace( @@ -37,213 +46,72 @@ export const GovernanceVotedOnCard = ({ votedProposal, inProgress }: Props) => { return ( + - - {inProgress ? ( - t("inProgress") - ) : ( - <> - - {t("govActions.voteSubmitted")} - - )} - - - - - - {t("govActions.governanceActionType")} - - - - - {getProposalTypeLabel(proposal.type)} - - - - - - - {t("govActions.governanceActionId")} - - - - - {getShortenedGovActionId(proposal.txHash, proposal.index)} - - - - - - - {t("govActions.myVote")} - - - - - - - - + + + + + + - {proposal.createdDate ? ( - - - {t("govActions.submissionDate")} - - - {formatDisplayDate(proposal.createdDate)} - - - - - - ) : null} - {proposal.expiryDate ? ( - - - {t("govActions.expiryDate")} - - - {formatDisplayDate(proposal.expiryDate)} - - - - - - ) : null} diff --git a/govtool/frontend/src/components/molecules/Share.tsx b/govtool/frontend/src/components/molecules/Share.tsx new file mode 100644 index 000000000..1165c9c24 --- /dev/null +++ b/govtool/frontend/src/components/molecules/Share.tsx @@ -0,0 +1,99 @@ +import { useState } from "react"; +import { Box, Popover } from "@mui/material"; + +import { Typography } from "@atoms"; +import { ICONS } from "@consts"; +import { useSnackbar } from "@context"; +import { useTranslation } from "@hooks"; + +export const Share = ({ link }: { link: string }) => { + const { addSuccessAlert } = useSnackbar(); + const { t } = useTranslation(); + const [anchorEl, setAnchorEl] = useState(null); + const [isActive, setIsActive] = useState(true); + + const handleClick = (event: any) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const onCopy = (e: any) => { + navigator.clipboard.writeText(link); + addSuccessAlert(t("alerts.copiedToClipboard")); + setIsActive(false); + e.stopPropagation(); + }; + + const open = Boolean(anchorEl); + const id = open ? "simple-popover" : undefined; + + return ( + <> + + + + + + Share + + link + + Click to copy + + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/SliderArrows.tsx b/govtool/frontend/src/components/molecules/SliderArrows.tsx new file mode 100644 index 000000000..f38a45b0e --- /dev/null +++ b/govtool/frontend/src/components/molecules/SliderArrows.tsx @@ -0,0 +1,44 @@ +import { KeenSliderHooks, KeenSliderInstance } from "keen-slider/react"; +import { SliderArrow } from "@atoms"; +import { Box } from "@mui/material"; + +interface ArrowsProps { + currentSlide: number; + instanceRef: React.MutableRefObject | null>; +} + +export const SliderArrows = ({ currentSlide, instanceRef }: ArrowsProps) => { + return ( + <> + {instanceRef.current && ( + + + e.stopPropagation() || instanceRef.current?.prev() + } + disabled={currentSlide === 0} + /> + + e.stopPropagation() || instanceRef.current?.next() + } + disabled={ + currentSlide === + instanceRef.current.track.details.slides.length - 1 + } + /> + + )} + + ); +}; diff --git a/govtool/frontend/src/components/molecules/VoteActionForm.tsx b/govtool/frontend/src/components/molecules/VoteActionForm.tsx index 4c7413400..2cecfbc2a 100644 --- a/govtool/frontend/src/components/molecules/VoteActionForm.tsx +++ b/govtool/frontend/src/components/molecules/VoteActionForm.tsx @@ -1,4 +1,11 @@ -import { useState, useEffect, useMemo, useCallback } from "react"; +import { + useState, + useEffect, + useMemo, + useCallback, + Dispatch, + SetStateAction, +} from "react"; import { useLocation } from "react-router-dom"; import { Box, Link } from "@mui/material"; @@ -14,21 +21,32 @@ import { import { openInNewTab } from "@utils"; import { ControlledField } from "../organisms"; +import { Trans } from "react-i18next"; + +// TODO: Change into props when BE is ready +const castVoteDate = undefined; +const castVoteChangeDeadline = "20.06.2024 (Epoch 445)"; + +type VoteActionFormProps = { + setIsVoteSubmitted: Dispatch>; + voteFromEP?: string; + yesVotes: number; + noVotes: number; + abstainVotes: number; + isInProgress?: boolean; +}; export const VoteActionForm = ({ + setIsVoteSubmitted, voteFromEP, yesVotes, noVotes, abstainVotes, -}: { - voteFromEP?: string; - yesVotes: number; - noVotes: number; - abstainVotes: number; -}) => { - const { state } = useLocation(); + isInProgress, +}: VoteActionFormProps) => { const [isContext, setIsContext] = useState(false); - const { isMobile, screenWidth } = useScreenDimension(); + const { state } = useLocation(); + const { isMobile } = useScreenDimension(); const { openModal } = useModal(); const { t } = useTranslation(); const { voter } = useGetVoterInfo(); @@ -49,8 +67,10 @@ export const VoteActionForm = ({ useEffect(() => { if (state && state.vote) { setValue("vote", state.vote); + setIsVoteSubmitted(true); } else if (voteFromEP) { setValue("vote", voteFromEP); + setIsVoteSubmitted(true); } }, [state, voteFromEP, setValue]); @@ -104,56 +124,97 @@ export const VoteActionForm = ({ ); return ( - - - - {t("govActions.chooseHowToVote")} - - - - - - - - - - + + + {castVoteDate ? ( + <> + + ]} + /> + + + {t("govActions.castVoteDeadline", { + date: castVoteChangeDeadline, + })} + + + ) : ( + + {t("govActions.chooseHowToVote")} + + )} + + + + {(voter?.isRegisteredAsDRep || voter?.isRegisteredAsSoleVoter) && ( - - - {isDRep ? ( - - ) : ( - - )} - + {(isVoteSubmitted || isInProgress) && ( + + )} + + ); }; diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx new file mode 100644 index 000000000..7ac215c13 --- /dev/null +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx @@ -0,0 +1,121 @@ +import { Box } from "@mui/material"; + +import { ExternalModalButton } from "@atoms"; +import { + GovernanceActionCardElement, + GovernanceActionDetailsCardLinks, + DataMissingInfoBox, + GovernanceActionDetailsCardHeader, + GovernanceActionsDatesBox, + GovernanceActionDetailsCardOnChainData, +} from "@molecules"; +import { useScreenDimension, useTranslation } from "@hooks"; + +const mockedLongDescription = + "I am the Cardano crusader carving his path in the blockchain battleground. With a mind sharper than a Ledger Nano X, this fearless crypto connoisseur fearlessly navigates the volatile seas of Cardano, turning code into currency. Armed with a keyboard and a heart pumping with blockchain beats, Mister Big Bad fearlessly champions decentralization, smart contracts, and the Cardano community. His Twitter feed is a mix of market analysis that rivals CNBC and memes that could break the internet."; + +const mockedOnChainData = [ + { + label: "Reward Address", + content: "Lorem ipsum dolor sit amet consectetur.", + }, + { label: "Amount", content: "₳ 12,350" }, +]; + +type GovernanceActionDetailsCardDataProps = { + type: string; + govActionId: string; + createdDate: string; + expiryDate: string; + isDataMissing: boolean; + isOneColumn: boolean; + isDashboard?: boolean; +}; + +export const GovernanceActionDetailsCardData = ({ + type, + govActionId, + createdDate, + expiryDate, + isDataMissing, + isOneColumn, + isDashboard, +}: GovernanceActionDetailsCardDataProps) => { + const { t } = useTranslation(); + const { screenWidth } = useScreenDimension(); + + const isModifiedPadding = + (isDashboard && screenWidth < 1168) ?? screenWidth < 900; + + return ( + + + {isDataMissing && } + + + {isDataMissing && ( + + )} + + + + + + + + + + ); +}; diff --git a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx index 41eaa2bb2..794a33dc3 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx @@ -2,13 +2,13 @@ import { useNavigate, generatePath } from "react-router-dom"; import { Box } from "@mui/material"; -import { Typography } from '@atoms'; -import { PATHS } from '@consts'; -import { useCardano } from '@context'; -import { useScreenDimension, useTranslation } from '@hooks'; -import { GovernanceActionCard } from '@molecules'; -import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from '@utils'; -import { Slider } from './Slider'; +import { Typography } from "@atoms"; +import { PATHS } from "@consts"; +import { useCardano } from "@context"; +import { useScreenDimension, useTranslation } from "@hooks"; +import { GovernanceActionCard } from "@molecules"; +import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from "@utils"; +import { Slider } from "@organisms"; type GovernanceActionsToVoteProps = { filters: string[]; @@ -34,7 +34,7 @@ export const GovernanceActionsToVote = ({ <> {!proposals.length ? ( - {t('govActions.noResultsForTheSearch')} + {t("govActions.noResultsForTheSearch")} ) : ( <> @@ -46,49 +46,51 @@ export const GovernanceActionsToVote = ({ className="keen-slider__slide" key={action.id} style={{ - overflow: 'visible', - width: 'auto', + overflow: "visible", + width: "auto", }} > onDashboard && - pendingTransaction.vote?.resourceId === + pendingTransaction.vote?.resourceId === action?.txHash + action?.index ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) + "https://adanordic.com/latest_transactions", + ) : navigate( - onDashboard - ? generatePath( - PATHS.dashboardGovernanceActionsAction, - { - proposalId: getFullGovActionId( - action.txHash, - action.index, + onDashboard + ? generatePath( + PATHS.dashboardGovernanceActionsAction, + { + proposalId: getFullGovActionId( + action.txHash, + action.index, + ), + }, + ) + : PATHS.governanceActionsAction.replace( + ":proposalId", + getFullGovActionId( + action.txHash, + action.index, + ), ), - }, - ) - : PATHS.governanceActionsAction.replace( - ":proposalId", - getFullGovActionId( - action.txHash, - action.index, - ), - ), - { - state: { ...action }, - }, - ) + { + state: { ...action }, + }, + ) } /> diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index 577ca56a6..f4f0499c5 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -1,13 +1,15 @@ -import { useCallback, useEffect, useMemo } from "react"; -import { Box, Link, Typography } from "@mui/material"; +import { useEffect } from "react"; +import { generatePath, useNavigate } from "react-router-dom"; +import { Box } from "@mui/material"; import { KeenSliderOptions } from "keen-slider"; import "keen-slider/keen-slider.min.css"; -import { ICONS, PATHS } from "@consts"; import { useCardano } from "@context"; -import { useScreenDimension, useSlider, useTranslation } from "@hooks"; -import { generatePath, useNavigate } from "react-router-dom"; -import styles from "./slider.module.css"; +import { useScreenDimension, useTranslation, useSlider } from "@hooks"; +import { Button, Typography } from "@atoms"; +import { SliderArrows } from "@molecules"; +import { theme } from "@/theme"; +import { PATHS } from "@consts"; const SLIDER_MAX_LENGTH = 1000; @@ -36,39 +38,34 @@ export const Slider = ({ searchPhrase, sorting, }: SliderProps) => { - const { isMobile, screenWidth, pagePadding } = useScreenDimension(); + const { isMobile, screenWidth } = useScreenDimension(); const navigate = useNavigate(); const { pendingTransaction } = useCardano(); const { t } = useTranslation(); + const { + palette: { primaryBlue, arcticWhite, lightBlue }, + } = theme; + const DEFAULT_SLIDER_CONFIG = { mode: "free", initial: 0, slides: { perView: "auto", - spacing: 24, + spacing: 20, }, } as KeenSliderOptions; - const { - currentRange, - sliderRef, - setPercentageValue, - instanceRef, - setCurrentRange, - } = useSlider({ + const isShowArrows = + screenWidth < 268 + 28 + dataLength * 350 + 20 * dataLength - 5; + + const { sliderRef, instanceRef, currentSlide } = useSlider({ config: DEFAULT_SLIDER_CONFIG, sliderMaxLength: SLIDER_MAX_LENGTH, }); - const paddingOffset = useMemo(() => { - const padding = onDashboard ? (isMobile ? 2 : 3.5) : pagePadding; - return padding * 8 * 2; - }, [isMobile, pagePadding]); - const refresh = () => { instanceRef.current?.update(instanceRef.current?.options); - setCurrentRange(0); instanceRef.current?.track.to(0); instanceRef.current?.moveToIdx(0); }; @@ -77,58 +74,66 @@ export const Slider = ({ refresh(); }, [filters, sorting, searchPhrase, pendingTransaction.vote?.resourceId, data]); - const rangeSliderCalculationElement = - dataLength < notSlicedDataLength - ? (screenWidth + - (onDashboard ? -290 - paddingOffset : -paddingOffset + 250)) / - 437 - : (screenWidth + (onDashboard ? -280 - paddingOffset : -paddingOffset)) / - 402; - - const handleLinkPress = useCallback(() => { - if (onDashboard) { - navigate( - generatePath(PATHS.dashboardGovernanceActionsCategory, { - category: navigateKey, - }), - ); - } else { - navigate( - generatePath(PATHS.governanceActionsCategory, { - category: navigateKey, - }), - ); - } - }, [onDashboard]); - return ( - - - {title} - - {isMobile && isShowAll && ( - + + - + {(notSlicedDataLength > 6 || (isMobile && isShowAll)) && ( + + )} + + {isShowArrows && dataLength > 1 && !isMobile && ( + )}
{data} - {!isMobile && isShowAll && dataLength < notSlicedDataLength && ( -
- - - {t("slider.viewAll")} - - arrow - -
- )}
- {!isMobile && Math.floor(rangeSliderCalculationElement) < dataLength && ( - - - - )}
); }; diff --git a/govtool/frontend/src/components/organisms/index.ts b/govtool/frontend/src/components/organisms/index.ts index bbded8e90..cccbc8400 100644 --- a/govtool/frontend/src/components/organisms/index.ts +++ b/govtool/frontend/src/components/organisms/index.ts @@ -17,6 +17,7 @@ export * from "./DrawerMobile"; export * from "./ExternalLinkModal"; export * from "./Footer"; export * from "./GovernanceActionDetailsCard"; +export * from "./GovernanceActionDetailsCardData"; export * from "./GovernanceActionsToVote"; export * from "./Hero"; export * from "./HomeCards"; diff --git a/govtool/frontend/src/consts/icons.ts b/govtool/frontend/src/consts/icons.ts index 2a5fc394d..dfabf260d 100644 --- a/govtool/frontend/src/consts/icons.ts +++ b/govtool/frontend/src/consts/icons.ts @@ -8,6 +8,7 @@ export const ICONS = { closeIcon: "/icons/Close.svg", closeWhiteIcon: "/icons/CloseWhite.svg", copyBlueIcon: "/icons/CopyBlue.svg", + copyBlueThinIcon: "/icons/CopyBlueThin.svg", copyIcon: "/icons/Copy.svg", copyWhiteIcon: "/icons/CopyWhite.svg", dashboardActiveIcon: "/icons/DashboardActive.svg", @@ -25,6 +26,7 @@ export const ICONS = { guidesIcon: "/icons/Guides.svg", helpIcon: "/icons/Help.svg", link: "/icons/Link.svg", + share: "/icons/Share.svg", sortActiveIcon: "/icons/SortActive.svg", sortIcon: "/icons/Sort.svg", sortWhiteIcon: "/icons/SortWhite.svg", diff --git a/govtool/frontend/src/hooks/useSlider.ts b/govtool/frontend/src/hooks/useSlider.ts index 78788791e..e73341422 100644 --- a/govtool/frontend/src/hooks/useSlider.ts +++ b/govtool/frontend/src/hooks/useSlider.ts @@ -1,4 +1,4 @@ -import { ChangeEvent, useState } from "react"; +import { useState } from "react"; import { KeenSliderOptions, useKeenSlider } from "keen-slider/react"; import type { KeenSliderInstance } from "keen-slider"; @@ -49,51 +49,26 @@ const WheelControls = (slider: KeenSliderInstance) => { export const useSlider = ({ config, - sliderMaxLength, }: { config: KeenSliderOptions; sliderMaxLength: number; }) => { const [currentSlide, setCurrentSlide] = useState(0); - const [currentRange, setCurrentRange] = useState(0); const [sliderRef, instanceRef] = useKeenSlider( { ...config, rubberband: false, detailsChanged: (slider) => { - setCurrentRange(slider.track.details.progress * sliderMaxLength); setCurrentSlide(slider.track.details.rel); }, }, [WheelControls], ); - const DATA_LENGTH = instanceRef?.current?.slides?.length ?? 10; - const ITEMS_PER_VIEW = - DATA_LENGTH - (instanceRef?.current?.track?.details?.maxIdx ?? 2); - - const setPercentageValue = (e: ChangeEvent) => { - const target = e?.target; - const currentIndexOfSlide = Math.floor( - +(target?.value ?? 0) / - (sliderMaxLength / (DATA_LENGTH - Math.floor(ITEMS_PER_VIEW))), - ); - - instanceRef.current?.track.add( - (+(target?.value ?? 0) - currentRange) * - (instanceRef.current.track.details.length / sliderMaxLength), - ); - setCurrentRange(+(target?.value ?? 0)); - setCurrentSlide(currentIndexOfSlide); - }; - return { sliderRef, instanceRef, currentSlide, - currentRange, - setCurrentRange, - setPercentageValue, }; }; diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index f70098f2a..28a2756db 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -314,24 +314,39 @@ export const en = { }, }, govActions: { + about: "About", + abstract: "Abstract:", + castVote: "<0>You voted {{vote}} for this proposal\nat {{date}}", + castVoteDeadline: + "You can change your vote up to the deadline of {{date}}", changeVote: "Change vote", changeYourVote: "Change your vote", chooseHowToVote: "Choose how you want to vote:", + dataMissing: "Data Missing", details: "Governance Details:", + expiresDateWithEpoch: "Expires: <0>{{date}} <1>(Epoch {{epoch}})", expiryDate: "Expiry date:", filterTitle: "Governance Action Type", forGovAction: "for this Governance Action", governanceActionId: "Governance Action ID:", governanceActionType: "Governance Action Type:", + motivation: "Motivation", myVote: "My Vote:", noResultsForTheSearch: "No results for the search.", + onChainTransactionDetails: "On-chain Transaction Details", optional: "(optional)", provideContext: "Provide context about your vote", + rationale: "Rationale", + seeExternalData: "See external data", selectDifferentOption: "Select a different option to change your vote", showVotes: "Show votes", submissionDate: "Submission date:", + submittedDateWithEpoch: + "Submitted: <0>{{date}} <1>(Epoch {{epoch}})", + supportingLinks: "Supporting links", title: "Governance Actions", toVote: "To vote", + viewDetails: "View Details", viewOtherDetails: "View other details", viewProposalDetails: "View proposal details", vote: "Vote", @@ -496,7 +511,7 @@ export const en = { }, }, slider: { - showAll: "Show all", + showAll: "Show All", viewAll: "View all", }, soleVoter: { @@ -590,7 +605,7 @@ export const en = { continue: "Continue", delegate: "Delegate", here: "here", - inProgress: "In Progress", + inProgress: "In progress", learnMore: "Learn more", loading: "Loading...", myDRepId: "My DRep ID:", @@ -602,6 +617,7 @@ export const en = { required: "required", seeTransaction: "See transaction", select: "Select", + showMore: "Show more", skip: "Skip", sortBy: "Sort by", submit: "Submit", diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx index 8685bfbf8..cf6a0bbdc 100644 --- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx @@ -191,6 +191,8 @@ export const DashboardGovernanceActionsCategory = () => { pendingTransaction.vote?.resourceId === item.txHash + item.index } + // TODO: Add data validation + isDataMissing={false} onClick={() => { saveScrollPosition(); diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index 34378488d..376e4156e 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -130,11 +130,7 @@ export const GovernanceActionDetails = () => {
) : data || state ? ( - + { } url={state ? state.url : data.proposal.url} yesVotes={state ? state.yesVotes : data.proposal.yesVotes} - shortenedGovActionId={shortenedGovActionId} + govActionId={fullProposalId} /> ) : ( diff --git a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx index e29cbe1c3..4538cb006 100644 --- a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx @@ -218,6 +218,8 @@ export const GovernanceActionsCategory = () => { {...item} txHash={item.txHash} index={item.index} + // TODO: Add data validation + isDataMissing={false} onClick={() => { saveScrollPosition(); diff --git a/govtool/frontend/src/theme.ts b/govtool/frontend/src/theme.ts index 8a8736f17..83cd35489 100644 --- a/govtool/frontend/src/theme.ts +++ b/govtool/frontend/src/theme.ts @@ -9,6 +9,59 @@ import { successGreen, } from "./consts"; +declare module "@mui/material/styles" { + interface Palette { + accentOrange: string; + accentYellow: string; + arcticWhite: string; + boxShadow1: string; + boxShadow2: string; + errorRed: string; + highlightBlue: string; + inputRed: string; + negativeRed: string; + neutralGray: string; + orangeDark: string; + neutralWhite: string; + positiveGreen: string; + primaryBlue: string; + secondaryBlue: string; + specialCyan: string; + specialCyanBorder: string; + lightBlue: string; + textBlack: string; + textGray: string; + lightOrange: string; + fadedPurple: string; + } + interface PaletteOptions { + accentOrange: string; + accentYellow: string; + arcticWhite: string; + boxShadow1: string; + boxShadow2: string; + errorRed: string; + highlightBlue: string; + orangeDark: string; + inputRed: string; + negativeRed: string; + neutralGray: string; + neutralWhite: string; + positiveGreen: string; + primaryBlue: string; + secondaryBlue: string; + specialCyan: string; + specialCyanBorder: string; + lightBlue: string; + textBlack: string; + textGray: string; + lightOrange: string; + fadedPurple: string; + } +} + +export type Theme = typeof theme; + export const theme = createTheme({ breakpoints: { values: { @@ -114,8 +167,10 @@ export const theme = createTheme({ palette: { accentOrange: "#F29339", accentYellow: "#F2D9A9", + arcticWhite: "#FBFBFF", boxShadow1: "rgba(0, 18, 61, 0.37)", boxShadow2: "rgba(47, 98, 220, 0.2)", + errorRed: "#9E2323", fadedPurple: "#716E88", highlightBlue: "#C2EFF299", inputRed: "#FAEAEB", From 471927736dc4dc5ee42b853fd2e64cf2aede06b6 Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Sat, 23 Mar 2024 13:45:34 +0100 Subject: [PATCH 2/5] Lint fixes --- .../frontend/src/components/atoms/Radio.tsx | 6 +- .../src/components/atoms/SliderArrow.tsx | 2 +- .../molecules/GovernanceActionCard.tsx | 4 +- .../molecules/GovernanceActionCardElement.tsx | 80 ++--- .../molecules/GovernanceActionCardHeader.tsx | 4 +- .../molecules/GovernanceActionCardMyVote.tsx | 2 +- .../GovernanceActionDetailsCardLinks.tsx | 4 +- ...GovernanceActionDetailsCardOnChainData.tsx | 2 +- .../molecules/GovernanceActionsDatesBox.tsx | 4 +- .../molecules/GovernanceVotedOnCard.tsx | 5 +- .../src/components/molecules/Share.tsx | 12 +- .../src/components/molecules/SliderArrows.tsx | 63 ++-- .../components/molecules/VoteActionForm.tsx | 8 +- .../components/molecules/VotesSubmitted.tsx | 2 +- .../DashboardGovernanceActionDetails.tsx | 6 +- .../DashboardGovernanceActionsVotedOn.tsx | 2 +- .../organisms/GovernanceActionDetailsCard.tsx | 8 +- .../GovernanceActionDetailsCardData.tsx | 4 +- .../organisms/GovernanceActionsToVote.tsx | 74 ++-- .../src/components/organisms/Slider.tsx | 6 +- .../DashboardGovernanceActionsCategory.tsx | 52 +-- .../src/pages/GovernanceActionDetails.tsx | 8 +- .../src/stories/GovernanceAction.stories.ts | 107 +++--- .../GovernanceActionDetailsCard.stories.ts | 127 +++---- .../stories/GovernanceActionVoted.stories.ts | 339 +++++++++--------- 25 files changed, 475 insertions(+), 456 deletions(-) diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index bf4184681..9695dd30c 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -42,9 +42,7 @@ export const Radio = ({ ...props }: RadioProps) => { border={isChecked ? 2 : 0} borderColor={isChecked ? "specialCyanBorder" : undefined} sx={[{ "&:hover": { color: "blue", cursor: "pointer" } }]} - boxShadow={ - "0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)" - } + boxShadow="0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)" > { checked={isChecked} /> diff --git a/govtool/frontend/src/components/atoms/SliderArrow.tsx b/govtool/frontend/src/components/atoms/SliderArrow.tsx index 2ab59999a..aee0bf94e 100644 --- a/govtool/frontend/src/components/atoms/SliderArrow.tsx +++ b/govtool/frontend/src/components/atoms/SliderArrow.tsx @@ -5,7 +5,7 @@ import { theme } from "@/theme"; interface Props { disabled: boolean; - onClick: (e: any) => void; + onClick: (e: React.MouseEvent) => void; left?: boolean; } diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx index c9f6bbeb3..159cd8a73 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx @@ -32,7 +32,7 @@ interface ActionTypeProps | "index" > { onClick?: () => void; - inProgress?: boolean; + // inProgress?: boolean; txHash: string; index: number; isDataMissing: boolean; @@ -41,7 +41,7 @@ interface ActionTypeProps export const GovernanceActionCard: FC = ({ ...props }) => { const { type, - inProgress = false, + // inProgress = false, expiryDate, onClick, createdDate, diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx index fe6baf8c2..51186d699 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx @@ -33,21 +33,20 @@ export const GovernanceActionCardElement = ({ isCopyButton, tooltipProps, marginBottom, -}: GovernanceActionCardElementProps) => { - return ( +}: GovernanceActionCardElementProps) => ( + - - + - {label} - - {tooltipProps && ( - - - + > + {label} + + {tooltipProps && ( + + + )} - - - {textVariant === "pill" ? ( - + + {textVariant === "pill" ? ( + - + - {text} - - + > + {text} + + ) : ( )} - + ); -}; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx index 148377b7e..3d0c023ca 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardHeader.tsx @@ -43,9 +43,9 @@ export const GovernanceActionCardHeader = ({ {isDataMissing && ( { const { t } = useTranslation(); return ( - + { rowGap: 2, }} > - {LINKS.map((link, index) => ( - + {LINKS.map((link) => ( + ))} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx index ae8872ad4..1a4077ce0 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardOnChainData.tsx @@ -57,7 +57,7 @@ export const GovernanceActionDetailsCardOnChainData = ({ }} > {label} - {":"} + : { const navigate = useNavigate(); @@ -104,7 +105,7 @@ export const GovernanceVotedOnCard = ({ { const { addSuccessAlert } = useSnackbar(); const { t } = useTranslation(); - const [anchorEl, setAnchorEl] = useState(null); - const [isActive, setIsActive] = useState(true); + const [anchorEl, setAnchorEl] = useState(null); - const handleClick = (event: any) => { - setAnchorEl(event.currentTarget); + const handleClick = (e: React.MouseEvent) => { + setAnchorEl(e.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; - const onCopy = (e: any) => { + const onCopy = (e: React.MouseEvent) => { navigator.clipboard.writeText(link); addSuccessAlert(t("alerts.copiedToClipboard")); - setIsActive(false); e.stopPropagation(); }; @@ -46,7 +44,7 @@ export const Share = ({ link }: { link: string }) => { padding: 1.5, }} > - + share icon | null>; } -export const SliderArrows = ({ currentSlide, instanceRef }: ArrowsProps) => { - return ( - <> - {instanceRef.current && ( - ( + <> + {instanceRef.current && ( + + ) => { + e.stopPropagation(); + instanceRef.current?.prev(); }} - > - - e.stopPropagation() || instanceRef.current?.prev() - } - disabled={currentSlide === 0} - /> - - e.stopPropagation() || instanceRef.current?.next() - } - disabled={ - currentSlide === - instanceRef.current.track.details.slides.length - 1 - } - /> - - )} - - ); -}; + disabled={currentSlide === 0} + /> + ) => { + e.stopPropagation(); + instanceRef.current?.next(); + }} + disabled={ + currentSlide === instanceRef.current.track.details.slides.length - 1 + } + /> + + )} + +); diff --git a/govtool/frontend/src/components/molecules/VoteActionForm.tsx b/govtool/frontend/src/components/molecules/VoteActionForm.tsx index 2cecfbc2a..26a6bb484 100644 --- a/govtool/frontend/src/components/molecules/VoteActionForm.tsx +++ b/govtool/frontend/src/components/molecules/VoteActionForm.tsx @@ -20,8 +20,8 @@ import { } from "@hooks"; import { openInNewTab } from "@utils"; -import { ControlledField } from "../organisms"; import { Trans } from "react-i18next"; +import { ControlledField } from "../organisms"; // TODO: Change into props when BE is ready const castVoteDate = undefined; @@ -237,8 +237,8 @@ export const VoteActionForm = ({ flexDirection="row" flexWrap="wrap" justifyContent="center" - mb={"15px"} - mt={"58px"} + mb="15px" + mt="58px" onClick={handleContext} >

{ > {t("govActions.forGovAction")} - + {t("govActions.votesSubmittedOnChain")} { ? formatDisplayDate(state.createdDate) : formatDisplayDate(data.proposal.createdDate) } - details={state ? state.details : data.proposal.details} + // TODO: To decide if we want to keep it when metadate BE is ready + // details={state ? state.details : data.proposal.details} expiryDate={ state ? formatDisplayDate(state.expiryDate) @@ -145,7 +146,8 @@ export const DashboardGovernanceActionDetails = () => { ? getProposalTypeLabel(state.type) : getProposalTypeLabel(data.proposal.type) } - url={state ? state.url : data.proposal.url} + // TODO: To decide if we want to keep it when metadate BE is ready + // url={state ? state.url : data.proposal.url} yesVotes={state ? state.yesVotes : data.proposal.yesVotes} voteFromEP={data?.vote?.vote} govActionId={fullProposalId} diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx index a958fc570..e14f42f35 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx @@ -76,7 +76,7 @@ export const DashboardGovernanceActionsVotedOn = ({ > {isDataMissing && } @@ -72,7 +72,7 @@ export const GovernanceActionDetailsCardData = ({ /> {isDataMissing && ( )} diff --git a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx index 794a33dc3..e8862e026 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx @@ -10,6 +10,13 @@ import { GovernanceActionCard } from "@molecules"; import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from "@utils"; import { Slider } from "@organisms"; +type GroupedActions = { + [key: string]: { + title: string; + actions: ActionType[]; + }; +}; + type GovernanceActionsToVoteProps = { filters: string[]; sorting: string; @@ -54,44 +61,47 @@ export const GovernanceActionsToVote = ({ {...action} txHash={action.txHash} index={action.index} + // inProgress={ + // onDashboard && + // pendingTransaction.vote?.resourceId === + // action?.txHash + action?.index + // } // TODO: Add data validation isDataMissing={false} - inProgress={ - onDashboard && - pendingTransaction.vote?.resourceId === - action?.txHash + action?.index - } - // eslint-disable-next-line no-confusing-arrow - onClick={() => - onDashboard && - pendingTransaction.vote?.resourceId === - action?.txHash + action?.index - ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) - : navigate( - onDashboard - ? generatePath( - PATHS.dashboardGovernanceActionsAction, - { - proposalId: getFullGovActionId( - action.txHash, - action.index, - ), - }, - ) - : PATHS.governanceActionsAction.replace( - ":proposalId", - getFullGovActionId( + onClick={() => { + if ( + onDashboard && + pendingTransaction.vote?.resourceId === + `${action.txHash ?? ""}${action.index ?? ""}` + ) { + openInNewTab( + "https://adanordic.com/latest_transactions", + ); + } else { + navigate( + onDashboard + ? generatePath( + PATHS.dashboardGovernanceActionsAction, + { + proposalId: getFullGovActionId( action.txHash, action.index, ), + }, + ) + : PATHS.governanceActionsAction.replace( + ":proposalId", + getFullGovActionId( + action.txHash, + action.index, ), - { - state: { ...action }, - }, - ) - } + ), + { + state: { ...action }, + }, + ); + } + }} /> ))} diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index f4f0499c5..545bb8c47 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -8,8 +8,8 @@ import { useCardano } from "@context"; import { useScreenDimension, useTranslation, useSlider } from "@hooks"; import { Button, Typography } from "@atoms"; import { SliderArrows } from "@molecules"; -import { theme } from "@/theme"; import { PATHS } from "@consts"; +import { theme } from "@/theme"; const SLIDER_MAX_LENGTH = 1000; @@ -115,7 +115,7 @@ export const Slider = ({ "&:hover": { backgroundColor: arcticWhite }, }} onClick={() => - onDashboard + (onDashboard ? navigate( generatePath(PATHS.dashboardGovernanceActionsCategory, { category: navigateKey, @@ -125,7 +125,7 @@ export const Slider = ({ generatePath(PATHS.governanceActionsCategory, { category: navigateKey, }), - ) + )) } > {t("slider.showAll")} diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx index cf6a0bbdc..16187ab72 100644 --- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx @@ -175,49 +175,51 @@ export const DashboardGovernanceActionsCategory = () => { {mappedData.map((item) => ( { saveScrollPosition(); // eslint-disable-next-line no-unused-expressions - pendingTransaction.vote?.resourceId === item.txHash + item.index + pendingTransaction.vote?.resourceId === + item.txHash + item.index ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) + "https://adanordic.com/latest_transactions", + ) : navigate( - generatePath( - PATHS.dashboardGovernanceActionsAction, + generatePath( + PATHS.dashboardGovernanceActionsAction, + { + proposalId: getFullGovActionId( + item.txHash, + item.index, + ), + }, + ), { - proposalId: getFullGovActionId( - item.txHash, - item.index, - ), - }, - ), - { - state: { - ...item, - openedFromCategoryPage: true, + state: { + ...item, + openedFromCategoryPage: true, + }, }, - }, - ); + ); }} txHash={item.txHash} /> diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index 376e4156e..fe916ca4d 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -130,7 +130,7 @@ export const GovernanceActionDetails = () => { ) : data || state ? ( - + { ? formatDisplayDate(state.createdDate) : formatDisplayDate(data.proposal.createdDate) } - details={state ? state.details : data.proposal.details} + // TODO: To decide if we want to keep it when metadate BE is ready + // details={state ? state.details : data.proposal.details} expiryDate={ state ? formatDisplayDate(state.expiryDate) @@ -152,7 +153,8 @@ export const GovernanceActionDetails = () => { ? getProposalTypeLabel(state.type) : getProposalTypeLabel(data.proposal.type) } - url={state ? state.url : data.proposal.url} + // TODO: To decide if we want to keep it when metadate BE is ready + // url={state ? state.url : data.proposal.url} yesVotes={state ? state.yesVotes : data.proposal.yesVotes} govActionId={fullProposalId} /> diff --git a/govtool/frontend/src/stories/GovernanceAction.stories.ts b/govtool/frontend/src/stories/GovernanceAction.stories.ts index 8ed28776b..770b603c0 100644 --- a/govtool/frontend/src/stories/GovernanceAction.stories.ts +++ b/govtool/frontend/src/stories/GovernanceAction.stories.ts @@ -1,58 +1,61 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { within, userEvent, waitFor, screen } from "@storybook/testing-library"; -import { expect, jest } from "@storybook/jest"; +// Story to be updated when new Gov Actions are finished +/* eslint-disable storybook/default-exports */ -import { formatDisplayDate } from "@utils"; -import { GovernanceActionCard } from "@/components/molecules"; +// import type { Meta, StoryObj } from "@storybook/react"; +// import { within, userEvent, waitFor, screen } from "@storybook/testing-library"; +// import { expect, jest } from "@storybook/jest"; -const meta = { - title: "Example/GovernanceActionCard", - component: GovernanceActionCard, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], -} satisfies Meta; +// import { formatDisplayDate } from "@utils"; +// import { GovernanceActionCard } from "@/components/molecules"; -export default meta; -type Story = StoryObj; +// const meta = { +// title: "Example/GovernanceActionCard", +// component: GovernanceActionCard, +// parameters: { +// layout: "centered", +// }, +// tags: ["autodocs"], +// } satisfies Meta; -export const GovernanceActionCardComponent: Story = { - args: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - type: "exampleType", - txHash: "sad78afdsf7jasd98d", - index: 2, - onClick: jest.fn(), - }, - play: async ({ canvasElement, args }) => { - const canvas = within(canvasElement); - expect(canvas.getByTestId("exampleType-type")).toBeInTheDocument(); - expect(canvas.getByTestId("sad78afdsf7jasd98d#2-id")).toBeInTheDocument(); - expect( - canvas.getByText(formatDisplayDate("1970-01-01T00:00:00Z")), - ).toBeInTheDocument(); - expect( - canvas.getByText(formatDisplayDate("1970-02-01T00:00:00Z")), - ).toBeInTheDocument(); +// export default meta; +// type Story = StoryObj; - const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); - await userEvent.hover(tooltips[0]); - await waitFor(async () => { - expect(screen.getByRole("tooltip")).toBeInTheDocument(); - expect(screen.getByRole("tooltip")).toHaveTextContent(/Submission Date/i); - await userEvent.unhover(tooltips[0]); - }); - await userEvent.hover(tooltips[1]); - await waitFor(() => { - expect(screen.getByRole("tooltip")).toBeInTheDocument(); - expect(screen.getByRole("tooltip")).toHaveTextContent(/Expiry Date/i); - }); +// export const GovernanceActionCardComponent: Story = { +// args: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// type: "exampleType", +// txHash: "sad78afdsf7jasd98d", +// index: 2, +// onClick: jest.fn(), +// }, +// play: async ({ canvasElement, args }) => { +// const canvas = within(canvasElement); +// expect(canvas.getByTestId("exampleType-type")).toBeInTheDocument(); +// expect(canvas.getByTestId("sad78afdsf7jasd98d#2-id")).toBeInTheDocument(); +// expect( +// canvas.getByText(formatDisplayDate("1970-01-01T00:00:00Z")), +// ).toBeInTheDocument(); +// expect( +// canvas.getByText(formatDisplayDate("1970-02-01T00:00:00Z")), +// ).toBeInTheDocument(); - await userEvent.click( - canvas.getByTestId("govaction-sad78afdsf7jasd98d#2-view-detail"), - ); - await expect(args.onClick).toHaveBeenCalled(); - }, -}; +// const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); +// await userEvent.hover(tooltips[0]); +// await waitFor(async () => { +// expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// expect(screen.getByRole("tooltip")).toHaveTextContent(/Submission Date/i); +// await userEvent.unhover(tooltips[0]); +// }); +// await userEvent.hover(tooltips[1]); +// await waitFor(() => { +// expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// expect(screen.getByRole("tooltip")).toHaveTextContent(/Expiry Date/i); +// }); + +// await userEvent.click( +// canvas.getByTestId("govaction-sad78afdsf7jasd98d#2-view-detail"), +// ); +// await expect(args.onClick).toHaveBeenCalled(); +// }, +// }; diff --git a/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts b/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts index 8f7d8793d..454db3a52 100644 --- a/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts +++ b/govtool/frontend/src/stories/GovernanceActionDetailsCard.stories.ts @@ -1,62 +1,65 @@ -import type { Meta, StoryObj } from "@storybook/react"; -import { screen, userEvent, waitFor, within } from "@storybook/testing-library"; -import { GovernanceActionDetailsCard } from "@organisms"; -import { expect } from "@storybook/jest"; - -const meta = { - title: "Example/GovernanceActionDetailsCard", - component: GovernanceActionDetailsCard, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const GovernanceActionDetailsCardComponent: Story = { - args: { - abstainVotes: 1000000, - createdDate: new Date().toLocaleDateString(), - expiryDate: new Date().toLocaleDateString(), - shortenedGovActionId: "Example id", - noVotes: 1000000, - type: "Gov Type", - url: "https://exampleurl.com", - yesVotes: 1000000, - details: { - key: "value", - key2: ["key-list", "key-list", "key-list"], - }, - }, - - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const todayDate = new Date().toLocaleDateString(); - await expect(canvas.getAllByText(todayDate)).toHaveLength(2); - - const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); - await userEvent.hover(tooltips[0]); - await waitFor(async () => { - await expect(screen.getByRole("tooltip")).toBeInTheDocument(); - await expect(screen.getByRole("tooltip")).toHaveTextContent( - /Submission Date/i, - ); - await userEvent.unhover(tooltips[0]); - }); - await userEvent.hover(tooltips[1]); - - await expect(canvas.getByText("Gov Type")).toBeInTheDocument(); - await expect(canvas.getByText("Example id")).toBeInTheDocument(); - - await expect(canvas.getByText("key: value")).toBeInTheDocument(); - await expect(canvas.getAllByText("key-list")).toHaveLength(3); - - await expect(canvas.getByText(/Yes/i)).toBeInTheDocument(); - await expect(canvas.getByText(/Abstain/i)).toBeInTheDocument(); - await expect(canvas.getByText(/No/i)).toBeInTheDocument(); - - await userEvent.click(canvas.getByTestId("view-other-details-button")); - }, -}; +// Story to be updated when new Gov Actions are finished +/* eslint-disable storybook/default-exports */ + +// import type { Meta, StoryObj } from "@storybook/react"; +// import { screen, userEvent, waitFor, within } from "@storybook/testing-library"; +// import { GovernanceActionDetailsCard } from "@organisms"; +// import { expect } from "@storybook/jest"; + +// const meta = { +// title: "Example/GovernanceActionDetailsCard", +// component: GovernanceActionDetailsCard, +// parameters: { +// layout: "centered", +// }, +// tags: ["autodocs"], +// } satisfies Meta; + +// export default meta; +// type Story = StoryObj; + +// export const GovernanceActionDetailsCardComponent: Story = { +// args: { +// abstainVotes: 1000000, +// createdDate: new Date().toLocaleDateString(), +// expiryDate: new Date().toLocaleDateString(), +// shortenedGovActionId: "Example id", +// noVotes: 1000000, +// type: "Gov Type", +// url: "https://exampleurl.com", +// yesVotes: 1000000, +// details: { +// key: "value", +// key2: ["key-list", "key-list", "key-list"], +// }, +// }, + +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// const todayDate = new Date().toLocaleDateString(); +// await expect(canvas.getAllByText(todayDate)).toHaveLength(2); + +// const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); +// await userEvent.hover(tooltips[0]); +// await waitFor(async () => { +// await expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// await expect(screen.getByRole("tooltip")).toHaveTextContent( +// /Submission Date/i, +// ); +// await userEvent.unhover(tooltips[0]); +// }); +// await userEvent.hover(tooltips[1]); + +// await expect(canvas.getByText("Gov Type")).toBeInTheDocument(); +// await expect(canvas.getByText("Example id")).toBeInTheDocument(); + +// await expect(canvas.getByText("key: value")).toBeInTheDocument(); +// await expect(canvas.getAllByText("key-list")).toHaveLength(3); + +// await expect(canvas.getByText(/Yes/i)).toBeInTheDocument(); +// await expect(canvas.getByText(/Abstain/i)).toBeInTheDocument(); +// await expect(canvas.getByText(/No/i)).toBeInTheDocument(); + +// await userEvent.click(canvas.getByTestId("view-other-details-button")); +// }, +// }; diff --git a/govtool/frontend/src/stories/GovernanceActionVoted.stories.ts b/govtool/frontend/src/stories/GovernanceActionVoted.stories.ts index 4fdbc5790..3547562ab 100644 --- a/govtool/frontend/src/stories/GovernanceActionVoted.stories.ts +++ b/govtool/frontend/src/stories/GovernanceActionVoted.stories.ts @@ -1,180 +1,183 @@ -import type { Meta, StoryObj } from "@storybook/react"; +// Story to be updated when new Gov Actions are finished +/* eslint-disable storybook/default-exports */ -import { GovernanceVotedOnCard } from "@molecules"; -import { userEvent, waitFor, within, screen } from "@storybook/testing-library"; -import { expect } from "@storybook/jest"; -import { formatDisplayDate } from "@/utils"; +// import type { Meta, StoryObj } from "@storybook/react"; -const meta = { - title: "Example/GovernanceVotedOnCard", - component: GovernanceVotedOnCard, - parameters: { - layout: "centered", - }, +// import { GovernanceVotedOnCard } from "@molecules"; +// import { userEvent, waitFor, within, screen } from "@storybook/testing-library"; +// import { expect } from "@storybook/jest"; +// import { formatDisplayDate } from "@/utils"; - tags: ["autodocs"], -} satisfies Meta; +// const meta = { +// title: "Example/GovernanceVotedOnCard", +// component: GovernanceVotedOnCard, +// parameters: { +// layout: "centered", +// }, -export default meta; -type Story = StoryObj; +// tags: ["autodocs"], +// } satisfies Meta; -async function checkGovActionVisibility(canvas: ReturnType) { - expect(canvas.getByTestId("exampleType-type")).toBeInTheDocument(); - expect(canvas.getByTestId("exampleHash#1-id")).toBeInTheDocument(); - expect(canvas.getByText(/vote submitted/i)).toBeInTheDocument(); +// export default meta; +// type Story = StoryObj; - expect( - canvas.getByText(formatDisplayDate("1970-01-01T00:00:00Z")), - ).toBeInTheDocument(); - expect( - canvas.getByText(formatDisplayDate("1970-02-01T00:00:00Z")), - ).toBeInTheDocument(); +// async function checkGovActionVisibility(canvas: ReturnType) { +// expect(canvas.getByTestId("exampleType-type")).toBeInTheDocument(); +// expect(canvas.getByTestId("exampleHash#1-id")).toBeInTheDocument(); +// expect(canvas.getByText(/vote submitted/i)).toBeInTheDocument(); - const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); - await userEvent.hover(tooltips[0]); - await waitFor(async () => { - expect(screen.getByRole("tooltip")).toBeInTheDocument(); - expect(screen.getByRole("tooltip")).toHaveTextContent(/Submission Date/i); - await userEvent.unhover(tooltips[0]); - }); - await userEvent.hover(tooltips[1]); - await waitFor(() => { - expect(screen.getByRole("tooltip")).toBeInTheDocument(); - expect(screen.getByRole("tooltip")).toHaveTextContent(/Expiry Date/i); - }); +// expect( +// canvas.getByText(formatDisplayDate("1970-01-01T00:00:00Z")), +// ).toBeInTheDocument(); +// expect( +// canvas.getByText(formatDisplayDate("1970-02-01T00:00:00Z")), +// ).toBeInTheDocument(); - await expect( - canvas.getByTestId("govaction-exampleHash#1-change-your-vote"), - ).toBeInTheDocument(); -} +// const tooltips = canvas.getAllByTestId("InfoOutlinedIcon"); +// await userEvent.hover(tooltips[0]); +// await waitFor(async () => { +// expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// expect(screen.getByRole("tooltip")).toHaveTextContent(/Submission Date/i); +// await userEvent.unhover(tooltips[0]); +// }); +// await userEvent.hover(tooltips[1]); +// await waitFor(() => { +// expect(screen.getByRole("tooltip")).toBeInTheDocument(); +// expect(screen.getByRole("tooltip")).toHaveTextContent(/Expiry Date/i); +// }); -export const GovernanceVotedOnCardComponent: Story = { - args: { - votedProposal: { - proposal: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - id: "exampleId", - type: "exampleType", - index: 1, - txHash: "exampleHash", - details: "some details", - url: "https://example.com", - metadataHash: "exampleHash", - yesVotes: 1, - noVotes: 0, - abstainVotes: 2, - }, - vote: { - vote: "no", - proposalId: "exampleId", - drepId: "exampleId", - url: "https://example.com", - metadataHash: "exampleHash", - }, - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await checkGovActionVisibility(canvas); - }, -}; +// await expect( +// canvas.getByTestId("govaction-exampleHash#1-change-your-vote"), +// ).toBeInTheDocument(); +// } -export const GovernanceVotedOnCardAbstain: Story = { - args: { - votedProposal: { - proposal: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - id: "exampleId", - type: "exampleType", - index: 1, - txHash: "exampleHash", - details: "some details", - url: "https://example.com", - metadataHash: "exampleHash", - yesVotes: 1, - noVotes: 0, - abstainVotes: 2, - }, - vote: { - vote: "abstain", - proposalId: "exampleId", - drepId: "exampleId", - url: "https://example.com", - metadataHash: "exampleHash", - }, - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await checkGovActionVisibility(canvas); - expect(canvas.getByText(/abstain/i)).toBeInTheDocument(); - }, -}; +// export const GovernanceVotedOnCardComponent: Story = { +// args: { +// votedProposal: { +// proposal: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// id: "exampleId", +// type: "exampleType", +// index: 1, +// txHash: "exampleHash", +// details: "some details", +// url: "https://example.com", +// metadataHash: "exampleHash", +// yesVotes: 1, +// noVotes: 0, +// abstainVotes: 2, +// }, +// vote: { +// vote: "no", +// proposalId: "exampleId", +// drepId: "exampleId", +// url: "https://example.com", +// metadataHash: "exampleHash", +// }, +// }, +// }, +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// await checkGovActionVisibility(canvas); +// }, +// }; -export const GovernanceVotedOnCardYes: Story = { - args: { - votedProposal: { - proposal: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - id: "exampleId", - type: "exampleType", - index: 1, - txHash: "exampleHash", - details: "some details", - url: "https://example.com", - metadataHash: "exampleHash", - yesVotes: 1, - noVotes: 0, - abstainVotes: 2, - }, - vote: { - vote: "yes", - proposalId: "exampleId", - drepId: "exampleId", - url: "https://example.com", - metadataHash: "exampleHash", - }, - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await checkGovActionVisibility(canvas); - expect(canvas.getByText(/yes/i)).toBeInTheDocument(); - }, -}; +// export const GovernanceVotedOnCardAbstain: Story = { +// args: { +// votedProposal: { +// proposal: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// id: "exampleId", +// type: "exampleType", +// index: 1, +// txHash: "exampleHash", +// details: "some details", +// url: "https://example.com", +// metadataHash: "exampleHash", +// yesVotes: 1, +// noVotes: 0, +// abstainVotes: 2, +// }, +// vote: { +// vote: "abstain", +// proposalId: "exampleId", +// drepId: "exampleId", +// url: "https://example.com", +// metadataHash: "exampleHash", +// }, +// }, +// }, +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// await checkGovActionVisibility(canvas); +// expect(canvas.getByText(/abstain/i)).toBeInTheDocument(); +// }, +// }; -export const GovernanceVotedOnCardNo: Story = { - args: { - votedProposal: { - proposal: { - createdDate: "1970-01-01T00:00:00Z", - expiryDate: "1970-02-01T00:00:00Z", - id: "exampleId", - type: "exampleType", - index: 1, - txHash: "exampleHash", - details: "some details", - url: "https://example.com", - metadataHash: "exampleHash", - yesVotes: 1, - noVotes: 0, - abstainVotes: 2, - }, - vote: { - vote: "no", - proposalId: "exampleId", - drepId: "exampleId", - url: "https://example.com", - metadataHash: "exampleHash", - }, - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await checkGovActionVisibility(canvas); - expect(canvas.getByText(/no/i)).toBeInTheDocument(); - }, -}; +// export const GovernanceVotedOnCardYes: Story = { +// args: { +// votedProposal: { +// proposal: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// id: "exampleId", +// type: "exampleType", +// index: 1, +// txHash: "exampleHash", +// details: "some details", +// url: "https://example.com", +// metadataHash: "exampleHash", +// yesVotes: 1, +// noVotes: 0, +// abstainVotes: 2, +// }, +// vote: { +// vote: "yes", +// proposalId: "exampleId", +// drepId: "exampleId", +// url: "https://example.com", +// metadataHash: "exampleHash", +// }, +// }, +// }, +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// await checkGovActionVisibility(canvas); +// expect(canvas.getByText(/yes/i)).toBeInTheDocument(); +// }, +// }; + +// export const GovernanceVotedOnCardNo: Story = { +// args: { +// votedProposal: { +// proposal: { +// createdDate: "1970-01-01T00:00:00Z", +// expiryDate: "1970-02-01T00:00:00Z", +// id: "exampleId", +// type: "exampleType", +// index: 1, +// txHash: "exampleHash", +// details: "some details", +// url: "https://example.com", +// metadataHash: "exampleHash", +// yesVotes: 1, +// noVotes: 0, +// abstainVotes: 2, +// }, +// vote: { +// vote: "no", +// proposalId: "exampleId", +// drepId: "exampleId", +// url: "https://example.com", +// metadataHash: "exampleHash", +// }, +// }, +// }, +// play: async ({ canvasElement }) => { +// const canvas = within(canvasElement); +// await checkGovActionVisibility(canvas); +// expect(canvas.getByText(/no/i)).toBeInTheDocument(); +// }, +// }; From 2aa8b89f50e98920aa4911caa5150da70410702e Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Sun, 24 Mar 2024 16:31:30 +0100 Subject: [PATCH 3/5] Further changes --- govtool/frontend/public/icons/Separator.svg | 3 + .../components/atoms/ExternalModalButton.tsx | 1 + .../frontend/src/components/atoms/Radio.tsx | 13 +- .../src/components/atoms/SliderArrow.tsx | 9 +- .../src/components/molecules/Breadcrumbs.tsx | 57 ++++++++ .../components/molecules/DataActionsBar.tsx | 3 +- .../molecules/GovernanceActionCard.tsx | 25 ++-- .../molecules/GovernanceActionCardHeader.tsx | 8 +- .../GovernanceActionCardStatePill.tsx | 1 + .../GovernanceActionDetailsCardHeader.tsx | 1 + .../GovernanceActionDetailsCardLinks.tsx | 10 +- ...GovernanceActionDetailsCardOnChainData.tsx | 6 +- .../molecules/GovernanceActionsDatesBox.tsx | 2 + .../molecules/GovernanceVotedOnCard.tsx | 28 ++-- .../components/molecules/OrderActionsChip.tsx | 79 ++++++++--- .../src/components/molecules/Share.tsx | 4 +- .../src/components/molecules/SliderArrows.tsx | 14 +- .../components/molecules/VoteActionForm.tsx | 68 ++++----- .../src/components/molecules/index.ts | 1 + .../DashboardGovernanceActionDetails.tsx | 48 ++----- .../organisms/DashboardGovernanceActions.tsx | 2 +- .../DashboardGovernanceActionsVotedOn.tsx | 2 +- .../organisms/GovernanceActionDetailsCard.tsx | 10 +- .../GovernanceActionDetailsCardData.tsx | 12 +- .../organisms/GovernanceActionsToVote.tsx | 18 +-- .../src/components/organisms/Slider.tsx | 77 +++++----- govtool/frontend/src/consts/icons.ts | 1 + govtool/frontend/src/context/wallet.tsx | 130 ++++++++--------- govtool/frontend/src/hooks/useSlider.ts | 12 +- govtool/frontend/src/i18n/locales/en.ts | 9 +- .../DashboardGovernanceActionsCategory.tsx | 133 ++++++++---------- .../src/pages/GovernanceActionDetails.tsx | 60 ++++---- .../src/pages/GovernanceActionsCategory.tsx | 71 ++-------- .../src/utils/getProposalTypeLabel.ts | 2 + 34 files changed, 497 insertions(+), 423 deletions(-) create mode 100644 govtool/frontend/public/icons/Separator.svg create mode 100644 govtool/frontend/src/components/molecules/Breadcrumbs.tsx diff --git a/govtool/frontend/public/icons/Separator.svg b/govtool/frontend/public/icons/Separator.svg new file mode 100644 index 000000000..e6edeb0dd --- /dev/null +++ b/govtool/frontend/public/icons/Separator.svg @@ -0,0 +1,3 @@ + + + diff --git a/govtool/frontend/src/components/atoms/ExternalModalButton.tsx b/govtool/frontend/src/components/atoms/ExternalModalButton.tsx index c4debddc5..8584c15b0 100644 --- a/govtool/frontend/src/components/atoms/ExternalModalButton.tsx +++ b/govtool/frontend/src/components/atoms/ExternalModalButton.tsx @@ -32,6 +32,7 @@ export const ExternalModalButton = ({ }} disableRipple variant="text" + data-testid="external-modal-button" > {label} diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index 9695dd30c..f93e68ec3 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -41,8 +41,17 @@ export const Radio = ({ ...props }: RadioProps) => { p={isChecked ? "2px" : 0} border={isChecked ? 2 : 0} borderColor={isChecked ? "specialCyanBorder" : undefined} - sx={[{ "&:hover": { color: "blue", cursor: "pointer" } }]} - boxShadow="0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)" + sx={[ + { + boxShadow: + "0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)", + + "&:hover": { + color: "blue", + cursor: disabled ? "default" : "pointer", + }, + }, + ]} > ) => void; left?: boolean; } -export const SliderArrow = ({ disabled, onClick, left }: Props) => { +export const SliderArrow = ({ disabled, onClick, left }: SliderArrowProps) => { const { palette: { primaryBlue, arcticWhite, lightBlue }, } = theme; @@ -27,6 +27,11 @@ export const SliderArrow = ({ disabled, onClick, left }: Props) => { justifyContent: "center", alignItems: "center", cursor: "pointer", + transition: "0.3s", + + "&:hover": { + boxShadow: disabled ? 0 : 2, + }, }} > { + const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); + + return ( + + + + {elementOne} + + + separator + + {isDataMissing ? t("govActions.dataMissing") : elementTwo} + + + ); +}; diff --git a/govtool/frontend/src/components/molecules/DataActionsBar.tsx b/govtool/frontend/src/components/molecules/DataActionsBar.tsx index c54010f5a..297d58bab 100644 --- a/govtool/frontend/src/components/molecules/DataActionsBar.tsx +++ b/govtool/frontend/src/components/molecules/DataActionsBar.tsx @@ -2,10 +2,9 @@ import { Dispatch, FC, SetStateAction } from "react"; import { Box, InputBase } from "@mui/material"; import Search from "@mui/icons-material/Search"; -import { GovernanceActionsFilters, GovernanceActionsSorting } from "."; +import { GovernanceActionsFilters, GovernanceActionsSorting } from "@molecules"; import { OrderActionsChip } from "./OrderActionsChip"; import { ClickOutside } from "../atoms"; - import { theme } from "@/theme"; type DataActionsBarProps = { diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx index 159cd8a73..29bb4dad4 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCard.tsx @@ -5,6 +5,7 @@ import { Button } from "@atoms"; import { GovernanceActionCardElement, GovernanceActionCardHeader, + GovernanceActionCardStatePill, GovernanceActionsDatesBox, } from "@molecules"; @@ -13,6 +14,7 @@ import { formatDisplayDate, getFullGovActionId, getProposalTypeLabel, + getProposalTypeNoEmptySpaces, } from "@utils"; const mockedLongText = @@ -31,17 +33,17 @@ interface ActionTypeProps | "txHash" | "index" > { - onClick?: () => void; - // inProgress?: boolean; txHash: string; index: number; isDataMissing: boolean; + onClick?: () => void; + inProgress?: boolean; } export const GovernanceActionCard: FC = ({ ...props }) => { const { type, - // inProgress = false, + inProgress = false, expiryDate, onClick, createdDate, @@ -53,10 +55,6 @@ export const GovernanceActionCard: FC = ({ ...props }) => { const { t } = useTranslation(); const govActionId = getFullGovActionId(txHash, index); - const proposalTypeNoEmptySpaces = getProposalTypeLabel(type).replace( - / /g, - "", - ); return ( = ({ ...props }) => { ...(isDataMissing && { border: "1px solid #F6D5D5", }), + ...(inProgress && { + border: "1px solid #FFCBAD", + }), }} - data-testid={`govaction-${proposalTypeNoEmptySpaces}-card`} + data-testid={`govaction-${getProposalTypeNoEmptySpaces(type)}-card`} > + {inProgress && } = ({ ...props }) => { label={t("govActions.governanceActionType")} text={getProposalTypeLabel(type)} textVariant="pill" - dataTestId={`${proposalTypeNoEmptySpaces}-type`} + dataTestId={`${getProposalTypeNoEmptySpaces(type)}-type`} isSliderCard /> = ({ ...props }) => { > )} + {/* TODO: Change below into new voting context */} {(state?.vote && state?.vote !== vote) || - (voteFromEP && voteFromEP !== vote) ? ( - - {isMobile ? renderChangeVoteButton : renderCancelButton} - - {isMobile ? renderCancelButton : renderChangeVoteButton} - + (voteFromEP && voteFromEP !== vote) ? ( + + {isMobile ? renderChangeVoteButton : renderCancelButton} + + {isMobile ? renderCancelButton : renderChangeVoteButton} + ) : ( { const { voter } = useGetVoterInfo(); const { pendingTransaction } = useCardano(); const { state, hash } = useLocation(); const navigate = useNavigate(); - const { isMobile, screenWidth } = useScreenDimension(); + const { isMobile } = useScreenDimension(); const { t } = useTranslation(); const { proposalId } = useParams(); const fullProposalId = proposalId + hash; @@ -45,21 +42,6 @@ export const DashboardGovernanceActionDetails = () => { state ? state.index : data?.proposal.index ?? "", ); - const breadcrumbs = [ - - - {t("govActions.title")} - - , - - {t("govActions.voteOnGovActions")} - , - ]; - return ( { flex={1} > - {breadcrumbs} - + elementOne={t("govActions.title")} + elementOnePath={PATHS.dashboardGovernanceActions} + elementTwo="Fund our project" + isDataMissing={false} + /> { style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - {t("backToList")} + {t("back")} @@ -130,6 +108,8 @@ export const DashboardGovernanceActionDetails = () => { ? formatDisplayDate(state.createdDate) : formatDisplayDate(data.proposal.createdDate) } + // TODO: Add data validation + isDataMissing={isDataMissing} // TODO: To decide if we want to keep it when metadate BE is ready // details={state ? state.details : data.proposal.details} expiryDate={ diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx index cf07ab14e..2ff29ba3e 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx @@ -164,7 +164,7 @@ export const DashboardGovernanceActions = () => { /> (

{ const [isVoteSubmitted, setIsVoteSubmitted] = useState(false); const { screenWidth, isMobile } = useScreenDimension(); - // TODO: Add as a prop when BE is ready - const isDataMissing = false; - const isOneColumn = (isDashboard && screenWidth < 1036) ?? isMobile; return ( @@ -57,9 +56,12 @@ export const GovernanceActionDetailsCard = ({ position: "relative", boxShadow: isInProgress ? "2px 2px 20px 0px rgba(245, 90, 0, 0.20)" - : isVoteSubmitted + : isVoteSubmitted && !isDataMissing ? "2px 2px 20px 0px rgba(98, 188, 82, 0.20)" : "2px 2px 20px 0px rgba(47, 98, 220, 0.20)", + ...(isDataMissing && { + border: "1px solid #F6D5D5", + }), }} data-testid="governance-action-details-card" > diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx index 25240ecf4..9a6641d41 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx @@ -10,6 +10,7 @@ import { GovernanceActionDetailsCardOnChainData, } from "@molecules"; import { useScreenDimension, useTranslation } from "@hooks"; +import { getProposalTypeNoEmptySpaces } from "@utils"; const mockedLongDescription = "I am the Cardano crusader carving his path in the blockchain battleground. With a mind sharper than a Ledger Nano X, this fearless crypto connoisseur fearlessly navigates the volatile seas of Cardano, turning code into currency. Armed with a keyboard and a heart pumping with blockchain beats, Mister Big Bad fearlessly champions decentralization, smart contracts, and the Cardano community. His Twitter feed is a mix of market analysis that rivals CNBC and memes that could break the internet."; @@ -57,7 +58,10 @@ export const GovernanceActionDetailsCardData = ({ }} > {isDataMissing && } @@ -65,6 +69,7 @@ export const GovernanceActionDetailsCardData = ({ label={t("govActions.governanceActionType")} text={type} textVariant="pill" + dataTestId={`${getProposalTypeNoEmptySpaces(type)}-type`} /> + {/* TODO: To add option display of on-chain data when BE is ready */} diff --git a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx index e8862e026..b9ce93be9 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionsToVote.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-unsafe-optional-chaining */ import { useNavigate, generatePath } from "react-router-dom"; import { Box } from "@mui/material"; @@ -10,13 +9,6 @@ import { GovernanceActionCard } from "@molecules"; import { getProposalTypeLabel, getFullGovActionId, openInNewTab } from "@utils"; import { Slider } from "@organisms"; -type GroupedActions = { - [key: string]: { - title: string; - actions: ActionType[]; - }; -}; - type GovernanceActionsToVoteProps = { filters: string[]; sorting: string; @@ -61,11 +53,11 @@ export const GovernanceActionsToVote = ({ {...action} txHash={action.txHash} index={action.index} - // inProgress={ - // onDashboard && - // pendingTransaction.vote?.resourceId === - // action?.txHash + action?.index - // } + inProgress={ + onDashboard && + pendingTransaction.vote?.resourceId === + `${action.txHash ?? ""}${action.index ?? ""}` + } // TODO: Add data validation isDataMissing={false} onClick={() => { diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index 545bb8c47..1394a70b5 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useMemo, useState } from "react"; import { generatePath, useNavigate } from "react-router-dom"; import { Box } from "@mui/material"; import { KeenSliderOptions } from "keen-slider"; @@ -11,8 +11,6 @@ import { SliderArrows } from "@molecules"; import { PATHS } from "@consts"; import { theme } from "@/theme"; -const SLIDER_MAX_LENGTH = 1000; - type SliderProps = { title: string; navigateKey: string; @@ -38,6 +36,8 @@ export const Slider = ({ searchPhrase, sorting, }: SliderProps) => { + const [isSliderInitialized, setIsSliderInitialized] = useState(false); + const { isMobile, screenWidth } = useScreenDimension(); const navigate = useNavigate(); const { pendingTransaction } = useCardano(); @@ -56,12 +56,15 @@ export const Slider = ({ }, } as KeenSliderOptions; - const isShowArrows = - screenWidth < 268 + 28 + dataLength * 350 + 20 * dataLength - 5; + const isShowArrows = useMemo( + () => + screenWidth < + (onDashboard ? 268 : 40) + 28 + dataLength * 350 + 20 * dataLength - 5, + [screenWidth, dataLength], + ); - const { sliderRef, instanceRef, currentSlide } = useSlider({ + const { sliderRef, instanceRef, currentSlide, itemsPerView } = useSlider({ config: DEFAULT_SLIDER_CONFIG, - sliderMaxLength: SLIDER_MAX_LENGTH, }); const refresh = () => { @@ -70,9 +73,21 @@ export const Slider = ({ instanceRef.current?.moveToIdx(0); }; + useEffect(() => { + if (instanceRef.current) { + setIsSliderInitialized(true); + } + }, [instanceRef.current]); + useEffect(() => { refresh(); - }, [filters, sorting, searchPhrase, pendingTransaction.vote?.resourceId, data]); + }, [ + filters, + sorting, + searchPhrase, + pendingTransaction.vote?.resourceId, + data, + ]); return ( @@ -93,15 +108,7 @@ export const Slider = ({ gap: 2, }} > - - {title} - + {title} {(notSlicedDataLength > 6 || (isMobile && isShowAll)) && ( )} - {isShowArrows && dataLength > 1 && !isMobile && ( - + {isSliderInitialized && isShowArrows && dataLength > 1 && !isMobile && ( + )}
{ undefined, ); const [address, setAddress] = useState(undefined); - const [pubDRepKey, setPubDRepKey] = useState(''); - const [dRepID, setDRepID] = useState(''); - const [dRepIDBech32, setDRepIDBech32] = useState(''); + const [pubDRepKey, setPubDRepKey] = useState(""); + const [dRepID, setDRepID] = useState(""); + const [dRepIDBech32, setDRepIDBech32] = useState(""); const [stakeKey, setStakeKey] = useState(undefined); const [stakeKeys, setStakeKeys] = useState([]); const [isMainnet, setIsMainnet] = useState(false); @@ -204,7 +204,7 @@ const CardanoProvider = (props: Props) => { try { const raw = await enabledApi.getChangeAddress(); const changeAddress = Address.from_bytes( - Buffer.from(raw, 'hex'), + Buffer.from(raw, "hex"), ).to_bech32(); setWalletState((prev) => ({ ...prev, changeAddress })); } catch (err) { @@ -218,7 +218,7 @@ const CardanoProvider = (props: Props) => { const raw = await enabledApi.getUsedAddresses(); const rawFirst = raw[0]; const usedAddress = Address.from_bytes( - Buffer.from(rawFirst, 'hex'), + Buffer.from(rawFirst, "hex"), ).to_bech32(); setWalletState((prev) => ({ ...prev, usedAddress })); } catch (err) { @@ -237,13 +237,13 @@ const CardanoProvider = (props: Props) => { try { // Check that this wallet supports CIP-95 connection if (!window.cardano[walletName].supportedExtensions) { - throw new Error(t('errors.walletNoCIP30Support')); + throw new Error(t("errors.walletNoCIP30Support")); } else if ( !window.cardano[walletName].supportedExtensions.some( (item) => item.cip === 95, ) ) { - throw new Error(t('errors.walletNoCIP30Nor90Support')); + throw new Error(t("errors.walletNoCIP30Nor90Support")); } // Enable wallet connection const enabledApi: CardanoApiWallet = await window.cardano[walletName] @@ -261,7 +261,7 @@ const CardanoProvider = (props: Props) => { // Check if wallet has enabled the CIP-95 extension const enabledExtensions = await enabledApi.getExtensions(); if (!enabledExtensions.some((item) => item.cip === 95)) { - throw new Error(t('errors.walletNoCIP90FunctionsEnabled')); + throw new Error(t("errors.walletNoCIP90FunctionsEnabled")); } const network = await enabledApi.getNetworkId(); if (network !== NETWORK) { @@ -277,7 +277,7 @@ const CardanoProvider = (props: Props) => { const usedAddresses = await enabledApi.getUsedAddresses(); const unusedAddresses = await enabledApi.getUnusedAddresses(); if (!usedAddresses.length && !unusedAddresses.length) { - throw new Error(t('errors.noAddressesFound')); + throw new Error(t("errors.noAddressesFound")); } if (!usedAddresses.length) { setAddress(unusedAddresses[0]); @@ -341,22 +341,22 @@ const CardanoProvider = (props: Props) => { stakeKeySet = true; } const dRepIDs = await getPubDRepID(enabledApi); - setPubDRepKey(dRepIDs?.dRepKey || ''); - setDRepID(dRepIDs?.dRepID || ''); - setDRepIDBech32(dRepIDs?.dRepIDBech32 || ''); + setPubDRepKey(dRepIDs?.dRepKey || ""); + setDRepID(dRepIDs?.dRepID || ""); + setDRepIDBech32(dRepIDs?.dRepIDBech32 || ""); setItemToLocalStorage(`${WALLET_LS_KEY}_name`, walletName); const protocol = await getEpochParams(); setItemToLocalStorage(PROTOCOL_PARAMS_KEY, protocol); - return { status: t('ok'), stakeKey: stakeKeySet }; + return { status: t("ok"), stakeKey: stakeKeySet }; } catch (e) { Sentry.captureException(e); console.error(e); setError(`${e}`); setAddress(undefined); setWalletApi(undefined); - setPubDRepKey(''); + setPubDRepKey(""); setStakeKey(undefined); setIsEnabled(false); // eslint-disable-next-line no-throw-literal @@ -439,7 +439,7 @@ const CardanoProvider = (props: Props) => { const txBuilder = await initTransactionBuilder(); if (!txBuilder) { - throw new Error(t('errors.appCannotCreateTransaction')); + throw new Error(t("errors.appCannotCreateTransaction")); } if (certBuilder) { @@ -468,10 +468,10 @@ const CardanoProvider = (props: Props) => { ); // Add output of 1 ADA to the address of our wallet - let outputValue = BigNum.from_str('1000000'); + let outputValue = BigNum.from_str("1000000"); if ( - (type === 'retireAsDrep' || type === 'retireAsSoleVoter') && + (type === "retireAsDrep" || type === "retireAsSoleVoter") && voterDeposit ) { outputValue = outputValue.checked_add(BigNum.from_str(voterDeposit)); @@ -484,7 +484,7 @@ const CardanoProvider = (props: Props) => { const utxos = await getUtxos(walletApi); if (!utxos) { - throw new Error(t('errors.appCannotGetUtxos')); + throw new Error(t("errors.appCannotGetUtxos")); } // Find the available UTXOs in the wallet and use them as Inputs for the transaction const txUnspentOutputs = await getTxUnspentOutputs(utxos); @@ -511,11 +511,11 @@ const CardanoProvider = (props: Props) => { // Create witness set object using the witnesses provided by the wallet txVkeyWitnesses = TransactionWitnessSet.from_bytes( - Buffer.from(txVkeyWitnesses, 'hex'), + Buffer.from(txVkeyWitnesses, "hex"), ); const vkeys = txVkeyWitnesses.vkeys(); - if (!vkeys) throw new Error(t('errors.appCannotGetVkeys')); + if (!vkeys) throw new Error(t("errors.appCannotGetVkeys")); transactionWitnessSet.set_vkeys(vkeys); // Build transaction with witnesses @@ -546,7 +546,7 @@ const CardanoProvider = (props: Props) => { } Sentry.captureException(error); - console.error(error, 'error'); + console.error(error, "error"); throw error?.info ?? error; } }, @@ -560,7 +560,7 @@ const CardanoProvider = (props: Props) => { const certBuilder = CertificatesBuilder.new(); let stakeCred; if (!stakeKey) { - throw new Error(t('errors.noStakeKeySelected')); + throw new Error(t("errors.noStakeKeySelected")); } // Remove network tag from stake key hash const stakeKeyHash = Ed25519KeyHash.from_hex(stakeKey.substring(2)); @@ -575,11 +575,11 @@ const CardanoProvider = (props: Props) => { // Create correct DRep let targetDRep; - if (target === 'abstain') { + if (target === "abstain") { targetDRep = DRep.new_always_abstain(); - } else if (target === 'no confidence') { + } else if (target === "no confidence") { targetDRep = DRep.new_always_no_confidence(); - } else if (target.includes('drep')) { + } else if (target.includes("drep")) { targetDRep = DRep.new_key_hash(Ed25519KeyHash.from_bech32(target)); } else { targetDRep = DRep.new_key_hash(Ed25519KeyHash.from_hex(target)); @@ -725,9 +725,9 @@ const CardanoProvider = (props: Props) => { ); let votingChoice; - if (voteChoice === 'yes') { + if (voteChoice === "yes") { votingChoice = 1; - } else if (voteChoice === 'no') { + } else if (voteChoice === "no") { votingChoice = 0; } else { votingChoice = 2; @@ -761,11 +761,11 @@ const CardanoProvider = (props: Props) => { const getRewardAddress = useCallback(async () => { const addresses = await walletApi?.getRewardAddresses(); if (!addresses) { - throw new Error('Can not get reward addresses from wallet.'); + throw new Error("Can not get reward addresses from wallet."); } const firstAddress = addresses[0]; const bech32Address = Address.from_bytes( - Buffer.from(firstAddress, 'hex'), + Buffer.from(firstAddress, "hex"), ).to_bech32(); return RewardAddress.from_address(Address.from_bech32(bech32Address)); @@ -783,7 +783,7 @@ const CardanoProvider = (props: Props) => { const anchor = generateAnchor(url, hash); const rewardAddr = await getRewardAddress(); - if (!rewardAddr) throw new Error('Can not get reward address'); + if (!rewardAddr) throw new Error("Can not get reward address"); // Create voting proposal const votingProposal = VotingProposal.new( @@ -811,7 +811,7 @@ const CardanoProvider = (props: Props) => { Address.from_bech32(receivingAddress), ); - if (!treasuryTarget) throw new Error('Can not get tresasury target'); + if (!treasuryTarget) throw new Error("Can not get tresasury target"); const myWithdrawal = BigNum.from_str(amount); const withdrawals = TreasuryWithdrawals.new(); @@ -825,7 +825,7 @@ const CardanoProvider = (props: Props) => { const rewardAddr = await getRewardAddress(); - if (!rewardAddr) throw new Error('Can not get reward address'); + if (!rewardAddr) throw new Error("Can not get reward address"); // Create voting proposal const votingProposal = VotingProposal.new( treasuryGovAct, @@ -909,7 +909,7 @@ function useCardano() { const { t } = useTranslation(); if (context === undefined) { - throw new Error(t('errors.useCardano')); + throw new Error(t("errors.useCardano")); } const enable = useCallback( @@ -922,22 +922,22 @@ function useCardano() { if (!result.error) { closeModal(); if (result.stakeKey) { - addSuccessAlert(t('alerts.walletConnected'), 3000); + addSuccessAlert(t("alerts.walletConnected"), 3000); } if (!isSanchoInfoShown) { openModal({ - type: 'statusModal', + type: "statusModal", state: { - status: 'info', - dataTestId: 'info-about-sancho-net-modal', + status: "info", + dataTestId: "info-about-sancho-net-modal", message: (

- {t('system.sanchoNetIsBeta')} + {t("system.sanchoNetIsBeta")} openInNewTab('https://sancho.network/')} - sx={{ cursor: 'pointer' }} + onClick={() => openInNewTab("https://sancho.network/")} + sx={{ cursor: "pointer" }} > - {t('system.sanchoNet')} + {t("system.sanchoNet")} .
@@ -950,8 +950,8 @@ function useCardano() { />

), - title: t('system.toolConnectedToSanchonet'), - buttonText: t('ok'), + title: t("system.toolConnectedToSanchonet"), + buttonText: t("ok"), }, }); setItemToLocalStorage(`${SANCHO_INFO_KEY}_${walletName}`, true); @@ -965,15 +965,15 @@ function useCardano() { await context.disconnectWallet(); navigate(PATHS.home); openModal({ - type: 'statusModal', + type: "statusModal", state: { - status: 'warning', - message: e?.error?.replace('Error: ', ''), + status: "warning", + message: e?.error?.replace("Error: ", ""), onSubmit: () => { closeModal(); }, - title: t('modals.common.oops'), - dataTestId: 'wallet-connection-error-modal', + title: t("modals.common.oops"), + dataTestId: "wallet-connection-error-modal", }, }); throw e; diff --git a/govtool/frontend/src/hooks/useSlider.ts b/govtool/frontend/src/hooks/useSlider.ts index e73341422..b2f64adf5 100644 --- a/govtool/frontend/src/hooks/useSlider.ts +++ b/govtool/frontend/src/hooks/useSlider.ts @@ -47,12 +47,7 @@ const WheelControls = (slider: KeenSliderInstance) => { }); }; -export const useSlider = ({ - config, -}: { - config: KeenSliderOptions; - sliderMaxLength: number; -}) => { +export const useSlider = ({ config }: { config: KeenSliderOptions }) => { const [currentSlide, setCurrentSlide] = useState(0); const [sliderRef, instanceRef] = useKeenSlider( @@ -66,9 +61,14 @@ export const useSlider = ({ [WheelControls], ); + const dataLength = instanceRef?.current?.slides?.length ?? 10; + const itemsPerView = + dataLength - (instanceRef?.current?.track?.details?.maxIdx ?? 2); + return { sliderRef, instanceRef, currentSlide, + itemsPerView, }; }; diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 28a2756db..19e49e5f0 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -316,6 +316,7 @@ export const en = { govActions: { about: "About", abstract: "Abstract:", + backToGovActions: "Back to Governance Actions", castVote: "<0>You voted {{vote}} for this proposal\nat {{date}}", castVoteDeadline: "You can change your vote up to the deadline of {{date}}", @@ -323,6 +324,8 @@ export const en = { changeYourVote: "Change your vote", chooseHowToVote: "Choose how you want to vote:", dataMissing: "Data Missing", + dataMissingTooltipExplanation: + "Please click “View Details” for more information.", details: "Governance Details:", expiresDateWithEpoch: "Expires: <0>{{date}} <1>(Epoch {{epoch}})", expiryDate: "Expiry date:", @@ -350,7 +353,7 @@ export const en = { viewOtherDetails: "View other details", viewProposalDetails: "View proposal details", vote: "Vote", - voted: "Voted", + votedOnByMe: "Voted on by me", voteOnGovActions: "Vote on Governance Action", voteSubmitted: "Vote submitted", voteTransaction: "Vote transaction", @@ -601,9 +604,11 @@ export const en = { backToList: "Back to the list", cancel: "Cancel", clear: "Clear", + clickToCopyLink: "Click to copy link", confirm: "Confirm", continue: "Continue", delegate: "Delegate", + filter: "Filter", here: "here", inProgress: "In progress", learnMore: "Learn more", @@ -617,8 +622,10 @@ export const en = { required: "required", seeTransaction: "See transaction", select: "Select", + share: "Share", showMore: "Show more", skip: "Skip", + sort: "Sort", sortBy: "Sort by", submit: "Submit", thisLink: "this link", diff --git a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx index 16187ab72..47384da36 100644 --- a/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/DashboardGovernanceActionsCategory.tsx @@ -1,19 +1,8 @@ import { useCallback, useMemo, useRef, useState } from "react"; -import { - generatePath, - NavLink, - useNavigate, - useParams, -} from "react-router-dom"; -import { - Box, - Breadcrumbs, - CircularProgress, - Link, - Typography, -} from "@mui/material"; +import { generatePath, useNavigate, useParams } from "react-router-dom"; +import { Box, CircularProgress, Link } from "@mui/material"; -import { Background } from "@atoms"; +import { Background, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; import { useCardano } from "@context"; import { DataActionsBar, GovernanceActionCard } from "@molecules"; @@ -68,21 +57,6 @@ export const DashboardGovernanceActionsCategory = () => { isProposalsFetching, ); - const breadcrumbs = [ - - - {t("govActions.title")} - - , - - {getProposalTypeLabel(category ?? "")} - , - ]; - const mappedData = useMemo(() => { const uniqueProposals = removeDuplicatedProposals(proposals); @@ -112,23 +86,13 @@ export const DashboardGovernanceActionsCategory = () => { > - - {breadcrumbs} - navigate(PATHS.dashboardGovernanceActions)} > @@ -137,8 +101,8 @@ export const DashboardGovernanceActionsCategory = () => { alt="arrow" style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - - {t("backToList")} + + {t("govActions.backToGovActions")} { sortingActive={Boolean(chosenSorting)} sortOpen={sortOpen} /> - + + {getProposalTypeLabel(category ?? "")} + {!mappedData || isEnableLoading || isProposalsLoading ? ( ) : !mappedData?.length ? ( - + {t("govActions.withCategoryNotExist.partOne")}   - {` ${category} `} + + {` ${category} `} +   {t("govActions.withCategoryNotExist.partTwo")} @@ -173,14 +155,10 @@ export const DashboardGovernanceActionsCategory = () => { ) : ( {mappedData.map((item) => ( @@ -188,38 +166,41 @@ export const DashboardGovernanceActionsCategory = () => { { saveScrollPosition(); - // eslint-disable-next-line no-unused-expressions - pendingTransaction.vote?.resourceId === - item.txHash + item.index - ? openInNewTab( - "https://adanordic.com/latest_transactions", - ) - : navigate( - generatePath( - PATHS.dashboardGovernanceActionsAction, - { - proposalId: getFullGovActionId( - item.txHash, - item.index, - ), - }, - ), + if ( + pendingTransaction.vote?.resourceId === + item.txHash + item.index + ) { + openInNewTab( + "https://adanordic.com/latest_transactions", + ); + } else { + navigate( + generatePath( + PATHS.dashboardGovernanceActionsAction, { - state: { - ...item, - openedFromCategoryPage: true, - }, + proposalId: getFullGovActionId( + item.txHash, + item.index, + ), + }, + ), + { + state: { + ...item, + openedFromCategoryPage: true, }, - ); + }, + ); + } }} txHash={item.txHash} /> diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index fe916ca4d..8fc1055bc 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -3,10 +3,9 @@ import { useNavigate, useLocation, useParams, - NavLink, generatePath, } from "react-router-dom"; -import { Box, Breadcrumbs, CircularProgress, Link } from "@mui/material"; +import { Box, CircularProgress, Link } from "@mui/material"; import { Background, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; @@ -24,11 +23,15 @@ import { getItemFromLocalStorage, getShortenedGovActionId, } from "@utils"; +import { Breadcrumbs } from "@molecules"; + +// TODO: Remove when data validation is ready +const isDataMissing = false; export const GovernanceActionDetails = () => { const { state, hash } = useLocation(); const navigate = useNavigate(); - const { pagePadding, screenWidth } = useScreenDimension(); + const { pagePadding, isMobile } = useScreenDimension(); const { isEnabled } = useCardano(); const { t } = useTranslation(); const { proposalId } = useParams(); @@ -48,21 +51,6 @@ export const GovernanceActionDetails = () => { } }, [isEnabled]); - const breadcrumbs = [ - - - {t("govActions.title")} - - , - - {t("govActions.voteOnGovActions")} - , - ]; - return ( { px={pagePadding} > - {screenWidth >= 1024 ? ( - - {t("govActions.title")} - + {isMobile ? ( + + + {t("govActions.title")} + + ) : null} - {breadcrumbs} - + elementOne={t("govActions.title")} + elementOnePath={PATHS.dashboardGovernanceActions} + elementTwo="Fund our project" + isDataMissing={false} + /> { style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - {t("backToList")} + {t("back")} {isLoading ? ( @@ -140,6 +134,8 @@ export const GovernanceActionDetails = () => { ? formatDisplayDate(state.createdDate) : formatDisplayDate(data.proposal.createdDate) } + // TODO: Add data validation + isDataMissing={isDataMissing} // TODO: To decide if we want to keep it when metadate BE is ready // details={state ? state.details : data.proposal.details} expiryDate={ diff --git a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx index 4538cb006..82b67ce46 100644 --- a/govtool/frontend/src/pages/GovernanceActionsCategory.tsx +++ b/govtool/frontend/src/pages/GovernanceActionsCategory.tsx @@ -1,12 +1,6 @@ import { useState, useCallback, useEffect, useMemo, useRef } from "react"; -import { NavLink, useNavigate, useParams } from "react-router-dom"; -import { - Box, - Breadcrumbs, - CircularProgress, - Divider, - Link, -} from "@mui/material"; +import { useNavigate, useParams } from "react-router-dom"; +import { Box, CircularProgress, Link } from "@mui/material"; import { Background, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; @@ -65,21 +59,6 @@ export const GovernanceActionsCategory = () => { isProposalsFetching, ); - const breadcrumbs = [ - - - {t("govActions.title")} - - , - - {getProposalTypeLabel(category ?? "")} - , - ]; - const mappedData = useMemo(() => { const uniqueProposals = removeDuplicatedProposals(proposals); @@ -116,32 +95,7 @@ export const GovernanceActionsCategory = () => { > - - {t("govActions.title")} - - {isMobile && ( - - )} - - {breadcrumbs} - { alt="arrow" style={{ marginRight: "12px", transform: "rotate(180deg)" }} /> - - {t("backToList")} + + {t("govActions.backToGovActions")} { closeSorts={closeSorts} setChosenSorting={setChosenSorting} /> - + + {getProposalTypeLabel(category ?? "")} + {!isProposalsLoading ? ( !mappedData?.length ? ( @@ -202,14 +163,10 @@ export const GovernanceActionsCategory = () => { ) : ( {mappedData.map((item) => ( diff --git a/govtool/frontend/src/utils/getProposalTypeLabel.ts b/govtool/frontend/src/utils/getProposalTypeLabel.ts index e44f7905c..0064dd602 100644 --- a/govtool/frontend/src/utils/getProposalTypeLabel.ts +++ b/govtool/frontend/src/utils/getProposalTypeLabel.ts @@ -4,3 +4,5 @@ export const getProposalTypeLabel = (type: string) => { const label = GOVERNANCE_ACTIONS_FILTERS.find((i) => i.key === type)?.label; return label || type; }; +export const getProposalTypeNoEmptySpaces = (type: string) => + getProposalTypeLabel(type).replace(/ /g, ""); From de9dffb92cab72533e72fdc3500eb0f1ffe313a6 Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Mon, 25 Mar 2024 16:54:09 +0100 Subject: [PATCH 4/5] Fixes after CR --- govtool/frontend/public/icons/Separator.svg | 3 - .../src/components/atoms/CopyButton.tsx | 4 +- .../src/components/atoms/IconLink.tsx | 45 ------ .../frontend/src/components/atoms/Radio.tsx | 4 +- .../frontend/src/components/atoms/Tooltip.tsx | 7 +- .../frontend/src/components/atoms/index.ts | 2 - .../frontend/src/components/atoms/types.ts | 7 + .../src/components/molecules/Breadcrumbs.tsx | 9 +- .../molecules/GovernanceActionCard.tsx | 27 ++-- .../molecules/GovernanceActionCardElement.tsx | 140 +++++++++--------- .../molecules/GovernanceActionCardMyVote.tsx | 6 +- .../GovernanceActionDetailsCardLinks.tsx | 13 +- .../molecules/GovernanceActionsFilters.tsx | 5 +- .../molecules/GovernanceActionsSorting.tsx | 5 +- .../molecules/GovernanceVotedOnCard.tsx | 4 +- .../src/components/molecules/LinkWithIcon.tsx | 14 +- .../components/molecules/OrderActionsChip.tsx | 89 +++++++---- .../{atoms => molecules}/SliderArrow.tsx | 4 +- .../src/components/molecules/SliderArrows.tsx | 59 ++++---- .../components/molecules/VotesSubmitted.tsx | 4 +- .../src/components/molecules/index.ts | 1 + .../src/components/molecules/types.ts | 1 + .../DashboardGovernanceActionDetails.tsx | 2 - .../organisms/DashboardGovernanceActions.tsx | 4 +- .../DashboardGovernanceActionsVotedOn.tsx | 4 +- .../organisms/GovernanceActionDetailsCard.tsx | 2 - .../src/components/organisms/Slider.tsx | 4 + govtool/frontend/src/consts/icons.ts | 1 - .../src/pages/GovernanceActionDetails.tsx | 2 - govtool/frontend/src/theme.ts | 51 ------- govtool/frontend/src/types/@mui.d.ts | 2 + .../src/utils/getProposalTypeLabel.ts | 1 + .../getProposalTypeNoEmptySpaces.test.ts | 25 ++++ 33 files changed, 269 insertions(+), 282 deletions(-) delete mode 100644 govtool/frontend/public/icons/Separator.svg delete mode 100644 govtool/frontend/src/components/atoms/IconLink.tsx rename govtool/frontend/src/components/{atoms => molecules}/SliderArrow.tsx (97%) create mode 100644 govtool/frontend/src/utils/tests/getProposalTypeNoEmptySpaces.test.ts diff --git a/govtool/frontend/public/icons/Separator.svg b/govtool/frontend/public/icons/Separator.svg deleted file mode 100644 index e6edeb0dd..000000000 --- a/govtool/frontend/public/icons/Separator.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/govtool/frontend/src/components/atoms/CopyButton.tsx b/govtool/frontend/src/components/atoms/CopyButton.tsx index 2783d0fb7..021d5fcc0 100644 --- a/govtool/frontend/src/components/atoms/CopyButton.tsx +++ b/govtool/frontend/src/components/atoms/CopyButton.tsx @@ -4,11 +4,11 @@ import { ICONS } from "@consts"; import { useSnackbar } from "@context"; import { useTranslation } from "@hooks"; -interface Props { +type Props = { isChecked?: boolean; text: string; variant?: "blueThin" | "blue"; -} +}; export const CopyButton = ({ isChecked, text, variant }: Props) => { const { addSuccessAlert } = useSnackbar(); diff --git a/govtool/frontend/src/components/atoms/IconLink.tsx b/govtool/frontend/src/components/atoms/IconLink.tsx deleted file mode 100644 index 0c12fcd1e..000000000 --- a/govtool/frontend/src/components/atoms/IconLink.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Box } from "@mui/material"; - -import { Typography } from "@atoms"; -import { openInNewTab } from "@utils"; -import { ICONS } from "@consts"; - -type IconLinkProps = { - label: string; - navTo: string; - isSmall?: boolean; -}; - -export const IconLink = ({ label, navTo, isSmall }: IconLinkProps) => { - const openLink = () => openInNewTab(navTo); - - return ( - - link - - {label} - - - ); -}; diff --git a/govtool/frontend/src/components/atoms/Radio.tsx b/govtool/frontend/src/components/atoms/Radio.tsx index f93e68ec3..d997c1b58 100644 --- a/govtool/frontend/src/components/atoms/Radio.tsx +++ b/govtool/frontend/src/components/atoms/Radio.tsx @@ -3,6 +3,7 @@ import { Box } from "@mui/material"; import { Typography } from "@atoms"; import { UseFormRegister, UseFormSetValue } from "react-hook-form"; +import { theme } from "@/theme"; type RadioProps = { isChecked: boolean; @@ -43,8 +44,7 @@ export const Radio = ({ ...props }: RadioProps) => { borderColor={isChecked ? "specialCyanBorder" : undefined} sx={[ { - boxShadow: - "0px 1px 2px 0px rgba(0, 51, 173, 0.08), 0px 1px 6px 1px rgba(0, 51, 173, 0.15)", + boxShadow: theme.shadows[1], "&:hover": { color: "blue", diff --git a/govtool/frontend/src/components/atoms/Tooltip.tsx b/govtool/frontend/src/components/atoms/Tooltip.tsx index 4e2ccdc51..dd8d8dc23 100644 --- a/govtool/frontend/src/components/atoms/Tooltip.tsx +++ b/govtool/frontend/src/components/atoms/Tooltip.tsx @@ -1,12 +1,7 @@ import { styled } from "@mui/material"; import * as TooltipMUI from "@mui/material/Tooltip"; import Typography from "@mui/material/Typography"; - -export type TooltipProps = Omit & { - heading?: string; - paragraphOne?: string; - paragraphTwo?: string; -}; +import { TooltipProps } from "@atoms"; const StyledTooltip = styled( ({ className, ...props }: TooltipMUI.TooltipProps) => ( diff --git a/govtool/frontend/src/components/atoms/index.ts b/govtool/frontend/src/components/atoms/index.ts index 1939a0971..6fa848bac 100644 --- a/govtool/frontend/src/components/atoms/index.ts +++ b/govtool/frontend/src/components/atoms/index.ts @@ -9,7 +9,6 @@ export * from "./ExternalModalButton"; export * from "./FormErrorMessage"; export * from "./FormHelpfulText"; export * from "./HighlightedText"; -export * from "./IconLink"; export * from "./InfoText"; export * from "./Input"; export * from "./Link"; @@ -21,7 +20,6 @@ export * from "./modal/ModalWrapper"; export * from "./Radio"; export * from "./ScrollToManage"; export * from "./ScrollToTop"; -export * from "./SliderArrow"; export * from "./Spacer"; export * from "./StakeRadio"; export * from "./TextArea"; diff --git a/govtool/frontend/src/components/atoms/types.ts b/govtool/frontend/src/components/atoms/types.ts index 34f0fdf14..ff2e603b8 100644 --- a/govtool/frontend/src/components/atoms/types.ts +++ b/govtool/frontend/src/components/atoms/types.ts @@ -7,6 +7,7 @@ import { TextareaAutosizeProps, SxProps, } from "@mui/material"; +import * as TooltipMUI from "@mui/material/Tooltip"; export type ButtonProps = Omit & { size?: "small" | "medium" | "large" | "extraLarge"; @@ -71,3 +72,9 @@ export type InfoTextProps = { label: string; sx?: SxProps; }; + +export type TooltipProps = Omit & { + heading?: string; + paragraphOne?: string; + paragraphTwo?: string; +}; diff --git a/govtool/frontend/src/components/molecules/Breadcrumbs.tsx b/govtool/frontend/src/components/molecules/Breadcrumbs.tsx index 435d56e3d..8a9e564a2 100644 --- a/govtool/frontend/src/components/molecules/Breadcrumbs.tsx +++ b/govtool/frontend/src/components/molecules/Breadcrumbs.tsx @@ -1,7 +1,7 @@ import { NavLink, To } from "react-router-dom"; import { Box } from "@mui/material"; +import Divider from "@mui/material/Divider"; -import { ICONS } from "@consts"; import { useScreenDimension, useTranslation } from "@hooks"; import { Typography } from "@atoms"; @@ -40,7 +40,12 @@ export const Breadcrumbs = ({ {elementOne} - separator + { +type ActionTypeProps = Omit< + ActionType, + | "yesVotes" + | "noVotes" + | "abstainVotes" + | "metadataHash" + | "url" + | "details" + | "id" + | "txHash" + | "index" +> & { txHash: string; index: number; isDataMissing: boolean; onClick?: () => void; inProgress?: boolean; -} +}; export const GovernanceActionCard: FC = ({ ...props }) => { const { diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx index 51186d699..94517a9ca 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardElement.tsx @@ -40,99 +40,97 @@ export const GovernanceActionCardElement = ({ > {label} {tooltipProps && ( - - - - )} + + + + )} {textVariant === "pill" ? ( {text} - ) : ( - + - - {text} - - {isCopyButton && ( - - - - )} - - )} + {text} + + {isCopyButton && ( + + + + )} + + )} - ); +); diff --git a/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx b/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx index 0a53fbfe6..c1dde83ec 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionCardMyVote.tsx @@ -5,7 +5,11 @@ import { openInNewTab } from "@utils"; import { useTranslation } from "@hooks"; import { Vote } from "@models"; -export const GovernanceActionCardMyVote = ({ vote }: { vote: Vote }) => { +type Props = { + vote: Vote; +}; + +export const GovernanceActionCardMyVote = ({ vote }: Props) => { const { t } = useTranslation(); return ( diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx index 8fdb99397..8028668be 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardLinks.tsx @@ -1,7 +1,10 @@ import { Box } from "@mui/material"; -import { IconLink, Typography } from "@atoms"; +import { Typography } from "@atoms"; import { useScreenDimension, useTranslation } from "@hooks"; +import { LinkWithIcon } from "@molecules"; +import { openInNewTab } from "@/utils"; +import { ICONS } from "@/consts"; // TODO: When BE is ready, pass links as props const LINKS = [ @@ -41,7 +44,13 @@ export const GovernanceActionDetailsCardLinks = () => { }} > {LINKS.map((link) => ( - + openInNewTab(link)} + icon={link} + cutWithEllipsis + /> ))} diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx index 05746f111..89542217d 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx @@ -8,7 +8,7 @@ import { } from "@mui/material"; import { GOVERNANCE_ACTIONS_FILTERS } from "@consts"; -import { useTranslation } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; interface Props { chosenFilters: string[]; @@ -36,6 +36,7 @@ export const GovernanceActionsFilters = ({ ); const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); return ( { const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); return ( diff --git a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx index 7e9f140be..7e5a6aa2b 100644 --- a/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceVotedOnCard.tsx @@ -22,11 +22,11 @@ import { const mockedLongText = "Lorem ipsum dolor sit, amet consectetur adipisicing elit. Sit, distinctio culpa minus eaque illo quidem voluptates quisquam mollitia consequuntur ex, sequi saepe? Ad ex adipisci molestiae sed."; -interface Props { +type Props = { votedProposal: VotedProposal; isDataMissing: boolean; inProgress?: boolean; -} +}; export const GovernanceVotedOnCard = ({ votedProposal, diff --git a/govtool/frontend/src/components/molecules/LinkWithIcon.tsx b/govtool/frontend/src/components/molecules/LinkWithIcon.tsx index ab12e106c..ecfa97b16 100644 --- a/govtool/frontend/src/components/molecules/LinkWithIcon.tsx +++ b/govtool/frontend/src/components/molecules/LinkWithIcon.tsx @@ -10,6 +10,7 @@ export const LinkWithIcon = ({ onClick, icon, sx, + cutWithEllipsis, }: LinkWithIconProps) => ( {label} diff --git a/govtool/frontend/src/components/molecules/OrderActionsChip.tsx b/govtool/frontend/src/components/molecules/OrderActionsChip.tsx index 3bddbd6dd..037a68cd4 100644 --- a/govtool/frontend/src/components/molecules/OrderActionsChip.tsx +++ b/govtool/frontend/src/components/molecules/OrderActionsChip.tsx @@ -1,12 +1,12 @@ import { Dispatch, SetStateAction } from "react"; import { Box } from "@mui/material"; -import { useTranslation } from "@hooks"; +import { useScreenDimension, useTranslation } from "@hooks"; import { Typography } from "@atoms"; import { ICONS } from "@consts"; import { theme } from "@/theme"; -interface Props { +type Props = { filtersOpen?: boolean; setFiltersOpen?: Dispatch>; chosenFiltersLength?: number; @@ -14,10 +14,11 @@ interface Props { setSortOpen: Dispatch>; sortingActive: boolean; isFiltering?: boolean; -} +}; export const OrderActionsChip = (props: Props) => { const { t } = useTranslation(); + const { isMobile } = useScreenDimension(); const { palette: { secondary }, @@ -34,7 +35,13 @@ export const OrderActionsChip = (props: Props) => { } = props; return ( - + {isFiltering && ( { alignItems: "center", justifyContent: "center", cursor: "pointer", + ...(!isMobile && { + background: filtersOpen ? secondary.main : "transparent", + borderRadius: "99px", + padding: "12px 14px", + }), }} onClick={() => { setSortOpen(false); @@ -56,24 +68,30 @@ export const OrderActionsChip = (props: Props) => { alt="filter" src={filtersOpen ? ICONS.filterWhiteIcon : ICONS.filterIcon} style={{ - background: filtersOpen ? secondary.main : "transparent", borderRadius: "100%", - padding: "14px", + marginRight: "8px", overflow: "visible", height: 20, width: 20, objectFit: "contain", + ...(isMobile && { + background: filtersOpen ? secondary.main : "transparent", + padding: "14px", + marginRight: "0", + }), }} /> - - {t("filter")} - + {!isMobile && ( + + {t("filter")} + + )} {!filtersOpen && chosenFiltersLength > 0 && ( { height: "16px", justifyContent: "center", position: "absolute", - left: "32px", + right: "-3px", top: "0", width: "16px", }} @@ -105,6 +123,11 @@ export const OrderActionsChip = (props: Props) => { alignItems: "center", justifyContent: "center", cursor: "pointer", + ...(!isMobile && { + background: sortOpen ? secondary.main : "transparent", + borderRadius: "99px", + padding: "12px 14px", + }), }} onClick={() => { if (isFiltering) { @@ -118,23 +141,29 @@ export const OrderActionsChip = (props: Props) => { alt="sort" src={sortOpen ? ICONS.sortWhiteIcon : ICONS.sortIcon} style={{ - background: sortOpen ? secondary.main : "transparent", borderRadius: "100%", - padding: "14px", - height: 24, - width: 24, + marginRight: "8px", + height: 20, + width: 20, objectFit: "contain", + ...(isMobile && { + background: sortOpen ? secondary.main : "transparent", + padding: "14px", + marginRight: "0", + }), }} /> - - {t("sort")} - + {!isMobile && ( + + {t("sort")} + + )} {!sortOpen && sortingActive && ( { height: "16px", justifyContent: "center", position: "absolute", - left: "36px", + right: "-3px", top: "0", width: "16px", }} diff --git a/govtool/frontend/src/components/atoms/SliderArrow.tsx b/govtool/frontend/src/components/molecules/SliderArrow.tsx similarity index 97% rename from govtool/frontend/src/components/atoms/SliderArrow.tsx rename to govtool/frontend/src/components/molecules/SliderArrow.tsx index d48fcbec8..b4b079d48 100644 --- a/govtool/frontend/src/components/atoms/SliderArrow.tsx +++ b/govtool/frontend/src/components/molecules/SliderArrow.tsx @@ -3,11 +3,11 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import { theme } from "@/theme"; -interface SliderArrowProps { +type SliderArrowProps = { disabled: boolean; onClick: (e: React.MouseEvent) => void; left?: boolean; -} +}; export const SliderArrow = ({ disabled, onClick, left }: SliderArrowProps) => { const { diff --git a/govtool/frontend/src/components/molecules/SliderArrows.tsx b/govtool/frontend/src/components/molecules/SliderArrows.tsx index c481836ae..6d5f9e7a6 100644 --- a/govtool/frontend/src/components/molecules/SliderArrows.tsx +++ b/govtool/frontend/src/components/molecules/SliderArrows.tsx @@ -1,5 +1,5 @@ import { KeenSliderHooks, KeenSliderInstance } from "keen-slider/react"; -import { SliderArrow } from "@atoms"; +import { SliderArrow } from "@molecules"; import { Box } from "@mui/material"; type SliderArrowsProps = { @@ -16,34 +16,31 @@ export const SliderArrows = ({ currentSlide, instanceRef, itemsPerView, -}: SliderArrowsProps) => ( - <> - {instanceRef.current && ( - + instanceRef.current && ( + + ) => { + e.stopPropagation(); + instanceRef.current?.prev(); }} - > - ) => { - e.stopPropagation(); - instanceRef.current?.prev(); - }} - disabled={currentSlide === 0} - /> - ) => { - e.stopPropagation(); - instanceRef.current?.next(); - }} - disabled={ - currentSlide + itemsPerView >= - instanceRef.current.track.details.slides.length - } - /> - - )} - -); + disabled={currentSlide === 0} + /> + ) => { + e.stopPropagation(); + instanceRef.current?.next(); + }} + disabled={ + currentSlide + itemsPerView >= + instanceRef.current.track.details.slides.length + } + /> + + ); diff --git a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx index f24b11c73..1e4ba2347 100644 --- a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx +++ b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx @@ -5,11 +5,11 @@ import { VotePill } from "@atoms"; import { useTranslation } from "@hooks"; import { correctAdaFormat } from "@/utils/adaFormat"; -interface Props { +type Props = { yesVotes: number; noVotes: number; abstainVotes: number; -} +}; export const VotesSubmitted = ({ yesVotes, noVotes, abstainVotes }: Props) => { const { t } = useTranslation(); diff --git a/govtool/frontend/src/components/molecules/index.ts b/govtool/frontend/src/components/molecules/index.ts index 0947666fb..78d4f0563 100644 --- a/govtool/frontend/src/components/molecules/index.ts +++ b/govtool/frontend/src/components/molecules/index.ts @@ -25,6 +25,7 @@ export * from "./GovernanceVotedOnCard"; export * from "./LinkWithIcon"; export * from "./OrderActionsChip"; export * from "./Share"; +export * from "./SliderArrow"; export * from "./SliderArrows"; export * from "./Step"; export * from "./VoteActionForm"; diff --git a/govtool/frontend/src/components/molecules/types.ts b/govtool/frontend/src/components/molecules/types.ts index 0ed7df185..dbe0db33c 100644 --- a/govtool/frontend/src/components/molecules/types.ts +++ b/govtool/frontend/src/components/molecules/types.ts @@ -5,6 +5,7 @@ export type LinkWithIconProps = { onClick: () => void; icon?: JSX.Element; sx?: SxProps; + cutWithEllipsis?: boolean; }; export type StepProps = { diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx index 8db0e5603..480de270c 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionDetails.tsx @@ -110,8 +110,6 @@ export const DashboardGovernanceActionDetails = () => { } // TODO: Add data validation isDataMissing={isDataMissing} - // TODO: To decide if we want to keep it when metadate BE is ready - // details={state ? state.details : data.proposal.details} expiryDate={ state ? formatDisplayDate(state.expiryDate) diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx index 2ff29ba3e..f9b22bc1e 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActions.tsx @@ -16,11 +16,11 @@ import { DashboardGovernanceActionsVotedOn, } from "@organisms"; -interface TabPanelProps { +type TabPanelProps = { children?: React.ReactNode; index: number; value: number; -} +}; const defaultCategories = GOVERNANCE_ACTIONS_FILTERS.map( (category) => category.key, diff --git a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx index 6c46c7f5b..5f8758cd2 100644 --- a/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx +++ b/govtool/frontend/src/components/organisms/DashboardGovernanceActionsVotedOn.tsx @@ -12,11 +12,11 @@ import { getProposalTypeLabel } from "@/utils/getProposalTypeLabel"; import { getFullGovActionId } from "@/utils"; import { useCardano } from "@/context"; -interface DashboardGovernanceActionsVotedOnProps { +type DashboardGovernanceActionsVotedOnProps = { filters: string[]; searchPhrase?: string; sorting: string; -} +}; export const DashboardGovernanceActionsVotedOn = ({ filters, diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx index 4c3cdec67..f6fabab7b 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx @@ -10,7 +10,6 @@ import { useState } from "react"; type GovernanceActionDetailsCardProps = { abstainVotes: number; createdDate: string; - // details: unknown; expiryDate: string; noVotes: number; type: string; @@ -27,7 +26,6 @@ type GovernanceActionDetailsCardProps = { export const GovernanceActionDetailsCard = ({ abstainVotes, createdDate, - // details, expiryDate, noVotes, type, diff --git a/govtool/frontend/src/components/organisms/Slider.tsx b/govtool/frontend/src/components/organisms/Slider.tsx index 1394a70b5..6be504364 100644 --- a/govtool/frontend/src/components/organisms/Slider.tsx +++ b/govtool/frontend/src/components/organisms/Slider.tsx @@ -58,6 +58,10 @@ export const Slider = ({ const isShowArrows = useMemo( () => + // Arrows are to be show only on desktop view. + // 268 - side menu width; 40 - distance needed from the left on + // disconnected wallet (no side menu); 350 - gov action card width; + // other values are for paddings and margins screenWidth < (onDashboard ? 268 : 40) + 28 + dataLength * 350 + 20 * dataLength - 5, [screenWidth, dataLength], diff --git a/govtool/frontend/src/consts/icons.ts b/govtool/frontend/src/consts/icons.ts index d1d237ced..dfabf260d 100644 --- a/govtool/frontend/src/consts/icons.ts +++ b/govtool/frontend/src/consts/icons.ts @@ -26,7 +26,6 @@ export const ICONS = { guidesIcon: "/icons/Guides.svg", helpIcon: "/icons/Help.svg", link: "/icons/Link.svg", - separator: "/icons/Separator.svg", share: "/icons/Share.svg", sortActiveIcon: "/icons/SortActive.svg", sortIcon: "/icons/Sort.svg", diff --git a/govtool/frontend/src/pages/GovernanceActionDetails.tsx b/govtool/frontend/src/pages/GovernanceActionDetails.tsx index 8fc1055bc..c914ce978 100644 --- a/govtool/frontend/src/pages/GovernanceActionDetails.tsx +++ b/govtool/frontend/src/pages/GovernanceActionDetails.tsx @@ -136,8 +136,6 @@ export const GovernanceActionDetails = () => { } // TODO: Add data validation isDataMissing={isDataMissing} - // TODO: To decide if we want to keep it when metadate BE is ready - // details={state ? state.details : data.proposal.details} expiryDate={ state ? formatDisplayDate(state.expiryDate) diff --git a/govtool/frontend/src/theme.ts b/govtool/frontend/src/theme.ts index 83cd35489..7b9bd2331 100644 --- a/govtool/frontend/src/theme.ts +++ b/govtool/frontend/src/theme.ts @@ -9,57 +9,6 @@ import { successGreen, } from "./consts"; -declare module "@mui/material/styles" { - interface Palette { - accentOrange: string; - accentYellow: string; - arcticWhite: string; - boxShadow1: string; - boxShadow2: string; - errorRed: string; - highlightBlue: string; - inputRed: string; - negativeRed: string; - neutralGray: string; - orangeDark: string; - neutralWhite: string; - positiveGreen: string; - primaryBlue: string; - secondaryBlue: string; - specialCyan: string; - specialCyanBorder: string; - lightBlue: string; - textBlack: string; - textGray: string; - lightOrange: string; - fadedPurple: string; - } - interface PaletteOptions { - accentOrange: string; - accentYellow: string; - arcticWhite: string; - boxShadow1: string; - boxShadow2: string; - errorRed: string; - highlightBlue: string; - orangeDark: string; - inputRed: string; - negativeRed: string; - neutralGray: string; - neutralWhite: string; - positiveGreen: string; - primaryBlue: string; - secondaryBlue: string; - specialCyan: string; - specialCyanBorder: string; - lightBlue: string; - textBlack: string; - textGray: string; - lightOrange: string; - fadedPurple: string; - } -} - export type Theme = typeof theme; export const theme = createTheme({ diff --git a/govtool/frontend/src/types/@mui.d.ts b/govtool/frontend/src/types/@mui.d.ts index c8ea5da12..e83f05e7e 100644 --- a/govtool/frontend/src/types/@mui.d.ts +++ b/govtool/frontend/src/types/@mui.d.ts @@ -16,8 +16,10 @@ declare module "@mui/material/styles" { interface PaletteOptions extends MuiPalette { accentOrange: string; accentYellow: string; + arcticWhite: string; boxShadow1: string; boxShadow2: string; + errorRed: string; highlightBlue: string; inputRed: string; negativeRed: string; diff --git a/govtool/frontend/src/utils/getProposalTypeLabel.ts b/govtool/frontend/src/utils/getProposalTypeLabel.ts index 0064dd602..7a3dcccdc 100644 --- a/govtool/frontend/src/utils/getProposalTypeLabel.ts +++ b/govtool/frontend/src/utils/getProposalTypeLabel.ts @@ -4,5 +4,6 @@ export const getProposalTypeLabel = (type: string) => { const label = GOVERNANCE_ACTIONS_FILTERS.find((i) => i.key === type)?.label; return label || type; }; + export const getProposalTypeNoEmptySpaces = (type: string) => getProposalTypeLabel(type).replace(/ /g, ""); diff --git a/govtool/frontend/src/utils/tests/getProposalTypeNoEmptySpaces.test.ts b/govtool/frontend/src/utils/tests/getProposalTypeNoEmptySpaces.test.ts new file mode 100644 index 000000000..4ec64fe92 --- /dev/null +++ b/govtool/frontend/src/utils/tests/getProposalTypeNoEmptySpaces.test.ts @@ -0,0 +1,25 @@ +import { getProposalTypeNoEmptySpaces } from ".."; + +describe("getProposalTypeNoEmptySpaces", () => { + it("returns correct label with no spaces for a known type", () => { + const type = "NoConfidence"; + const expectedLabel = "NoConfidence"; + expect(getProposalTypeNoEmptySpaces(type)).toBe(expectedLabel); + }); + + it("returns correct label with no spaces for another known type", () => { + const type = "ParameterChange"; + const expectedLabel = "ProtocolParameterChanges"; + expect(getProposalTypeNoEmptySpaces(type)).toBe(expectedLabel); + }); + + it("returns the type itself with no spaces removed when no matching key is found", () => { + const type = "UnknownType"; + expect(getProposalTypeNoEmptySpaces(type)).toBe(type); + }); + + it("returns an empty string when given an empty string", () => { + const type = ""; + expect(getProposalTypeNoEmptySpaces(type)).toBe(type); + }); +}); From 2f64bf1ca2ca9f9f8ce151b7c0f67ffe54c18a7f Mon Sep 17 00:00:00 2001 From: Jan Jaroszczak Date: Mon, 25 Mar 2024 19:39:51 +0100 Subject: [PATCH 5/5] Filter and sort dropdowns fixes --- .../src/components/atoms/ClickOutside.tsx | 30 ---------------- .../frontend/src/components/atoms/index.ts | 1 - .../components/molecules/DataActionsBar.tsx | 34 +++++++++---------- .../molecules/GovernanceActionsFilters.tsx | 18 ++++++---- .../molecules/GovernanceActionsSorting.tsx | 17 ++++++---- .../components/molecules/OrderActionsChip.tsx | 5 +++ govtool/frontend/src/hooks/index.ts | 5 +-- .../frontend/src/hooks/useOutsideClick.tsx | 25 ++++++++++++++ 8 files changed, 72 insertions(+), 63 deletions(-) delete mode 100644 govtool/frontend/src/components/atoms/ClickOutside.tsx create mode 100644 govtool/frontend/src/hooks/useOutsideClick.tsx diff --git a/govtool/frontend/src/components/atoms/ClickOutside.tsx b/govtool/frontend/src/components/atoms/ClickOutside.tsx deleted file mode 100644 index 86266bf2a..000000000 --- a/govtool/frontend/src/components/atoms/ClickOutside.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React, { useRef, useEffect, RefObject } from "react"; - -const useOutsideClick = (ref: RefObject, onClick: () => void) => { - useEffect(() => { - document.addEventListener("mousedown", (e) => { - if (ref.current && !ref.current.contains(e.target as Node)) { - onClick(); - } - }); - - return () => { - document.removeEventListener("mousedown", (e) => { - if (ref.current && !ref.current.contains(e.target as Node)) { - onClick(); - } - }); - }; - }, [ref]); -}; - -interface Props { - children: React.ReactElement; - onClick: () => void; -} - -export const ClickOutside = ({ children, onClick }: Props) => { - const wrapperRef = useRef(null); - useOutsideClick(wrapperRef, onClick); - return
{children}
; -}; diff --git a/govtool/frontend/src/components/atoms/index.ts b/govtool/frontend/src/components/atoms/index.ts index 6fa848bac..186752f80 100644 --- a/govtool/frontend/src/components/atoms/index.ts +++ b/govtool/frontend/src/components/atoms/index.ts @@ -2,7 +2,6 @@ export * from "./ActionRadio"; export * from "./Background"; export * from "./Button"; export * from "./Checkbox"; -export * from "./ClickOutside"; export * from "./CopyButton"; export * from "./DrawerLink"; export * from "./ExternalModalButton"; diff --git a/govtool/frontend/src/components/molecules/DataActionsBar.tsx b/govtool/frontend/src/components/molecules/DataActionsBar.tsx index 297d58bab..08e27a1db 100644 --- a/govtool/frontend/src/components/molecules/DataActionsBar.tsx +++ b/govtool/frontend/src/components/molecules/DataActionsBar.tsx @@ -4,7 +4,6 @@ import Search from "@mui/icons-material/Search"; import { GovernanceActionsFilters, GovernanceActionsSorting } from "@molecules"; import { OrderActionsChip } from "./OrderActionsChip"; -import { ClickOutside } from "../atoms"; import { theme } from "@/theme"; type DataActionsBarProps = { @@ -86,24 +85,23 @@ export const DataActionsBar: FC = ({ ...props }) => { setSortOpen={setSortOpen} sortingActive={sortingActive} sortOpen={sortOpen} - /> + > + {filtersOpen && ( + + )} + {sortOpen && ( + + )} +
- {filtersOpen && ( - - - - )} - {sortOpen && ( - - - - )} ); }; diff --git a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx index 89542217d..29bf5cc99 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionsFilters.tsx @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction, useCallback } from "react"; +import { Dispatch, SetStateAction, useCallback, useRef } from "react"; import { Box, Checkbox, @@ -8,16 +8,18 @@ import { } from "@mui/material"; import { GOVERNANCE_ACTIONS_FILTERS } from "@consts"; -import { useScreenDimension, useTranslation } from "@hooks"; +import { useOnClickOutside, useScreenDimension, useTranslation } from "@hooks"; interface Props { chosenFilters: string[]; setChosenFilters: Dispatch>; + closeFilters: () => void; } export const GovernanceActionsFilters = ({ chosenFilters, setChosenFilters, + closeFilters, }: Props) => { const handleFilterChange = useCallback( (e: React.ChangeEvent) => { @@ -36,7 +38,10 @@ export const GovernanceActionsFilters = ({ ); const { t } = useTranslation(); - const { isMobile } = useScreenDimension(); + const { isMobile, screenWidth } = useScreenDimension(); + + const wrapperRef = useRef(null); + useOnClickOutside(wrapperRef, closeFilters); return ( >; + closeSorts: () => void; } export const GovernanceActionsSorting = ({ chosenSorting, setChosenSorting, + closeSorts, }: Props) => { const { t } = useTranslation(); - const { isMobile } = useScreenDimension(); + + const wrapperRef = useRef(null); + useOnClickOutside(wrapperRef, closeSorts); return ( diff --git a/govtool/frontend/src/components/molecules/OrderActionsChip.tsx b/govtool/frontend/src/components/molecules/OrderActionsChip.tsx index 037a68cd4..006133653 100644 --- a/govtool/frontend/src/components/molecules/OrderActionsChip.tsx +++ b/govtool/frontend/src/components/molecules/OrderActionsChip.tsx @@ -13,6 +13,7 @@ type Props = { sortOpen: boolean; setSortOpen: Dispatch>; sortingActive: boolean; + children?: React.ReactNode; isFiltering?: boolean; }; @@ -32,6 +33,7 @@ export const OrderActionsChip = (props: Props) => { setSortOpen, sortingActive, isFiltering = true, + children, } = props; return ( @@ -41,6 +43,8 @@ export const OrderActionsChip = (props: Props) => { alignItems="center" ml="8px" gap={isMobile ? "8px" : "24px"} + position="relative" + sx={{ alignSelf: "end" }} > {isFiltering && ( { )} + {children} ); }; diff --git a/govtool/frontend/src/hooks/index.ts b/govtool/frontend/src/hooks/index.ts index 84e427cfa..126c2d0c9 100644 --- a/govtool/frontend/src/hooks/index.ts +++ b/govtool/frontend/src/hooks/index.ts @@ -1,8 +1,9 @@ export { useTranslation } from "react-i18next"; +export * from "./useFetchNextPageDetector"; +export * from "./useOutsideClick"; +export * from "./useSaveScrollPosition"; export * from "./useScreenDimension"; export * from "./useSlider"; -export * from "./useSaveScrollPosition"; -export * from "./useFetchNextPageDetector"; export * from "./useWalletConnectionListener"; export * from "./forms"; diff --git a/govtool/frontend/src/hooks/useOutsideClick.tsx b/govtool/frontend/src/hooks/useOutsideClick.tsx new file mode 100644 index 000000000..619086632 --- /dev/null +++ b/govtool/frontend/src/hooks/useOutsideClick.tsx @@ -0,0 +1,25 @@ +import { MutableRefObject, useEffect } from "react"; + +export function useOnClickOutside( + ref: MutableRefObject, + handler: (event: MouseEvent | TouchEvent) => void, +) { + useEffect(() => { + const listener = (event: MouseEvent | TouchEvent) => { + const target = event.target as Node; + + if (!ref.current || ref.current.contains(target)) { + return; + } + handler(event); + }; + + document.addEventListener("mousedown", listener as EventListener); + document.addEventListener("touchstart", listener as EventListener); + + return () => { + document.removeEventListener("mousedown", listener as EventListener); + document.removeEventListener("touchstart", listener as EventListener); + }; + }, [ref, handler]); +}