Skip to content

Commit

Permalink
Failed tokens goto dashboard action (#5521)
Browse files Browse the repository at this point in the history
# Motivation

To improve the failed imported tokens UI (because we don't display the
whole failed ledger canister ID) we add a button to check the ledger
canister on the dashboard.

# Changes

- Extended current token table action buttons to support
`UserTokenFailed`, to unify the action types.
- Added new `GoToDashboardButton` action button to the failed token
rows.

# Tests

- A unit test added.
- Tested locally
<img width="794" alt="image"
src="https://github.com/user-attachments/assets/7d4c63e2-8e84-427c-a79d-b713b6898c7e">


# Todos

- [ ] Add entry to changelog (if necessary).
Not necessary.
  • Loading branch information
mstrasinskis committed Sep 25, 2024
1 parent 44a12b6 commit 4534945
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
type UserTokenData,
type UserTokenLoading,
} from "$lib/types/tokens-page";
import { isUserTokenData } from "$lib/utils/user-token.utils";
import { isUserTokenLoading } from "$lib/utils/user-token.utils";
import GoToDashboardButton from "./actions/GoToDashboardButton.svelte";
import GoToDetailIcon from "./actions/GoToDetailIcon.svelte";
import ReceiveButton from "./actions/ReceiveButton.svelte";
import SendButton from "./actions/SendButton.svelte";
Expand All @@ -16,15 +17,18 @@
const actionMapper: Record<
UserTokenAction,
ComponentType<SvelteComponent<{ userToken: UserTokenData }>>
ComponentType<
SvelteComponent<{ userToken: UserTokenData | UserTokenFailed }>
>
> = {
[UserTokenAction.GoToDetail]: GoToDetailIcon,
[UserTokenAction.Receive]: ReceiveButton,
[UserTokenAction.Send]: SendButton,
[UserTokenAction.GoToDashboard]: GoToDashboardButton,
};
let userToken: UserTokenData | undefined;
$: userToken = isUserTokenData(rowData) ? rowData : undefined;
let userToken: UserTokenData | UserTokenFailed | undefined;
$: userToken = isUserTokenLoading(rowData) ? undefined : rowData;
</script>

{#if nonNullish(userToken)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import type { Principal } from "@dfinity/principal";
import LinkToDashboardCanister from "$lib/components/tokens/LinkToDashboardCanister.svelte";
export let userToken: UserTokenData | UserTokenFailed;
let canisterId: Principal;
$: canisterId = userToken.universeId;
</script>

<LinkToDashboardCanister {canisterId} />
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script lang="ts">
import type { UserTokenData } from "$lib/types/tokens-page";
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import { IconRight } from "@dfinity/gix-components";
// svelte-ignore unused-export-let
export let userToken: UserTokenData;
export let userToken: UserTokenData | UserTokenFailed;
</script>

<span data-tid="go-to-detail-icon-component">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
<script lang="ts">
import { ActionType } from "$lib/types/actions";
import type { UserTokenData } from "$lib/types/tokens-page";
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import { IconQRCodeScanner } from "@dfinity/gix-components";
import { createEventDispatcher } from "svelte";
import { isUserTokenData } from "$lib/utils/user-token.utils";
export let userToken: UserTokenData;
// The UserTokenFailed type was added to unify the action types. However, this action only works with UserTokenData.
export let userToken: UserTokenData | UserTokenFailed;
const dispatcher = createEventDispatcher();
</script>

<button
class="icon-only"
data-tid="receive-button-component"
on:click|preventDefault|stopPropagation={() => {
dispatcher("nnsAction", { type: ActionType.Receive, data: userToken });
}}
>
<IconQRCodeScanner />
</button>
{#if isUserTokenData(userToken)}
<button
class="icon-only"
data-tid="receive-button-component"
on:click|preventDefault|stopPropagation={() => {
dispatcher("nnsAction", { type: ActionType.Receive, data: userToken });
}}
>
<IconQRCodeScanner />
</button>
{/if}
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
<script lang="ts">
import { ActionType } from "$lib/types/actions";
import type { UserTokenData } from "$lib/types/tokens-page";
import type { UserTokenData, UserTokenFailed } from "$lib/types/tokens-page";
import { IconUp } from "@dfinity/gix-components";
import { createEventDispatcher } from "svelte";
import { isUserTokenData } from "$lib/utils/user-token.utils";
export let userToken: UserTokenData;
// The UserTokenFailed type was added to unify the action types. Works only with UserTokenData.
export let userToken: UserTokenData | UserTokenFailed;
const dispatcher = createEventDispatcher();
</script>

<button
class="icon-only"
data-tid="send-button-component"
on:click|stopPropagation|preventDefault={() => {
dispatcher("nnsAction", { type: ActionType.Send, data: userToken });
}}
>
<IconUp />
</button>
{#if isUserTokenData(userToken)}
<button
class="icon-only"
data-tid="send-button-component"
on:click|stopPropagation|preventDefault={() => {
dispatcher("nnsAction", { type: ActionType.Send, data: userToken });
}}
>
<IconUp />
</button>
{/if}
1 change: 1 addition & 0 deletions frontend/src/lib/types/tokens-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export enum UserTokenAction {
Send = "send",
GoToDetail = "goToDetail",
Receive = "receive",
GoToDashboard = "goToDashboard",
}

export type UserTokenBase = {
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/lib/utils/user-token.utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import UNKNOWN_LOGO from "$lib/assets/question-mark.svg";
import type {
UserTokenData,
UserTokenFailed,
UserTokenLoading,
import {
UserTokenAction,
type UserTokenData,
type UserTokenFailed,
type UserTokenLoading,
} from "$lib/types/tokens-page";
import { Principal } from "@dfinity/principal";

Expand Down Expand Up @@ -33,5 +34,5 @@ export const toUserTokenFailed = (
logo: UNKNOWN_LOGO,
balance: "failed",
domKey: ledgerCanisterIdText,
actions: [],
actions: [UserTokenAction.GoToDashboard],
});
5 changes: 5 additions & 0 deletions frontend/src/tests/page-objects/TokensTableRow.page-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { PageObjectElement } from "$tests/types/page-object.types";
import { nonNullish } from "@dfinity/utils";
import { AmountDisplayPo } from "./AmountDisplay.page-object";
import { HashPo } from "./Hash.page-object";
import { LinkToDashboardCanisterPo } from "./LinkToDashboardCanister.page-object";
import { TooltipPo } from "./Tooltip.page-object";

export type TokensTableRowData = {
Expand Down Expand Up @@ -134,6 +135,10 @@ export class TokensTableRowPo extends ResponsiveTableRowPo {
return this.root.byTestId("go-to-detail-icon-component");
}

getGoToDashboardButton(): LinkToDashboardCanisterPo {
return LinkToDashboardCanisterPo.under({ element: this.root });
}

hasGoToDetailIcon(): Promise<boolean> {
return this.getGoToDetailIcon().isPresent();
}
Expand Down
33 changes: 33 additions & 0 deletions frontend/src/tests/routes/app/tokens/page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,39 @@ describe("Tokens route", () => {
}
}
});

it("should not display goto dashboard for not failed tokens", async () => {
const po = await renderPage();
const tokensPagePo = po.getTokensPagePo();
const ckBTCTokenRow = await tokensPagePo
.getTokensTable()
.getRowByName("ckBTC");
const notFailedTokenRow = await tokensPagePo
.getTokensTable()
.getRowByName("ZTOKEN1");

expect(
await ckBTCTokenRow.getGoToDashboardButton().isPresent()
).toEqual(false);
expect(
await notFailedTokenRow.getGoToDashboardButton().isPresent()
).toEqual(false);
});

it("should have view on dashboard action button", async () => {
const po = await renderPage();
const tokensPagePo = po.getTokensPagePo();
const failedTokenRow = await tokensPagePo
.getTokensTable()
.getRowByName(failedImportedTokenIdText);

expect(
await failedTokenRow.getGoToDashboardButton().isPresent()
).toEqual(true);
expect(await failedTokenRow.getGoToDashboardButton().getHref()).toEqual(
`https://dashboard.internetcomputer.org/canister/${failedImportedTokenIdText}`
);
});
});

describe("when logged out", () => {
Expand Down

0 comments on commit 4534945

Please sign in to comment.