mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 10:20:58 -06:00
FEATURE: Absolute Numbers in Poll (#28240)
What does this add? =================== This PR adds an extra button to the poll to show the absolute number of people who voted for each option. This button will only be added for the single/multi-select bar chart. Related meta topic: https://meta.discourse.org/t/absolute-numbers-in-polls/32771
This commit is contained in:
parent
a49a6941c6
commit
c8c859762b
@ -35,6 +35,20 @@ const buttonOptionsMap = {
|
||||
icon: "lock",
|
||||
action: "toggleStatus",
|
||||
},
|
||||
showTally: {
|
||||
className: "btn-default show-tally",
|
||||
label: "poll.show-tally.label",
|
||||
title: "poll.show-tally.title",
|
||||
icon: "info",
|
||||
action: "toggleDisplayMode",
|
||||
},
|
||||
showPercentage: {
|
||||
className: "btn-default show-percentage",
|
||||
label: "poll.show-percentage.label",
|
||||
title: "poll.show-percentage.title",
|
||||
icon: "info",
|
||||
action: "toggleDisplayMode",
|
||||
},
|
||||
};
|
||||
|
||||
export default class PollButtonsDropdownComponent extends Component {
|
||||
@ -68,8 +82,15 @@ export default class PollButtonsDropdownComponent extends Component {
|
||||
topicArchived,
|
||||
groupableUserFields,
|
||||
isAutomaticallyClosed,
|
||||
availableDisplayMode,
|
||||
} = this.args;
|
||||
|
||||
if (availableDisplayMode) {
|
||||
const option = { ...buttonOptionsMap[availableDisplayMode] };
|
||||
option.id = option.action;
|
||||
contents.push(option);
|
||||
}
|
||||
|
||||
if (groupableUserFields.length && voters > 0) {
|
||||
const option = { ...buttonOptionsMap.showBreakdown };
|
||||
option.id = option.action;
|
||||
|
@ -76,10 +76,17 @@ export default class PollResultsStandardComponent extends Component {
|
||||
<div class="option">
|
||||
<p>
|
||||
{{#unless @isRankedChoice}}
|
||||
<span class="percentage">{{i18n
|
||||
"number.percent"
|
||||
count=option.percentage
|
||||
}}</span>
|
||||
{{#if @showTally}}
|
||||
<span class="absolute">{{i18n
|
||||
"poll.votes"
|
||||
count=option.votes
|
||||
}}</span>
|
||||
{{else}}
|
||||
<span class="percentage">{{i18n
|
||||
"number.percent"
|
||||
count=option.percentage
|
||||
}}</span>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
<span class="option-text">{{htmlSafe option.html}}</span>
|
||||
</p>
|
||||
|
@ -65,6 +65,7 @@ export default class TabsComponent extends Component {
|
||||
@voters={{@voters}}
|
||||
@votersCount={{@votersCount}}
|
||||
@fetchVoters={{@fetchVoters}}
|
||||
@showTally={{@showTally}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
|
@ -12,7 +12,11 @@ import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
import PollBreakdownModal from "../components/modal/poll-breakdown";
|
||||
import { PIE_CHART_TYPE } from "../components/modal/poll-ui-builder";
|
||||
import {
|
||||
MULTIPLE_POLL_TYPE,
|
||||
PIE_CHART_TYPE,
|
||||
REGULAR_POLL_TYPE,
|
||||
} from "../components/modal/poll-ui-builder";
|
||||
import PollButtonsDropdown from "../components/poll-buttons-dropdown";
|
||||
import PollInfo from "../components/poll-info";
|
||||
import PollOptions from "../components/poll-options";
|
||||
@ -48,6 +52,8 @@ export default class PollComponent extends Component {
|
||||
(this.topicArchived && !this.staffOnly) ||
|
||||
(this.closed && !this.staffOnly);
|
||||
|
||||
@tracked showTally = false;
|
||||
|
||||
checkUserGroups = (user, poll) => {
|
||||
const pollGroups =
|
||||
poll && poll.groups && poll.groups.split(",").map((g) => g.toLowerCase());
|
||||
@ -452,6 +458,17 @@ export default class PollComponent extends Component {
|
||||
return htmlSafe(I18n.t("poll.average_rating", { average }));
|
||||
}
|
||||
|
||||
get availableDisplayMode() {
|
||||
if (
|
||||
!this.showResults ||
|
||||
this.poll.chart_type === PIE_CHART_TYPE ||
|
||||
![REGULAR_POLL_TYPE, MULTIPLE_POLL_TYPE].includes(this.poll.type)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return this.showTally ? "showPercentage" : "showTally";
|
||||
}
|
||||
|
||||
@action
|
||||
updatedVoters() {
|
||||
this.preloadedVoters = this.defaultPreloadedVoters();
|
||||
@ -640,6 +657,12 @@ export default class PollComponent extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
toggleDisplayMode() {
|
||||
this.showTally = !this.showTally;
|
||||
}
|
||||
|
||||
<template>
|
||||
<div
|
||||
{{didUpdate this.updatedVoters @preloadedVoters}}
|
||||
@ -669,6 +692,7 @@ export default class PollComponent extends Component {
|
||||
@votersCount={{this.poll.voters}}
|
||||
@fetchVoters={{this.fetchVoters}}
|
||||
@rankedChoiceOutcome={{this.rankedChoiceOutcome}}
|
||||
@showTally={{this.showTally}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
@ -754,6 +778,7 @@ export default class PollComponent extends Component {
|
||||
@groupableUserFields={{this.groupableUserFields}}
|
||||
@isAutomaticallyClosed={{this.isAutomaticallyClosed}}
|
||||
@dropDownClick={{this.dropDownClick}}
|
||||
@availableDisplayMode={{this.availableDisplayMode}}
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -347,7 +347,9 @@ div.poll-outer {
|
||||
.poll-buttons-dropdown,
|
||||
.export-results,
|
||||
.toggle-status,
|
||||
.show-breakdown {
|
||||
.show-breakdown,
|
||||
.show-tally,
|
||||
.show-percentage {
|
||||
// we want these controls to be separated
|
||||
// from voting controls
|
||||
margin-left: auto;
|
||||
@ -367,7 +369,8 @@ div.poll-outer {
|
||||
}
|
||||
}
|
||||
|
||||
.percentage {
|
||||
.percentage,
|
||||
.absolute {
|
||||
float: right;
|
||||
color: var(--primary-medium);
|
||||
margin-left: 0.25em;
|
||||
|
@ -7,6 +7,9 @@ en:
|
||||
total_votes:
|
||||
one: "total vote"
|
||||
other: "total votes"
|
||||
votes:
|
||||
one: "%{count} vote"
|
||||
other: "%{count} votes"
|
||||
|
||||
average_rating: "Average rating: <strong>%{average}</strong>."
|
||||
|
||||
@ -46,6 +49,14 @@ en:
|
||||
title: "Display the poll results"
|
||||
label: "Results"
|
||||
|
||||
show-tally:
|
||||
title: "Show voting results by number of votes"
|
||||
label: "Display tally"
|
||||
|
||||
show-percentage:
|
||||
title: "Show voting results as percentage"
|
||||
label: "Display as percentage"
|
||||
|
||||
remove-vote:
|
||||
title: "Remove your vote"
|
||||
label: "Undo vote"
|
||||
|
@ -37,7 +37,7 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
|
||||
|
||||
await click(".widget-dropdown-header");
|
||||
|
||||
assert.strictEqual(count("li.dropdown-menu__item"), 2);
|
||||
assert.dom("li.dropdown-menu__item").exists({ count: 2 });
|
||||
|
||||
assert.strictEqual(
|
||||
query("li.dropdown-menu__item span").textContent.trim(),
|
||||
@ -46,6 +46,43 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
|
||||
);
|
||||
});
|
||||
|
||||
test("Renders a show-tally button when poll is a bar chart", async function (assert) {
|
||||
this.setProperties({
|
||||
closed: false,
|
||||
voters: 2,
|
||||
isStaff: false,
|
||||
isMe: false,
|
||||
topicArchived: false,
|
||||
groupableUserFields: ["stuff"],
|
||||
isAutomaticallyClosed: false,
|
||||
dropDownClick: () => {},
|
||||
availableDisplayMode: "showTally",
|
||||
});
|
||||
|
||||
await render(hbs`<PollButtonsDropdown
|
||||
@closed={{this.closed}}
|
||||
@voters={{this.voters}}
|
||||
@isStaff={{this.isStaff}}
|
||||
@isMe={{this.isMe}}
|
||||
@topicArchived={{this.topicArchived}}
|
||||
@groupableUserFields={{this.groupableUserFields}}
|
||||
@isAutomaticallyClosed={{this.isAutomaticallyClosed}}
|
||||
@dropDownClick={{this.dropDownClick}}
|
||||
@availableDisplayMode={{this.availableDisplayMode}}
|
||||
/>`);
|
||||
|
||||
await click(".widget-dropdown-header");
|
||||
|
||||
assert.strictEqual(count("li.dropdown-menu__item"), 2);
|
||||
|
||||
assert
|
||||
.dom(query("li.dropdown-menu__item span"))
|
||||
.hasText(
|
||||
I18n.t("poll.show-tally.label"),
|
||||
"displays the show absolute button"
|
||||
);
|
||||
});
|
||||
|
||||
test("Renders a single button when there is only one authorised action", async function (assert) {
|
||||
this.setProperties({
|
||||
closed: false,
|
||||
|
@ -3,6 +3,7 @@ import hbs from "htmlbars-inline-precompile";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { exists, queryAll } from "discourse/tests/helpers/qunit-helpers";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
const TWO_OPTIONS = [
|
||||
{ id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 5, rank: 0 },
|
||||
@ -160,4 +161,40 @@ module("Poll | Component | poll-results-standard", function (hooks) {
|
||||
"b"
|
||||
);
|
||||
});
|
||||
|
||||
test("options in ascending order, showing absolute vote number", async function (assert) {
|
||||
this.setProperties({
|
||||
options: FIVE_OPTIONS,
|
||||
pollName: "Five Multi Option Poll",
|
||||
pollType: "multiple",
|
||||
postId: 123,
|
||||
vote: ["1ddc47be0d2315b9711ee8526ca9d83f"],
|
||||
voters: PRELOADEDVOTERS,
|
||||
votersCount: 12,
|
||||
fetchVoters: () => {},
|
||||
showTally: true,
|
||||
});
|
||||
|
||||
await render(hbs`<PollResultsStandard
|
||||
@options={{this.options}}
|
||||
@pollName={{this.pollName}}
|
||||
@pollType={{this.pollType}}
|
||||
@postId={{this.postId}}
|
||||
@vote={{this.vote}}
|
||||
@voters={{this.voters}}
|
||||
@votersCount={{this.votersCount}}
|
||||
@fetchVoters={{this.fetchVoters}}
|
||||
@showTally={{this.showTally}}
|
||||
/>`);
|
||||
|
||||
let percentages = queryAll(".option .absolute");
|
||||
assert.dom(percentages[0]).hasText(I18n.t("poll.votes", { count: 5 }));
|
||||
assert.dom(percentages[1]).hasText(I18n.t("poll.votes", { count: 4 }));
|
||||
assert.dom(percentages[2]).hasText(I18n.t("poll.votes", { count: 2 }));
|
||||
assert.dom(percentages[3]).hasText(I18n.t("poll.votes", { count: 1 }));
|
||||
|
||||
assert.dom(queryAll(".option")[3].querySelectorAll("span")[1]).hasText("a");
|
||||
assert.dom(percentages[4]).hasText(I18n.t("poll.votes", { count: 1 }));
|
||||
assert.dom(queryAll(".option")[4].querySelectorAll("span")[1]).hasText("b");
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user