Skip to content

Commit

Permalink
feat: hints in modals for Beacon and WC
Browse files Browse the repository at this point in the history
  • Loading branch information
dianasavvatina committed Jan 29, 2025
1 parent e5b5a6c commit 9ca413e
Show file tree
Hide file tree
Showing 16 changed files with 134 additions and 28 deletions.
45 changes: 45 additions & 0 deletions apps/web/src/components/HintsAccordion/HintsAccordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
Heading,
} from "@chakra-ui/react";
import { Hints, type SignPage } from "@umami/core";

import { useColor } from "../../styles/useColor";

type HintsProps = {
signPage: SignPage;
};

export const HintsAccordion = ({ signPage }: HintsProps) => {
const color = useColor();

if (!Hints[signPage].header || !Hints[signPage].description) {
return null;
}

return (
<Accordion
width="full"
marginTop="16px"
marginBottom="16px"
allowToggle
data-testid="hints-accordion"
>
<AccordionItem border="none" borderRadius="8px" backgroundColor={color("100")}>
<h2>
<AccordionButton padding="12px" borderRadius="8px">
<Heading flex="1" textAlign="left" size="md">
{Hints[signPage].header}
</Heading>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel padding="16px">{Hints[signPage].description}</AccordionPanel>
</AccordionItem>
</Accordion>
);
};
1 change: 1 addition & 0 deletions apps/web/src/components/HintsAccordion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./HintsAccordion";
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import {
ModalContent,
ModalFooter,
} from "@chakra-ui/react";
import { type ContractCall } from "@umami/core";
import { type ContractCall, Titles } from "@umami/core";
import { FormProvider, useForm } from "react-hook-form";

import { Header } from "./Header";
import { useColor } from "../../../styles/useColor";
import { AddressTile } from "../../AddressTile/AddressTile";
import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion";
import { TezTile } from "../../AssetTiles/TezTile";
import { HintsAccordion } from "../../HintsAccordion";
import { JsValueWrap } from "../../JsValueWrap";
import { Titles } from "../../Titles/Titles";
import { SignButton } from "../SignButton";
import { SignPageFee } from "../SignPageFee";
import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
Expand All @@ -47,6 +47,7 @@ export const ContractCallSignPage = ({
<ModalContent data-testid="ContractCallSignPage">
<form>
<Header headerProps={headerProps} title={Titles.ContractCallSignPage} />
<HintsAccordion signPage="ContractCallSignPage" />
<ModalBody>
<TezTile mutezAmount={mutezAmount} />

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react";
import { type Delegation } from "@umami/core";
import { type Delegation, Titles } from "@umami/core";
import { FormProvider, useForm } from "react-hook-form";

import { Header } from "./Header";
import { AddressTile } from "../../AddressTile/AddressTile";
import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion";
import { Titles } from "../../Titles/Titles";
import { HintsAccordion } from "../../HintsAccordion";
import { SignButton } from "../SignButton";
import { SignPageFee } from "../SignPageFee";
import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
Expand All @@ -27,6 +27,7 @@ export const DelegationSignPage = ({
<ModalContent data-testid="DelegationSignPage">
<form>
<Header headerProps={headerProps} title={Titles.DelegationSignPage} />
<HintsAccordion signPage="DelegationSignPage" />
<ModalBody>
<FormLabel>From</FormLabel>
<AddressTile address={operation.signer.address} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react";
import { Titles } from "@umami/core";
import { useAccountTotalFinalizableUnstakeAmount } from "@umami/state";
import { FormProvider, useForm } from "react-hook-form";

import { Header } from "./Header";
import { AddressTile } from "../../AddressTile/AddressTile";
import { TezTile } from "../../AssetTiles/TezTile";
import { Titles } from "../../Titles/Titles";
import { HintsAccordion } from "../../HintsAccordion";
import { SignButton } from "../SignButton";
import { SignPageFee } from "../SignPageFee";
import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
Expand All @@ -27,6 +28,7 @@ export const FinalizeUnstakeSignPage = ({
<ModalContent data-testid="FinalizeUnstakeSignPage">
<form>
<Header headerProps={headerProps} title={Titles.FinalizeUnstakeSignPage} />
<HintsAccordion signPage="FinalizeUnstakeSignPage" />
<ModalBody>
<Flex alignItems="center" justifyContent="end" marginTop="12px">
<SignPageFee fee={fee} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe("<OriginationOperationSignPage />", () => {

expect(screen.getByTestId("sign-page-header")).toHaveTextContent("Origination Request");
expect(screen.getByTestId("app-name")).toHaveTextContent("mockDappName");
expect(screen.queryByTestId("hints-accordion")).not.toBeInTheDocument();
});

it("passes correct payload to sign handler", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import {
ModalContent,
ModalFooter,
} from "@chakra-ui/react";
import { type ContractOrigination } from "@umami/core";
import { type ContractOrigination, Titles } from "@umami/core";
import { FormProvider, useForm } from "react-hook-form";

import { useColor } from "../../../styles/useColor";
import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion";
import { HintsAccordion } from "../../HintsAccordion";
import { JsValueWrap } from "../../JsValueWrap";
import { SignButton } from "../SignButton";
import { SignPageFee } from "../SignPageFee";
import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
import { Header } from "./Header";
import { Titles } from "../../Titles/Titles";

export const OriginationOperationSignPage = ({
operation,
Expand All @@ -39,6 +39,7 @@ export const OriginationOperationSignPage = ({
<ModalContent data-testid="OriginationOperationSignPage">
<form>
<Header headerProps={headerProps} title={Titles.OriginationOperationSignPage} />
<HintsAccordion signPage="OriginationOperationSignPage" />
<ModalBody data-testid="beacon-request-body">
<Flex alignItems="center" justifyContent="end" marginTop="12px">
<SignPageFee fee={fee} />
Expand Down
25 changes: 11 additions & 14 deletions apps/web/src/components/SendFlow/common/SingleSignPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { BeaconMessageType, NetworkType, type OperationRequestOutput } from "@ai
import type { BatchWalletOperation } from "@taquito/taquito/dist/types/wallet/batch-operation";
import {
type EstimatedAccountOperations,
Hints,
type Operation,
type SignPage,
Titles,
executeOperations,
mockContractCall,
mockContractOrigination,
Expand All @@ -29,7 +32,6 @@ import {
import { SuccessStep } from "../SuccessStep";
import { type SdkSignPageProps, type SignHeaderProps } from "../utils";
import { SingleSignPage } from "./SingleSignPage";
import { Titles } from "../../Titles/Titles";

jest.mock("@umami/core", () => ({
...jest.requireActual("@umami/core"),
Expand Down Expand Up @@ -60,7 +62,7 @@ describe("<SingleSignPage />", () => {
} as OperationRequestOutput;

// check all types of Modals called by SingleSignOperation
const mockedOperations: Record<string, Operation> = {
const mockedOperations: Record<SignPage, Operation> = {
TezSignPage: mockTezOperation(0),
ContractCallSignPage: mockContractCall(0),
DelegationSignPage: mockDelegationOperation(0),
Expand All @@ -71,17 +73,6 @@ describe("<SingleSignPage />", () => {
FinalizeUnstakeSignPage: mockFinalizeUnstakeOperation(0),
};

const titles: Record<string, string> = {
TezSignPage: Titles.TezSignPage,
ContractCallSignPage: Titles.ContractCallSignPage,
DelegationSignPage: Titles.DelegationSignPage,
UndelegationSignPage: Titles.UndelegationSignPage,
OriginationOperationSignPage: Titles.OriginationOperationSignPage,
StakeSignPage: Titles.StakeSignPage,
UnstakeSignPage: Titles.UnstakeSignPage,
FinalizeUnstakeSignPage: Titles.FinalizeUnstakeSignPage,
};

const operation: EstimatedAccountOperations = {
type: "implicit" as const,
sender: mockImplicitAccount(0),
Expand Down Expand Up @@ -117,8 +108,14 @@ describe("<SingleSignPage />", () => {
expect(screen.queryByText("Mainnet")).not.toBeInTheDocument();
expect(screen.getByTestId(key)).toBeInTheDocument(); // e.g. TezSignPage

expect(screen.getByTestId("sign-page-header")).toHaveTextContent(titles[key]);
expect(screen.getByTestId("sign-page-header")).toHaveTextContent(Titles[key]);
expect(screen.getByTestId("app-name")).toHaveTextContent("mockDappName");
if (Hints[key].header && Hints[key].description) {
expect(screen.getByTestId("hints-accordion")).toHaveTextContent(Hints[key].header);
expect(screen.getByTestId("hints-accordion")).toHaveTextContent(Hints[key].description);
} else {
expect(screen.queryByTestId("hints-accordion")).not.toBeInTheDocument();
}

const signButton = screen.getByRole("button", {
name: "Confirm Transaction",
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/components/SendFlow/common/StakeSignPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react";
import { type Stake } from "@umami/core";
import { type Stake, Titles } from "@umami/core";
import { FormProvider, useForm } from "react-hook-form";

import { Header } from "./Header";
import { AddressTile } from "../../AddressTile/AddressTile";
import { TezTile } from "../../AssetTiles/TezTile";
import { Titles } from "../../Titles/Titles";
import { HintsAccordion } from "../../HintsAccordion";
import { SignButton } from "../SignButton";
import { SignPageFee } from "../SignPageFee";
import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
Expand All @@ -27,6 +27,7 @@ export const StakeSignPage = ({
<ModalContent data-testid="StakeSignPage">
<form>
<Header headerProps={headerProps} title={Titles.StakeSignPage} />
<HintsAccordion signPage="StakeSignPage" />
<ModalBody>
<Flex alignItems="center" justifyContent="end" marginTop="12px">
<SignPageFee fee={fee} />
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/components/SendFlow/common/TezSignPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react";
import { type TezTransfer } from "@umami/core";
import { type TezTransfer, Titles } from "@umami/core";
import { FormProvider, useForm } from "react-hook-form";

import { Header } from "./Header";
import { AddressTile } from "../../AddressTile/AddressTile";
import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion";
import { TezTile } from "../../AssetTiles/TezTile";
import { Titles } from "../../Titles/Titles";
import { HintsAccordion } from "../../HintsAccordion";
import { SignButton } from "../SignButton";
import { SignPageFee } from "../SignPageFee";
import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
Expand All @@ -27,6 +27,7 @@ export const TezSignPage = ({
<ModalContent data-testid="TezSignPage">
<form>
<Header headerProps={headerProps} title={Titles.TezSignPage} />
<HintsAccordion signPage="TezSignPage" />
<ModalBody>
<TezTile mutezAmount={mutezAmount} />

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react";
import { Titles } from "@umami/core";
import { FormProvider, useForm } from "react-hook-form";

import { Header } from "./Header";
import { AddressTile } from "../../AddressTile/AddressTile";
import { AdvancedSettingsAccordion } from "../../AdvancedSettingsAccordion";
import { Titles } from "../../Titles/Titles";
import { HintsAccordion } from "../../HintsAccordion";
import { SignButton } from "../SignButton";
import { SignPageFee } from "../SignPageFee";
import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
Expand All @@ -24,6 +25,7 @@ export const UndelegationSignPage = ({
<ModalContent data-testid="UndelegationSignPage">
<form>
<Header headerProps={headerProps} title={Titles.UndelegationSignPage} />
<HintsAccordion signPage="UndelegationSignPage" />
<ModalBody>
<FormLabel>From</FormLabel>
<AddressTile address={operation.signer.address} />
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/components/SendFlow/common/UnstakeSignPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Flex, FormLabel, ModalBody, ModalContent, ModalFooter } from "@chakra-ui/react";
import { type Unstake } from "@umami/core";
import { Titles, type Unstake } from "@umami/core";
import { FormProvider, useForm } from "react-hook-form";

import { Header } from "./Header";
import { AddressTile } from "../../AddressTile/AddressTile";
import { TezTile } from "../../AssetTiles/TezTile";
import { Titles } from "../../Titles/Titles";
import { HintsAccordion } from "../../HintsAccordion";
import { SignButton } from "../SignButton";
import { SignPageFee } from "../SignPageFee";
import { type CalculatedSignProps, type SdkSignPageProps } from "../utils";
Expand All @@ -27,6 +27,7 @@ export const UnstakeSignPage = ({
<ModalContent data-testid="UnstakeSignPage">
<form>
<Header headerProps={headerProps} title={Titles.UnstakeSignPage} />
<HintsAccordion signPage="UnstakeSignPage" />
<ModalBody>
<Flex alignItems="center" justifyContent="end" marginTop="12px">
<SignPageFee fee={fee} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ describe("<useHandleBeaconMessage />", () => {
);
expect(screen.getByTestId("sign-page-header")).toHaveTextContent("Send Request");
expect(screen.getByTestId("app-name")).toHaveTextContent("mockDappName");
expect(screen.queryByTestId("hints-accordion")).not.toBeInTheDocument();

expect(mockToast).not.toHaveBeenCalled();
});
Expand Down
48 changes: 48 additions & 0 deletions packages/core/src/Hints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { type SignPage } from "./Titles";

type HintData = {
header?: string;
description?: string;
};
const basicOperationHeader = undefined;
const basicOperationDescription = undefined;

const finalizationConsequences =
"Finalized funds are returned to your balance, allowing you to spend them or stake them again to earn rewards. Delegation rewards for these funds stop once they are finalized.";
const unstakingConsequences = `The unstaking process takes 4 cycles (~10 days) to unlock your staked funds, making them finalizable on request. You will receive delegation rewards for finalizable funds. ${finalizationConsequences}`;

export const Hints: Record<SignPage, HintData> = {
TezSignPage: {
header: basicOperationHeader,
description: basicOperationDescription,
},
OriginationOperationSignPage: {
header: basicOperationHeader,
description: basicOperationDescription,
},
ContractCallSignPage: {
header: basicOperationHeader,
description: basicOperationDescription,
},
DelegationSignPage: {
header: "It takes 2 cycles (~6 days) to start receiving delegation rewards.",
description:
"Bakers typically distribute delegation rewards every cycle (~3 days). Ensure the delegation fee offered by the baker is less than 100%; otherwise, you may not receive rewards. Delegation means that all your funds are delegated to one baker. The funds remain spendable, and you can cancel or change the delegation at any time.",
},
UndelegationSignPage: {
header: "Stops both delegation and staking. Restoring rewards takes 2 cycles (~6 days).",
description: `Undelegation takes effect immediately. However, you will still receive delegation rewards for the current and the next 2 cycles, as baking rights are computed 2 cycles in advance. By undelegating, you also initiate the unstaking process if you have staked. ${unstakingConsequences}`,
},
StakeSignPage: {
header: "You will start receiving rewards within 1 cycle (~3 days).",
description: `Your spendable balance will be reduced by the amount staked. You will start accruing staking rewards within 1 cycle (~3 days). Staking rewards are compounded on your staked balance. You can cancel staking any time. ${unstakingConsequences}`,
},
UnstakeSignPage: {
header: "Takes at least 4 cycles (~10 days) to complete.",
description: unstakingConsequences,
},
FinalizeUnstakeSignPage: {
header: "Takes 1 block (8 seconds) to complete.",
description: `Finalizing applies to all finalizable funds in your balance. ${finalizationConsequences}`,
},
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export type SignPage = keyof typeof Titles;
export const Titles: Record<string, string> = {
TezSignPage: "Send Request",
ContractCallSignPage: "Contract Call Request",
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ export * from "./Delegate";
export * from "./estimate";
export * from "./execute";
export * from "./helpers";
export * from "./Hints";
export * from "./Operation";
export * from "./testUtils";
export * from "./Titles";
export * from "./Token";
export * from "./TokenBalance";
export * from "./beaconUtils";
Expand Down

1 comment on commit 9ca413e

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Title Lines Statements Branches Functions
apps/desktop Coverage: 83%
83.82% (1788/2133) 79.58% (850/1068) 78.27% (454/580)
apps/web Coverage: 83%
83.82% (1788/2133) 79.58% (850/1068) 78.27% (454/580)
packages/components Coverage: 97%
97.51% (196/201) 95.91% (94/98) 88.13% (52/59)
packages/core Coverage: 82%
82.77% (221/267) 72.51% (95/131) 81.66% (49/60)
packages/crypto Coverage: 100%
100% (43/43) 90.9% (10/11) 100% (7/7)
packages/data-polling Coverage: 96%
94.63% (141/149) 87.5% (21/24) 92.85% (39/42)
packages/multisig Coverage: 98%
98.47% (129/131) 85.71% (18/21) 100% (36/36)
packages/social-auth Coverage: 100%
100% (21/21) 100% (11/11) 100% (3/3)
packages/state Coverage: 85%
84.3% (827/981) 80.5% (190/236) 77.83% (302/388)
packages/tezos Coverage: 89%
88.72% (118/133) 94.59% (35/37) 86.84% (33/38)
packages/tzkt Coverage: 89%
87.32% (62/71) 87.5% (14/16) 80.48% (33/41)

Please sign in to comment.