Skip to content

Commit

Permalink
Tooltip to voting cards for the blue dots (#4739)
Browse files Browse the repository at this point in the history
# Motivation

Add a tooltip when hovering over the blue dot that appears for
actionable proposals.
Copy to read: “You can still vote on this proposal.”

# Changes

- Make the dot a separate DOM element to have anything to wrap with the
tooltip.
- Add a tooltip.
- Add a simple initial animation.
- 📜 Side quest: added `role="status"` to the actionable count badge.

# Tests

- Extend PO to get the tooltip.
- Tooltip is presented.

# Todos

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

# Screenshots

<img width="337" alt="image"
src="https://github.com/dfinity/nns-dapp/assets/98811342/c27b6ca2-c6f8-45cf-833b-935045e94f3e">
  • Loading branch information
mstrasinskis authored Apr 17, 2024
1 parent b88af06 commit 3b9f150
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
duration: 250,
easing: cubicOut,
}}
class="tag">{count}</span
class="tag"
role="status">{count}</span
></Tooltip
>
{/if}
Expand Down
52 changes: 36 additions & 16 deletions frontend/src/lib/components/ui/ProposalStatusTag.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<script lang="ts">
import type { UniversalProposalStatus } from "$lib/types/proposals";
import { i18n } from "$lib/stores/i18n";
import { Tooltip } from "@dfinity/gix-components";
import { cubicOut } from "svelte/easing";
import { scale } from "svelte/transition";
import TestIdWrapper from "$lib/components/common/TestIdWrapper.svelte";
export let status: UniversalProposalStatus;
export let actionable: boolean | undefined = undefined;
Expand All @@ -9,9 +13,25 @@
$: label = $i18n.universal_proposal_status[status];
</script>

<span data-tid="proposal-status-tag" class={`tag ${status}`} class:actionable
>{label}</span
>
<div data-tid="proposal-status-tag" class={`tag ${status}`}>
{label}<TestIdWrapper testId="actionable-status-badge">
{#if actionable}
<div class="actionable-status-container">
<Tooltip
id="actionable-status-tooltip"
text={$i18n.voting.is_actionable_status_badge_tooltip}
top={true}
>
<div
class="actionable-status-badge"
role="status"
transition:scale={{ duration: 250, easing: cubicOut }}
/>
</Tooltip>
</div>
{/if}
</TestIdWrapper>
</div>

<style lang="scss">
@use "@dfinity/gix-components/dist/styles/mixins/media";
Expand Down Expand Up @@ -48,20 +68,20 @@
background-color: var(--orange-tint);
}
&.actionable {
&:after {
content: "";
position: absolute;
top: calc(-1 * var(--padding-0_5x));
right: calc(-1 * var(--padding-0_5x));
width: var(--padding-1_5x);
height: var(--padding-1_5x);
// Because of Tooltip wrapper the badge needs a container for positioning.
.actionable-status-container {
position: absolute;
top: calc(-1 * var(--padding-0_5x));
right: calc(-1 * var(--padding-0_5x));
}
.actionable-status-badge {
width: var(--padding-1_5x);
height: var(--padding-1_5x);
box-sizing: border-box;
border: 1.5px solid var(--card-background);
border-radius: var(--padding-1_5x);
background: var(--primary);
}
box-sizing: border-box;
border: 1.5px solid var(--card-background);
border-radius: var(--padding-1_5x);
background: var(--primary);
}
}
</style>
3 changes: 2 additions & 1 deletion frontend/src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,8 @@
"all_proposals": "All Proposals",
"actionable_proposals": "Actionable Proposals",
"nns_actionable_proposal_tooltip": "There are $count NNS proposals you can vote on.",
"sns_actionable_proposal_tooltip": "There are $count $snsName proposals you can vote on."
"sns_actionable_proposal_tooltip": "There are $count $snsName proposals you can vote on.",
"is_actionable_status_badge_tooltip": "You can still vote on this proposal."
},
"actionable_proposals_sign_in": {
"title": "You are not signed in.",
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 @@ -392,6 +392,7 @@ interface I18nVoting {
actionable_proposals: string;
nns_actionable_proposal_tooltip: string;
sns_actionable_proposal_tooltip: string;
is_actionable_status_badge_tooltip: string;
}

interface I18nActionable_proposals_sign_in {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,15 @@ describe("SnsProposalsList", () => {
await runResolvedPromises();

const cards = await po.getProposalCardPos();
expect(await cards[0].getProposalStatusTagPo().hasActionableMark()).toBe(
false
);
expect(await cards[1].getProposalStatusTagPo().hasActionableMark()).toBe(
true
);
expect(await cards[2].getProposalStatusTagPo().hasActionableMark()).toBe(
true
);
expect(
await cards[0].getProposalStatusTagPo().hasActionableStatusBadge()
).toBe(false);
expect(
await cards[1].getProposalStatusTagPo().hasActionableStatusBadge()
).toBe(true);
expect(
await cards[2].getProposalStatusTagPo().hasActionableStatusBadge()
).toBe(true);
});
});
});
10 changes: 7 additions & 3 deletions frontend/src/tests/lib/components/ui/ProposalStatusTag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,15 @@ describe("ProposalStatusTag", () => {
expect(await po.hasStatusClass("failed")).toBe(true);
});

it("should render actionable mark", async () => {
it("should render actionable badge", async () => {
const po = await renderComponent({ status: "open", actionable: true });
expect(await po.hasActionableMark()).toBe(true);
expect(await po.hasActionableStatusBadge()).toBe(true);
expect(await po.getActionableStatusBadgeTooltip().isPresent()).toBe(true);
expect(await po.getActionableStatusBadgeTooltip().getTooltipText()).toBe(
"You can still vote on this proposal."
);

const po2 = await renderComponent({ status: "open" });
expect(await po2.hasActionableMark()).toBe(false);
expect(await po2.hasActionableStatusBadge()).toBe(false);
});
});
4 changes: 2 additions & 2 deletions frontend/src/tests/lib/pages/NnsProposals.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ describe("NnsProposals", () => {
expect(
await (await po.getProposalCardPos())[0]
.getProposalStatusTagPo()
.hasActionableMark()
.hasActionableStatusBadge()
).toEqual(false);
// actionable proposal
expect(
Expand All @@ -176,7 +176,7 @@ describe("NnsProposals", () => {
expect(
await (await po.getProposalCardPos())[1]
.getProposalStatusTagPo()
.hasActionableMark()
.hasActionableStatusBadge()
).toEqual(true);
});

Expand Down
16 changes: 13 additions & 3 deletions frontend/src/tests/page-objects/ProposalStatusTag.page-object.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { UniversalProposalStatus } from "$lib/types/proposals";
import { TooltipPo } from "$tests/page-objects/Tooltip.page-object";
import { BasePageObject } from "$tests/page-objects/base.page-object";
import type { PageObjectElement } from "$tests/types/page-object.types";

Expand All @@ -9,13 +10,22 @@ export class ProposalStatusTagPo extends BasePageObject {
return new ProposalStatusTagPo(element.byTestId(ProposalStatusTagPo.TID));
}

getActionableStatusBadgeElement(): PageObjectElement {
return this.root.byTestId("actionable-status-badge");
}

getActionableStatusBadgeTooltip(): TooltipPo {
return TooltipPo.under(this.getActionableStatusBadgeElement());
}

async hasStatusClass(className: UniversalProposalStatus): Promise<boolean> {
const classNames = await this.root.getClasses();
return classNames.includes(className);
}

async hasActionableMark(): Promise<boolean> {
const classNames = await this.root.getClasses();
return classNames.includes("actionable");
async hasActionableStatusBadge(): Promise<boolean> {
return this.getActionableStatusBadgeElement()
.querySelector(".actionable-status-badge")
.isPresent();
}
}

0 comments on commit 3b9f150

Please sign in to comment.