diff --git a/CHANGELOG.md b/CHANGELOG.md index 69e2f4722..82cddf042 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ changes. ### Added +- Add Proposal discussion context that manages username [Issue 3341](https://github.com/IntersectMBO/govtool/issues/3341) +- Add epochParams and ada holder balance to Proposal Discussion Pillar [Issue 2243](https://github.com/IntersectMBO/govtool/issues/2243) + ### Fixed - Fix scroll on a drawer on smaller resolution diff --git a/govtool/frontend/src/context/contextProviders.tsx b/govtool/frontend/src/context/contextProviders.tsx index d38fa0acc..b17e122be 100644 --- a/govtool/frontend/src/context/contextProviders.tsx +++ b/govtool/frontend/src/context/contextProviders.tsx @@ -6,6 +6,7 @@ import { DataActionsBarProvider } from "./dataActionsBar"; import { FeatureFlagProvider } from "./featureFlag"; import { GovernanceActionProvider } from "./governanceAction"; import { AdaHandleProvider } from "./adaHandle"; +import { ProposalDiscussionProvider } from "./proposalDiscussion"; interface Props { children: React.ReactNode; @@ -14,17 +15,19 @@ interface Props { const ContextProviders = ({ children }: Props) => ( - - - - - - {children} - - - - - + + + + + + + {children} + + + + + + ); diff --git a/govtool/frontend/src/context/index.ts b/govtool/frontend/src/context/index.ts index 8bf7be85f..0ece5a1b5 100644 --- a/govtool/frontend/src/context/index.ts +++ b/govtool/frontend/src/context/index.ts @@ -8,3 +8,4 @@ export * from "./usersnapContext"; export * from "./wallet"; export * from "./featureFlag"; export * from "./governanceAction"; +export * from "./proposalDiscussion"; diff --git a/govtool/frontend/src/context/proposalDiscussion.tsx b/govtool/frontend/src/context/proposalDiscussion.tsx new file mode 100644 index 000000000..6e1ee3b1f --- /dev/null +++ b/govtool/frontend/src/context/proposalDiscussion.tsx @@ -0,0 +1,54 @@ +import { + PropsWithChildren, + useMemo, + createContext, + useContext, + useState, +} from "react"; + +type ProposalDiscussionContextType = { + username: string; + setUsername: (username: string) => void; +} | null; + +const ProposalDiscussionContext = + createContext(null); + +/** + * Provides proposal discussion context to its children components. + * + * @param children - The child components to render. + */ +const ProposalDiscussionProvider = ({ children }: PropsWithChildren) => { + const [username, setUsername] = useState(""); + + const value = useMemo( + () => ({ + username, + setUsername, + }), + [username], + ); + + return ( + + {children} + + ); +}; + +/** + * Custom hook to use the ProposalDiscussionContext. + * @returns The context value. + */ +const useProposalDiscussion = () => { + const context = useContext(ProposalDiscussionContext); + if (!context) { + throw new Error( + "useProposalDiscussion must be used within a ProposalDiscussionProvider", + ); + } + return context; +}; + +export { ProposalDiscussionProvider, useProposalDiscussion }; diff --git a/govtool/frontend/src/pages/GovernanceActionOutComes.tsx b/govtool/frontend/src/pages/GovernanceActionOutComes.tsx index 79db53f63..100250c23 100644 --- a/govtool/frontend/src/pages/GovernanceActionOutComes.tsx +++ b/govtool/frontend/src/pages/GovernanceActionOutComes.tsx @@ -2,7 +2,7 @@ import { Box, CircularProgress } from "@mui/material"; import React, { Suspense } from "react"; import { Footer, TopNav } from "@/components/organisms"; import { useCardano } from "@/context"; -import { useScreenDimension } from "@/hooks"; +import { useScreenDimension, useTranslation } from "@/hooks"; import { Background } from "@/components/atoms"; const GovernanceActionsOutcomes = React.lazy( @@ -12,6 +12,8 @@ const GovernanceActionsOutcomes = React.lazy( export const GovernanceActionOutComesPillar = () => { const { pagePadding } = useScreenDimension(); const { walletApi, ...context } = useCardano(); + const { i18n } = useTranslation(); + return ( { apiUrl={import.meta.env.VITE_OUTCOMES_API_URL} ipfsGateway={import.meta.env.VITE_IPFS_GATEWAY} walletAPI={{ ...context, ...walletApi }} + i18n={i18n} /> diff --git a/govtool/frontend/src/pages/ProposalDiscussion.tsx b/govtool/frontend/src/pages/ProposalDiscussion.tsx index 25d3c67bf..b69836df9 100644 --- a/govtool/frontend/src/pages/ProposalDiscussion.tsx +++ b/govtool/frontend/src/pages/ProposalDiscussion.tsx @@ -1,23 +1,35 @@ import React, { ComponentProps, Suspense } from "react"; import { Box, CircularProgress } from "@mui/material"; import "@intersect.mbo/pdf-ui/style"; -import { useCardano, useGovernanceActions } from "@/context"; +import { + useAppContext, + useCardano, + useGovernanceActions, + useProposalDiscussion, +} from "@/context"; import { useValidateMutation } from "@/hooks/mutations"; import { useScreenDimension } from "@/hooks/useScreenDimension"; import { Footer, TopNav } from "@/components/organisms"; -import { useGetDRepVotingPowerList, useGetVoterInfo } from "@/hooks"; +import { + useGetAdaHolderVotingPowerQuery, + useGetDRepVotingPowerList, + useGetVoterInfo, +} from "@/hooks"; const ProposalDiscussion = React.lazy( () => import("@intersect.mbo/pdf-ui/cjs"), ); export const ProposalDiscussionPillar = () => { + const { epochParams } = useAppContext(); const { pagePadding } = useScreenDimension(); const { validateMetadata } = useValidateMutation(); const { walletApi, ...context } = useCardano(); const { voter } = useGetVoterInfo(); const { createGovernanceActionJsonLD, createHash } = useGovernanceActions(); const { fetchDRepVotingPowerList } = useGetDRepVotingPowerList(); + const { username, setUsername } = useProposalDiscussion(); + const { votingPower } = useGetAdaHolderVotingPowerQuery(context.stakeKey); return ( { >["validateMetadata"] } fetchDRepVotingPowerList={fetchDRepVotingPowerList} + username={username} + setUsername={setUsername} + epochParams={epochParams} + votingPower={votingPower} /> diff --git a/govtool/frontend/src/types/@intersect.mbo.d.ts b/govtool/frontend/src/types/@intersect.mbo.d.ts index 39af24c64..140fcd55b 100644 --- a/govtool/frontend/src/types/@intersect.mbo.d.ts +++ b/govtool/frontend/src/types/@intersect.mbo.d.ts @@ -4,39 +4,46 @@ enum MetadataValidationStatus { INVALID_HASH = "INVALID_HASH", INCORRECT_FORMAT = "INCORRECT_FORMAT", } +declare module "@intersect.mbo/pdf-ui/cjs" { + import { EpochParams } from "@/models"; -type ProposalDiscussionProps = { - pdfApiUrl: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletAPI: any; - pathname: string; - locale?: string; - validateMetadata: ({ - url, - hash, - standard, - }: { - url: string; - hash: string; - standard: "CIP108"; - }) => Promise< + type ProposalDiscussionProps = { + pdfApiUrl: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any - | { status?: MetadataValidationStatus; metadata?: any; valid: boolean } - | undefined - >; - fetchDRepVotingPowerList: ( - identifiers: string[], - ) => Promise; -}; + walletAPI: any; + pathname: string; + locale?: string; + validateMetadata: ({ + url, + hash, + standard, + }: { + url: string; + hash: string; + standard: "CIP108"; + }) => Promise< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | { status?: MetadataValidationStatus; metadata?: any; valid: boolean } + | undefined + >; + fetchDRepVotingPowerList: ( + identifiers: string[], + ) => Promise; + epochParams?: EpochParams; + votingPower: number; + username: string; + setUsername: (username: string) => void; + }; -type GovernanceActionsOutcomesProps = { - apiUrl?: string; - ipfsGateway?: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - walletAPI?: any; -}; + type GovernanceActionsOutcomesProps = { + apiUrl?: string; + ipfsGateway?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + walletAPI?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + i18n?: any; + }; -declare module "@intersect.mbo/pdf-ui/cjs" { export default function ProposalDiscussion( props: ProposalDiscussionProps, ): JSX.Element; diff --git a/tests/govtool-frontend/playwright/lib/services/kuberService.ts b/tests/govtool-frontend/playwright/lib/services/kuberService.ts index d4bbd06c5..5192debc4 100644 --- a/tests/govtool-frontend/playwright/lib/services/kuberService.ts +++ b/tests/govtool-frontend/playwright/lib/services/kuberService.ts @@ -211,7 +211,10 @@ const kuberService = { }, multipleDRepRegistration: (metadataAndWallets: WalletAndAnchorType[]) => { - const kuber = new Kuber(faucetWallet.address, faucetWallet.payment.private); + const kuber = new Kuber( + proposalFaucetWallet.address, + proposalFaucetWallet.payment.private + ); const req = { certificates: metadataAndWallets.map((metadataAndWallet) => Kuber.generateCert( diff --git a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.loggedin.spec.ts index e73882eac..e76bb8738 100644 --- a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.loggedin.spec.ts @@ -49,7 +49,7 @@ test.describe("Budget proposal logged in state", () => { }); test("11I. Should comments on any proposal", async ({}) => { - const comment = faker.lorem.paragraph(2); + const comment = faker.lorem.paragraph(1); await budgetDiscussionDetailsPage.addComment(comment); await expect( budgetDiscussionDetailsPage.currentPage @@ -59,7 +59,7 @@ test.describe("Budget proposal logged in state", () => { }); test("11J. Should reply to any comments", async ({}) => { - const randComment = faker.lorem.paragraph(2); + const randComment = faker.lorem.paragraph(1); const randReply = faker.lorem.words(5); await budgetDiscussionDetailsPage.addComment(randComment); diff --git a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.spec.ts b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.spec.ts index 9e7fc2ce2..22a041e45 100644 --- a/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.spec.ts +++ b/tests/govtool-frontend/playwright/tests/11-proposal-budget/proposalBudget.spec.ts @@ -187,7 +187,7 @@ test("11E. Should view comments with count indications on a budget proposal", as page, }) => { let responsePromise = page.waitForResponse((response) => - response.url().includes(`/api/comments`) + response.url().includes(`/api/bds/`) ); const budgetDiscussionPage = new BudgetDiscussionPage(page); @@ -197,13 +197,23 @@ test("11E. Should view comments with count indications on a budget proposal", as await budgetDiscussionPage.viewFirstProposal(); const response = await responsePromise; - const comments: CommentResponse[] = (await response.json()).data; + const proposalResponse = await response.json(); - await responsePromise; + const actualTotalComments = + await budgetDiscussionDetailsPage.totalComments.textContent(); + const expectedTotalComments = + proposalResponse.data.attributes.prop_comments_number.toString(); + const isEqual = actualTotalComments === expectedTotalComments; - await expect(budgetDiscussionDetailsPage.totalComments).toHaveText( - comments.length.toString() - ); + const currentPageUrl = budgetDiscussionDetailsPage.currentPage.url(); + + const proposalId = extractProposalIdFromUrl(currentPageUrl); + + await expect( + budgetDiscussionDetailsPage.totalComments, + !isEqual && + `Total comments do not match in ${environments.frontendUrl}/budget_discussion/${proposalId}` + ).toHaveText(expectedTotalComments); }); test.describe("Restricted access to interact budget proposal", () => {