Skip to content

Commit

Permalink
Nns missing rewards indicator (#6068)
Browse files Browse the repository at this point in the history
# Motivation

When the user has Nns neurons that haven’t been active and are set to
start losing rewards in 30 days or less, a red circle needs to be
displayed next to the “Neuron Staking” sidebar item and on in the Neuron
Staking page table to indicate that action is required.
In this PR, we introduce the indicator component, which will be used in
the navigation menu and the projects table.

# Changes

- New `NnsNeuronsMissingRewardsBadge` component.

# Tests

- Added.

# Todos

- [ ] Add entry to changelog (if necessary).
Not necessary.
  • Loading branch information
mstrasinskis authored Dec 23, 2024
1 parent 4b5c9c3 commit f84b0fc
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts">
import TestIdWrapper from "$lib/components/common/TestIdWrapper.svelte";
import { soonLosingRewardNeuronsStore } from "$lib/derived/neurons.derived";
import { i18n } from "$lib/stores/i18n";
</script>

<TestIdWrapper testId="nns-neurons-missing-rewards-badge-component">
{#if $soonLosingRewardNeuronsStore.length > 0}
<span
data-tid="badge"
class="badge"
role="status"
aria-label={$i18n.losing_rewards.badge_label}
></span>
{/if}
</TestIdWrapper>

<style lang="scss">
.badge {
display: flex;
justify-content: center;
align-items: center;
&::after {
content: "";
display: block;
padding: var(--padding-0_5x);
border-radius: 50%;
background-color: var(--negative-emphasis);
}
}
</style>
1 change: 1 addition & 0 deletions frontend/src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@
"description": "ICP neurons that are inactive for $period start missing voting rewards. To avoid missing rewards, vote manually, edit, or confirm your following.",
"confirming": "Confirming following. This may take a moment.",
"confirm": "Confirm Following",
"badge_label": "Confirm following to avoid missing rewards",
"hw_hotkey_warning": "You may have neurons that are not added to the NNS dapp. If you want to view your neurons and get alerts in case they start missing voting rewards, you can add them by clicking <strong>Show Neurons > Add to NNS Dapp</strong>."
},
"losing_rewards_banner": {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ interface I18nLosing_rewards {
description: string;
confirming: string;
confirm: string;
badge_label: string;
hw_hotkey_warning: string;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import NnsNeuronsMissingRewardsBadge from "$lib/components/neurons/NnsNeuronsMissingRewardsBadge.svelte";
import { SECONDS_IN_HALF_YEAR } from "$lib/constants/constants";
import { neuronsStore } from "$lib/stores/neurons.store";
import { nowInSeconds } from "$lib/utils/date.utils";
import { mockIdentity } from "$tests/mocks/auth.store.mock";
import { mockFullNeuron, mockNeuron } from "$tests/mocks/neurons.mock";
import { NnsNeuronsMissingRewardsBadgePo } from "$tests/page-objects/NnsNeuronsMissingRewardsBadge.page-object";
import { JestPageObjectElement } from "$tests/page-objects/jest.page-object";
import { render } from "$tests/utils/svelte.test-utils";

describe("NnsNeuronsMissingRewardsBadge", () => {
const nowSeconds = nowInSeconds();
const activeNeuron = {
...mockNeuron,
neuronId: 0n,
fullNeuron: {
...mockFullNeuron,
votingPowerRefreshedTimestampSeconds: BigInt(nowSeconds),
controller: mockIdentity.getPrincipal().toText(),
},
};
const losingRewardsNeuron = {
...mockNeuron,
neuronId: 2n,
fullNeuron: {
...mockFullNeuron,
votingPowerRefreshedTimestampSeconds: BigInt(
nowSeconds - SECONDS_IN_HALF_YEAR
),
controller: mockIdentity.getPrincipal().toText(),
},
};
const renderComponent = async () => {
const { container } = render(NnsNeuronsMissingRewardsBadge);
return NnsNeuronsMissingRewardsBadgePo.under(
new JestPageObjectElement(container)
);
};

beforeEach(() => {
vi.useFakeTimers({
now: nowSeconds * 1000,
});
});

it("should be visible when there is an inactive neuron", async () => {
neuronsStore.setNeurons({
neurons: [activeNeuron, losingRewardsNeuron],
certified: true,
});
const po = await renderComponent();

expect(await po.isVisible()).toBe(true);
});

it("should be hidden when there is no inactive neurons", async () => {
neuronsStore.setNeurons({
neurons: [activeNeuron],
certified: true,
});
const po = await renderComponent();

expect(await po.isVisible()).toBe(false);
});

it("should be hidden when there is no neurons", async () => {
neuronsStore.setNeurons({
neurons: [],
certified: true,
});
const po = await renderComponent();

expect(await po.isVisible()).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BasePageObject } from "$tests/page-objects/base.page-object";
import type { PageObjectElement } from "$tests/types/page-object.types";

export class NnsNeuronsMissingRewardsBadgePo extends BasePageObject {
private static readonly TID = "nns-neurons-missing-rewards-badge-component";

static under(element: PageObjectElement): NnsNeuronsMissingRewardsBadgePo {
return new NnsNeuronsMissingRewardsBadgePo(
element.byTestId(NnsNeuronsMissingRewardsBadgePo.TID)
);
}

isVisible(): Promise<boolean> {
return this.root.byTestId("badge").isPresent();
}
}

0 comments on commit f84b0fc

Please sign in to comment.