Skip to content

Commit

Permalink
Snses without actionables banner (#4902)
Browse files Browse the repository at this point in the history
# Motivation

Because not all projects support actionable proposals (return neuron
ballots), not all actionable proposals will be shown on the actionable
proposals page. To make it clear that there could be more votable
proposals, we display a banner with a list of projects that do not have
actionable support. In this PR, we create a banner component that will
be shown on the actionable proposals page.

# Changes

- Add new component `ActionableProposalsNotSupportedSnses`

# Tests

- PO for `ActionableProposalsNotSupportedSnses`
- Tests for `ActionableProposalsNotSupportedSnses`

# Todos

- [ ] Add entry to changelog (if necessary).
not yet.

# Screenshot

<img width="1325" alt="image"
src="https://github.com/dfinity/nns-dapp/assets/98811342/ab1fbcf6-0f6e-4a8f-ac69-cb79fe990808">
  • Loading branch information
mstrasinskis authored May 23, 2024
1 parent 37633d0 commit b4daed2
Show file tree
Hide file tree
Showing 7 changed files with 209 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import { i18n } from "$lib/stores/i18n";
import { PageBanner, IconNotificationPage } from "@dfinity/gix-components";
import { replacePlaceholders } from "$lib/utils/i18n.utils.js";
import { actionableProposalNotSupportedUniversesStore } from "$lib/derived/actionable-proposals.derived";
import TestIdWrapper from "$lib/components/common/TestIdWrapper.svelte";
let snsNames: string;
$: snsNames = $actionableProposalNotSupportedUniversesStore
.map((universe) => universe.title)
.join(", ");
</script>

<TestIdWrapper testId="actionable-proposals-not-supported-snses-component">
{#if snsNames !== ""}
<PageBanner testId="actionable-proposals-not-supported-snses-banner">
<IconNotificationPage slot="image" />
<svelte:fragment slot="title"
>{replacePlaceholders(
$i18n.actionable_proposals_not_supported_snses.title,
{
$snsNames: snsNames,
}
)}</svelte:fragment
>
<p class="description" slot="description">
{$i18n.actionable_proposals_not_supported_snses.text}
</p>
</PageBanner>
{/if}
</TestIdWrapper>
12 changes: 12 additions & 0 deletions frontend/src/lib/derived/actionable-proposals.derived.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ export const actionableProposalSupportedStore: Readable<ActionableProposalSuppor
}),
}));

/** A store that contains sns universes w/o actionable proposals support */
export const actionableProposalNotSupportedUniversesStore: Readable<
Universe[]
> = derived(
[selectableUniversesStore, actionableSnsProposalsStore],
([universes, actionableSnsProposals]) =>
universes.filter(
({ canisterId }) =>
actionableSnsProposals[canisterId]?.includeBallotsByCaller === false
)
);

/** A store that contains sns universes with actionable support and their actionable proposals
* in the same order as they are displayed in the UI. */
export const actionableSnsProposalsByUniverseStore: Readable<
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,10 @@
"text": "$snsName SNS governance canister needs to be updated to the latest version to show actionable proposals. You can still vote on all proposals, but they will not have the visual indication.",
"dot_tooltip": "This SNS doesn't yet support actionable proposals."
},
"actionable_proposals_not_supported_snses": {
"title": "$snsNames don’t yet support actionable proposals.",
"text": "If an SNS DAO wishes to support actionable proposals, need to upgrade to a newer version of the SNS governance canister."
},
"canisters": {
"aria_label_canister_card": "Go to canister details",
"text": "Developers can create and manage their canisters (a form of smart contracts) and cycles consumption here.",
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/lib/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,11 @@ interface I18nActionable_proposals_not_supported {
dot_tooltip: string;
}

interface I18nActionable_proposals_not_supported_snses {
title: string;
text: string;
}

interface I18nCanisters {
aria_label_canister_card: string;
text: string;
Expand Down Expand Up @@ -1318,6 +1323,7 @@ interface I18n {
actionable_proposals_sign_in: I18nActionable_proposals_sign_in;
actionable_proposals_empty: I18nActionable_proposals_empty;
actionable_proposals_not_supported: I18nActionable_proposals_not_supported;
actionable_proposals_not_supported_snses: I18nActionable_proposals_not_supported_snses;
canisters: I18nCanisters;
canister_detail: I18nCanister_detail;
transaction_names: I18nTransaction_names;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import ActionableProposalsNotSupportedSnses from "$lib/components/proposals/ActionableProposalsNotSupportedSnses.svelte";
import { actionableSnsProposalsStore } from "$lib/stores/actionable-sns-proposals.store";
import { principal } from "$tests/mocks/sns-projects.mock";
import { ActionableProposalsNotSupportedSnsesPo } from "$tests/page-objects/ActionableProposalsNotSupportedSnses.page-object";
import { JestPageObjectElement } from "$tests/page-objects/jest.page-object";
import { resetSnsProjects, setSnsProjects } from "$tests/utils/sns.test-utils";
import { render } from "$tests/utils/svelte.test-utils";
import { SnsSwapLifecycle } from "@dfinity/sns";

describe("ActionableProposalsNotSupportedSnses", () => {
const addSnsesWithSupport = (includeBallotsByCallerList: boolean[]) => {
const snsProjects = Array.from(
new Array(includeBallotsByCallerList.length)
).map((_, i) => ({
lifecycle: SnsSwapLifecycle.Committed,
rootCanisterId: principal(i),
projectName: `SNS-${i}`,
}));
setSnsProjects(snsProjects);

Array.from(new Array(includeBallotsByCallerList.length)).forEach((_, i) =>
actionableSnsProposalsStore.set({
rootCanisterId: principal(i),
proposals: [],
includeBallotsByCaller: includeBallotsByCallerList[i],
})
);
};

const renderComponent = () => {
const { container } = render(ActionableProposalsNotSupportedSnses);

return ActionableProposalsNotSupportedSnsesPo.under(
new JestPageObjectElement(container)
);
};

beforeEach(() => {
resetSnsProjects();
actionableSnsProposalsStore.resetForTesting();
});

it("should render a banner when there are Snses w/o actionable support", async () => {
addSnsesWithSupport([false]);
const po = renderComponent();
expect(await po.getBannerPo().isPresent()).toEqual(true);
});

it("should not render the banner when all Snses supports actionable proposals", async () => {
addSnsesWithSupport([true]);
const po = renderComponent();
expect(await po.getBannerPo().isPresent()).toEqual(false);
});

it("should not render the banne when there are no Snses", async () => {
const po = renderComponent();
expect(await po.getBannerPo().isPresent()).toEqual(false);
});

it("should display title", async () => {
addSnsesWithSupport([true, false, true, false, true]);
const po = renderComponent();
expect(await po.getBannerPo().getTitleText()).toEqual(
"SNS-1, SNS-3 don’t yet support actionable proposals."
);
});

it("should display description", async () => {
addSnsesWithSupport([false]);
const po = renderComponent();
expect(await po.getBannerPo().getDescriptionText()).toEqual(
"If an SNS DAO wishes to support actionable proposals, need to upgrade to a newer version of the SNS governance canister."
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AppPath } from "$lib/constants/routes.constants";
import {
actionableProposalCountStore,
actionableProposalIndicationEnabledStore,
actionableProposalNotSupportedUniversesStore,
actionableProposalSupportedStore,
actionableProposalTotalCountStore,
actionableProposalsActiveStore,
Expand Down Expand Up @@ -282,4 +283,61 @@ describe("actionable proposals derived stores", () => {
expect(get(actionableSnsProposalsByUniverseStore)).toEqual([]);
});
});

describe("actionableProposalNotSupportedUniversesStore", () => {
it("should return universes w/o actionable support", async () => {
expect(get(actionableProposalNotSupportedUniversesStore)).toEqual([]);

setSnsProjects([
{
lifecycle: SnsSwapLifecycle.Committed,
rootCanisterId: principal0,
},
{
lifecycle: SnsSwapLifecycle.Committed,
rootCanisterId: principal1,
},
{
lifecycle: SnsSwapLifecycle.Committed,
rootCanisterId: principal2,
},
]);

expect(get(actionableProposalNotSupportedUniversesStore)).toEqual([]);

actionableSnsProposalsStore.set({
rootCanisterId: principal0,
proposals: [],
includeBallotsByCaller: false,
});
expect(
get(actionableProposalNotSupportedUniversesStore).map(
({ canisterId }) => canisterId
)
).toEqual([principal0.toText()]);

actionableSnsProposalsStore.set({
rootCanisterId: principal1,
proposals: [],
includeBallotsByCaller: false,
});
expect(
get(actionableProposalNotSupportedUniversesStore).map(
({ canisterId }) => canisterId
)
).toEqual([principal0.toText(), principal1.toText()]);

// One with `includeBallotsByCaller: true` should not change the result.
actionableSnsProposalsStore.set({
rootCanisterId: principal2,
proposals: [],
includeBallotsByCaller: true,
});
expect(
get(actionableProposalNotSupportedUniversesStore).map(
({ canisterId }) => canisterId
)
).toEqual([principal0.toText(), principal1.toText()]);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { PageBannerPo } from "$tests/page-objects/PageBanner.page-object";
import { BasePageObject } from "$tests/page-objects/base.page-object";
import type { PageObjectElement } from "$tests/types/page-object.types";

export class ActionableProposalsNotSupportedSnsesPo extends BasePageObject {
private static readonly TID =
"actionable-proposals-not-supported-snses-component";

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

getBannerPo(): PageBannerPo {
return PageBannerPo.under({
element: this.root,
testId: "actionable-proposals-not-supported-snses-banner",
});
}
}

0 comments on commit b4daed2

Please sign in to comment.