Skip to content

Commit

Permalink
refactor: add links to vote page addresses (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
goga-m committed Apr 20, 2023
1 parent 0598540 commit 28d8452
Show file tree
Hide file tree
Showing 8 changed files with 2,195 additions and 455 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { Contracts, ReadOnlyWallet } from "@ardenthq/sdk-profiles";
import userEvent from "@testing-library/user-event";
import React, { useEffect } from "react";
import { Route } from "react-router-dom";
import { createHashHistory } from "history";

import { AddressRow } from "@/domains/vote/components/AddressTable/AddressRow/AddressRow";
import { AddressRow, WalletAvatar } from "@/domains/vote/components/AddressTable/AddressRow/AddressRow";
import { data } from "@/tests/fixtures/coins/ark/devnet/delegates.json";
import walletMock from "@/tests/fixtures/coins/ark/devnet/wallets/D8rr7B1d6TL6pf14LgMz4sKp1VBMs6YUYD.json";
import { env, getDefaultProfileId, MNEMONICS, render, screen, syncDelegates } from "@/utils/testing-library";
Expand Down Expand Up @@ -43,7 +44,7 @@ const votingMockReturnValue = (delegatesIndex: number[]) =>
amount: 0,
wallet: new ReadOnlyWallet({
address: data[index].address,
explorerLink: "",
explorerLink: `https://test.arkscan.io/wallets/${data[0].address}`,
governanceIdentifier: "address",
isDelegate: true,
isResignedDelegate: false,
Expand Down Expand Up @@ -396,4 +397,75 @@ describe("AddressRow", () => {

expect(asFragment()).toMatchSnapshot();
});

it("should redirect to wallet details page", async () => {
const route = `/profiles/${profile.id()}/votes`;
const history = createHashHistory();

const historySpy = vi.spyOn(history, "push");

const { container } = render(
<AddressWrapper>
<AddressRow index={0} maxVotes={1} wallet={wallet} />
</AddressWrapper>,
{
history,
route,
},
);
history.push(route);

expect(container).toBeInTheDocument();

userEvent.click(screen.getByTestId("AddressRow__wallet"));

expect(historySpy).toHaveBeenCalledWith(`/profiles/${profile.id()}/wallets/${wallet.id()}`);
});

it("should render wallet avatar", async () => {
render(
<WalletAvatar
wallet={
new ReadOnlyWallet({
address: data[0].address,
explorerLink: `https://test.arkscan.io/wallets/${data[0].address}`,
governanceIdentifier: "address",
isDelegate: true,
isResignedDelegate: false,
publicKey: data[0].publicKey,
username: data[0].username,
})
}
/>,
);

expect(screen.getByTestId("Avatar")).toBeInTheDocument();
});

it("should render wallet avatar in compact mode", async () => {
render(
<WalletAvatar
useCompact
wallet={
new ReadOnlyWallet({
address: data[0].address,
explorerLink: `https://test.arkscan.io/wallets/${data[0].address}`,
governanceIdentifier: "address",
isDelegate: true,
isResignedDelegate: false,
publicKey: data[0].publicKey,
username: data[0].username,
})
}
/>,
);

expect(screen.getByTestId("Avatar")).toBeInTheDocument();
});

it("should not render wallet avatar if wallet is not provided", async () => {
render(<WalletAvatar />);

expect(screen.queryByTestId("Avatar")).not.toBeInTheDocument();
});
});
78 changes: 56 additions & 22 deletions src/domains/vote/components/AddressTable/AddressRow/AddressRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import cn from "classnames";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";

import { generatePath } from "react-router";
import { useHistory } from "react-router-dom";
import { Address } from "@/app/components/Address";
import { Amount } from "@/app/components/Amount";
import { Avatar } from "@/app/components/Avatar";
Expand All @@ -16,6 +18,8 @@ import { useConfiguration } from "@/app/contexts";
import { useActiveProfile, useBreakpoint, useWalletAlias } from "@/app/hooks";
import { assertReadOnlyWallet } from "@/utils/assertions";
import { isLedgerWalletCompatible } from "@/utils/wallet-utils";
import { ProfilePaths } from "@/router/paths";
import { Link } from "@/app/components/Link";

interface AddressRowProperties {
index: number;
Expand All @@ -33,11 +37,31 @@ const StatusIcon = ({ label, icon, color }: { label: string; icon: string; color
</Tooltip>
);

export const WalletAvatar = ({ wallet, useCompact }: { useCompact?: boolean; wallet?: Contracts.IReadOnlyWallet }) => {
if (!wallet) {
return null;
}

return (
<Tooltip content={wallet.username()}>
<Link to={wallet.explorerLink()} isExternal className="flex">
<Avatar
className={cn({ "ring-2 ring-theme-background": useCompact })}
size={useCompact ? "xs" : "lg"}
address={wallet.address()}
noShadow={useCompact}
/>
</Link>
</Tooltip>
);
};

export const AddressRow = ({ index, maxVotes, wallet, onSelect, isCompact = false }: AddressRowProperties) => {
const { t } = useTranslation();
const { profileHasSyncedOnce, profileIsSyncingWallets } = useConfiguration();
const activeProfile = useActiveProfile();
const { isMd, isSm, isXs } = useBreakpoint();
const history = useHistory();

const useCompact = useMemo(() => isCompact || isMd || isSm || isXs, [isCompact, isMd, isSm, isXs]);

Expand Down Expand Up @@ -77,19 +101,6 @@ export const AddressRow = ({ index, maxVotes, wallet, onSelect, isCompact = fals
const hasVotes = votes.length > 0;
const [first, second, third, ...rest] = votes;

const renderAvatar = (address?: string, username?: string) => (
<Tooltip content={username}>
<span className="flex">
<Avatar
className={cn({ "ring-2 ring-theme-background": useCompact })}
size={useCompact ? "xs" : "lg"}
address={address}
noShadow={useCompact}
/>
</span>
</Tooltip>
);

const renderRestOfVotes = (restOfVotes: number) => {
const rest = (
<span className="text-sm font-semibold text-theme-secondary-900 dark:text-theme-secondary-600">
Expand Down Expand Up @@ -159,22 +170,28 @@ export const AddressRow = ({ index, maxVotes, wallet, onSelect, isCompact = fals

if (maxVotes === 1) {
return (
<>
<div className="flex items-center space-x-3 overflow-hidden">
<Avatar size={useCompact ? "xs" : "lg"} address={votes[0].wallet?.address()} noShadow />
<span>{votes[0].wallet?.username()}</span>
</>
<Link
to={votes[0].wallet?.explorerLink() as string}
isExternal
className="block w-24 truncate md:w-auto"
>
{votes[0].wallet?.username()}
</Link>
</div>
);
}

return (
<div className={cn("flex items-center", useCompact ? "-space-x-1" : "-space-x-2")}>
{renderAvatar(first.wallet?.address(), first.wallet?.username())}
<WalletAvatar wallet={first.wallet} />

{second && renderAvatar(second.wallet?.address(), second.wallet?.username())}
{second && <WalletAvatar wallet={second.wallet} />}

{third && renderAvatar(third.wallet?.address(), third.wallet?.username())}
{third && <WalletAvatar wallet={third.wallet} />}

{rest && rest.length === 1 && renderAvatar(rest[0].wallet?.address(), rest[0].wallet?.username())}
{rest && rest.length === 1 && <WalletAvatar wallet={rest[0].wallet} />}

{rest && rest.length > 1 && renderRestOfVotes(rest.length)}
</div>
Expand All @@ -190,13 +207,30 @@ export const AddressRow = ({ index, maxVotes, wallet, onSelect, isCompact = fals
return (
<TableRow>
<TableCell
data-testid="AddressRow__wallet"
onClick={() => {
history.push(
generatePath(ProfilePaths.WalletDetails, {
profileId: activeProfile.id(),
walletId: wallet.id(),
}),
);
}}
variant="start"
innerClassName={cn("font-bold", { "space-x-3": useCompact }, { "space-x-4": !useCompact })}
innerClassName={cn(
"font-bold cursor-pointer group transition duration-300",
{ "space-x-3": useCompact },
{ "space-x-4": !useCompact },
)}
isCompact={useCompact}
>
<Avatar className="shrink-0" size={useCompact ? "xs" : "lg"} address={wallet.address()} noShadow />
<div className="w-40 flex-1">
<Address address={wallet.address()} walletName={alias} />
<Address
address={wallet.address()}
walletName={alias}
walletNameClass="text-theme-primary-700 hover:border-current border-b border-transparent transition-[color,border-color] duration-[200ms,350ms] delay-[0s,100ms] leading-tight"
/>
</div>
</TableCell>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import userEvent from "@testing-library/user-event";
import React, { useEffect } from "react";
import { Route } from "react-router-dom";

import { AddressRowMobile } from "@/domains/vote/components/AddressTable/AddressRow/AddressRowMobile";
import { Context as ResponsiveContext } from "react-responsive";
import {
AddressRowMobile,
AddressRowMobileDelegateName,
} from "@/domains/vote/components/AddressTable/AddressRow/AddressRowMobile";
import { data } from "@/tests/fixtures/coins/ark/devnet/delegates.json";
import walletMock from "@/tests/fixtures/coins/ark/devnet/wallets/D8rr7B1d6TL6pf14LgMz4sKp1VBMs6YUYD.json";
import { env, getDefaultProfileId, MNEMONICS, render, screen, syncDelegates } from "@/utils/testing-library";
Expand Down Expand Up @@ -128,6 +132,35 @@ describe("AddressRowMobile", () => {
isMultiSignatureSpy.mockRestore();
});

it.each([375, 420])("should render in %s screen width", (width: number) => {
const votesMock = vi.spyOn(wallet.voting(), "current").mockReturnValue(votingMockReturnValue([0, 1, 2, 3, 4]));

const { asFragment, container } = render(
<ResponsiveContext.Provider value={{ width }}>
<AddressWrapper>
<AddressRowMobile index={0} maxVotes={1} wallet={wallet} />
</AddressWrapper>
,
</ResponsiveContext.Provider>,
{
route: `/profiles/${profile.id()}/votes`,
},
);

expect(container).toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();

votesMock.mockRestore();
});

it("should not render delegate name if name is not provided", async () => {
const { asFragment } = render(<AddressRowMobileDelegateName />, {
route: `/profiles/${profile.id()}/votes`,
});

expect(asFragment()).toMatchSnapshot();
});

it("should render when wallet not found for votes", async () => {
const { asFragment } = render(
<AddressWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Contracts } from "@ardenthq/sdk-profiles";
import { generatePath } from "react-router";
import { useHistory } from "react-router-dom";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";

import { useMediaQuery } from "react-responsive";
import { Avatar } from "@/app/components/Avatar";
import { Circle } from "@/app/components/Circle";
import { Icon } from "@/app/components/Icon";
Expand All @@ -11,6 +15,9 @@ import { assertReadOnlyWallet } from "@/utils/assertions";
import { Address } from "@/app/components/Address";
import { Button } from "@/app/components/Button";
import { Divider } from "@/app/components/Divider";
import { Link } from "@/app/components/Link";
import { ProfilePaths } from "@/router/paths";
import { TruncateMiddle } from "@/app/components/TruncateMiddle";

interface AddressRowMobileProperties {
index: number;
Expand All @@ -27,10 +34,25 @@ const StatusIcon = ({ label, icon, color }: { label: string; icon: string; color
</Tooltip>
);

export const AddressRowMobileDelegateName = ({ name }: { name?: string }) => {
const is2Xs = useMediaQuery({ maxWidth: 410 });

if (!name) {
return null;
}

return (
<div className="flex w-full items-center">
<TruncateMiddle text={name} maxChars={is2Xs ? 10 : 50} />
</div>
);
};

export const AddressRowMobile = ({ index, maxVotes, wallet, onSelect }: AddressRowMobileProperties) => {
const { t } = useTranslation();
const activeProfile = useActiveProfile();
const { profileHasSyncedOnce, profileIsSyncingWallets } = useConfiguration();
const history = useHistory();

const { getWalletAlias } = useWalletAlias();

Expand Down Expand Up @@ -140,11 +162,15 @@ export const AddressRowMobile = ({ index, maxVotes, wallet, onSelect }: AddressR
<Avatar size="xs" address={votes[0].wallet.address()} noShadow />

{votes[0].wallet && (
<div className="flex items-center text-right">
<div className="flex items-center">
<div className="flex flex-1 justify-end overflow-hidden">
<span className="overflow-hidden overflow-ellipsis whitespace-nowrap">
{votes[0].wallet.username()}
</span>
<Link
isExternal
to={votes[0].wallet.explorerLink()}
className="flex w-full items-center"
>
<AddressRowMobileDelegateName name={votes[0].wallet.username()} />
</Link>
</div>
</div>
)}
Expand All @@ -170,7 +196,17 @@ export const AddressRowMobile = ({ index, maxVotes, wallet, onSelect }: AddressR
return (
<tr data-testid="AddressRowMobile">
<td className="pt-3">
<div className="overflow-hidden rounded-xl border border-theme-secondary-300 dark:border-theme-secondary-800">
<div
className="overflow-hidden rounded-xl border border-theme-secondary-300 dark:border-theme-secondary-800"
onClick={() => {
history.push(
generatePath(ProfilePaths.WalletDetails, {
profileId: activeProfile.id(),
walletId: wallet.id(),
}),
);
}}
>
<div className="overflow-hidden border-b border-theme-secondary-300 py-4 px-6 dark:border-theme-secondary-800">
<div className="flex items-center justify-start space-x-3 overflow-hidden">
<Avatar className="shrink-0" size="xs" address={wallet.address()} noShadow />
Expand Down
Loading

1 comment on commit 28d8452

@vercel
Copy link

@vercel vercel bot commented on 28d8452 Apr 20, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.