mirror of
https://github.com/discourse/discourse.git
synced 2025-05-23 16:31:09 +08:00
FEATURE: Poll breakdown 2.0 (#10345)
The poll breakdown modal replaces the grouped pie charts feature. Includes: * MODAL: Untangle `onSelectPanel` Previously modal-tab component would call on click the onSelectPanel callback with itself (modal-tab) as `this` which severely limited its usefulness. Now showModal binds the callback to its controller. "The PR includes a fix/change to d-modal (b7f6ec6) that hasn't been extracted to a separate PR because it's not currently possible to test a change like this in abstract, i.e. with dynamically created controllers/components in tests. The percentage/count toggle test for the poll breakdown feature is essentially a test for that d-modal modification."
This commit is contained in:
@ -1,18 +1,18 @@
|
||||
import I18n from "I18n";
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
import { h } from "virtual-dom";
|
||||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
import RawHtml from "discourse/widgets/raw-html";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import evenRound from "discourse/plugins/poll/lib/even-round";
|
||||
import { avatarFor } from "discourse/widgets/post";
|
||||
import round from "discourse/lib/round";
|
||||
import { relativeAge } from "discourse/lib/formatter";
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { getColors } from "../lib/chart-colors";
|
||||
import { classify } from "@ember/string";
|
||||
import { PIE_CHART_TYPE } from "../controllers/poll-ui-builder";
|
||||
import round from "discourse/lib/round";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
import { avatarFor } from "discourse/widgets/post";
|
||||
import RawHtml from "discourse/widgets/raw-html";
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
import { iconNode } from "discourse-common/lib/icon-library";
|
||||
import { PIE_CHART_TYPE } from "discourse/plugins/poll/controllers/poll-ui-builder";
|
||||
import { getColors } from "discourse/plugins/poll/lib/chart-colors";
|
||||
import evenRound from "discourse/plugins/poll/lib/even-round";
|
||||
|
||||
function optionHtml(option) {
|
||||
const $node = $(`<span>${option.html}</span>`);
|
||||
@ -453,120 +453,6 @@ createWidget("discourse-poll-info", {
|
||||
}
|
||||
});
|
||||
|
||||
function transformUserFieldToLabel(fieldName) {
|
||||
let transformed = fieldName.split("_").filter(Boolean);
|
||||
if (transformed.length > 1) {
|
||||
transformed[0] = classify(transformed[0]);
|
||||
}
|
||||
return transformed.join(" ");
|
||||
}
|
||||
|
||||
createWidget("discourse-poll-grouped-pies", {
|
||||
tagName: "div.poll-grouped-pies",
|
||||
buildAttributes(attrs) {
|
||||
return {
|
||||
id: `poll-results-grouped-pie-charts-${attrs.id}`
|
||||
};
|
||||
},
|
||||
|
||||
html(attrs) {
|
||||
const fields = Object.assign({}, attrs.groupableUserFields);
|
||||
const fieldSelectId = `field-select-${attrs.id}`;
|
||||
attrs.groupedBy = attrs.groupedBy || fields[0];
|
||||
|
||||
let contents = [];
|
||||
|
||||
const btn = this.attach("button", {
|
||||
className: "btn-default poll-group-by-toggle",
|
||||
label: "poll.ungroup-results.label",
|
||||
title: "poll.ungroup-results.title",
|
||||
icon: "far-eye-slash",
|
||||
action: "toggleGroupedPieCharts"
|
||||
});
|
||||
const select = h(
|
||||
`select#${fieldSelectId}.poll-group-by-selector`,
|
||||
{ value: attrs.groupBy },
|
||||
attrs.groupableUserFields.map(field => {
|
||||
return h("option", { value: field }, transformUserFieldToLabel(field));
|
||||
})
|
||||
);
|
||||
contents.push(h("div.poll-grouped-pies-controls", [btn, select]));
|
||||
|
||||
ajax("/polls/grouped_poll_results.json", {
|
||||
data: {
|
||||
post_id: attrs.post.id,
|
||||
poll_name: attrs.poll.name,
|
||||
user_field_name: attrs.groupedBy
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error) {
|
||||
popupAjaxError(error);
|
||||
} else {
|
||||
bootbox.alert(I18n.t("poll.error_while_fetching_voters"));
|
||||
}
|
||||
})
|
||||
.then(result => {
|
||||
let groupBySelect = document.getElementById(fieldSelectId);
|
||||
if (!groupBySelect) return;
|
||||
|
||||
groupBySelect.value = attrs.groupedBy;
|
||||
const parent = document.getElementById(
|
||||
`poll-results-grouped-pie-charts-${attrs.id}`
|
||||
);
|
||||
|
||||
for (
|
||||
let chartIdx = 0;
|
||||
chartIdx < result.grouped_results.length;
|
||||
chartIdx++
|
||||
) {
|
||||
const data = result.grouped_results[chartIdx].options.mapBy("votes");
|
||||
const labels = result.grouped_results[chartIdx].options.mapBy("html");
|
||||
const chartConfig = pieChartConfig(data, labels, {
|
||||
aspectRatio: 1.2
|
||||
});
|
||||
const canvasId = `pie-${attrs.id}-${chartIdx}`;
|
||||
let el = document.querySelector(`#${canvasId}`);
|
||||
if (!el) {
|
||||
const container = document.createElement("div");
|
||||
container.classList.add("poll-grouped-pie-container");
|
||||
|
||||
const label = document.createElement("label");
|
||||
label.classList.add("poll-pie-label");
|
||||
label.textContent = result.grouped_results[chartIdx].group;
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.classList.add(`poll-grouped-pie-${attrs.id}`);
|
||||
canvas.id = canvasId;
|
||||
|
||||
container.appendChild(label);
|
||||
container.appendChild(canvas);
|
||||
parent.appendChild(container);
|
||||
// eslint-disable-next-line
|
||||
new Chart(canvas.getContext("2d"), chartConfig);
|
||||
} else {
|
||||
// eslint-disable-next-line
|
||||
Chart.helpers.each(Chart.instances, function(instance) {
|
||||
if (instance.chart.canvas.id === canvasId && el.$chartjs) {
|
||||
instance.destroy();
|
||||
// eslint-disable-next-line
|
||||
new Chart(el.getContext("2d"), chartConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return contents;
|
||||
},
|
||||
|
||||
click(e) {
|
||||
let select = $(e.target).closest("select");
|
||||
if (select.length) {
|
||||
this.sendWidgetAction("refreshCharts", select[0].value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function clearPieChart(id) {
|
||||
let el = document.querySelector(`#poll-results-chart-${id}`);
|
||||
el && el.parentNode.removeChild(el);
|
||||
@ -607,27 +493,23 @@ createWidget("discourse-poll-pie-chart", {
|
||||
return contents;
|
||||
}
|
||||
|
||||
let btn;
|
||||
let chart;
|
||||
if (attrs.groupResults && attrs.groupableUserFields.length > 0) {
|
||||
chart = this.attach("discourse-poll-grouped-pies", attrs);
|
||||
clearPieChart(attrs.id);
|
||||
} else {
|
||||
if (attrs.groupableUserFields.length) {
|
||||
btn = this.attach("button", {
|
||||
className: "btn-default poll-group-by-toggle",
|
||||
label: "poll.group-results.label",
|
||||
title: "poll.group-results.title",
|
||||
icon: "far-eye",
|
||||
action: "toggleGroupedPieCharts"
|
||||
});
|
||||
}
|
||||
if (attrs.groupableUserFields.length) {
|
||||
const button = this.attach("button", {
|
||||
className: "btn-default poll-show-breakdown",
|
||||
label: "poll.group-results.label",
|
||||
title: "poll.group-results.title",
|
||||
icon: "far-eye",
|
||||
action: "showBreakdown"
|
||||
});
|
||||
|
||||
chart = this.attach("discourse-poll-pie-canvas", attrs);
|
||||
contents.push(button);
|
||||
}
|
||||
contents.push(btn);
|
||||
|
||||
const chart = this.attach("discourse-poll-pie-canvas", attrs);
|
||||
contents.push(chart);
|
||||
|
||||
contents.push(h(`div#poll-results-legend-${attrs.id}.pie-chart-legends`));
|
||||
|
||||
return contents;
|
||||
}
|
||||
});
|
||||
@ -1072,19 +954,13 @@ export default createWidget("discourse-poll", {
|
||||
});
|
||||
},
|
||||
|
||||
toggleGroupedPieCharts() {
|
||||
this.attrs.groupResults = !this.attrs.groupResults;
|
||||
},
|
||||
|
||||
refreshCharts(newGroupedByValue) {
|
||||
let el = document.getElementById(
|
||||
`poll-results-grouped-pie-charts-${this.attrs.id}`
|
||||
);
|
||||
Array.from(el.getElementsByClassName("poll-grouped-pie-container")).forEach(
|
||||
container => {
|
||||
el.removeChild(container);
|
||||
}
|
||||
);
|
||||
this.attrs.groupedBy = newGroupedByValue;
|
||||
showBreakdown() {
|
||||
showModal("poll-breakdown", {
|
||||
model: this.attrs,
|
||||
panels: [
|
||||
{ id: "percentage", title: "poll.breakdown.percentage" },
|
||||
{ id: "count", title: "poll.breakdown.count" }
|
||||
]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user