mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
UX: Improve poll builder UI (#12549)
* Improve poll validation * Redesign poll builder * Group all advanced settings under a new section
This commit is contained in:
@@ -1,234 +1,152 @@
|
|||||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
|
||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject, { action } from "@ember/object";
|
||||||
|
import { gt, or } from "@ember/object/computed";
|
||||||
|
import { next } from "@ember/runloop";
|
||||||
|
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
||||||
|
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
|
||||||
export const BAR_CHART_TYPE = "bar";
|
export const BAR_CHART_TYPE = "bar";
|
||||||
export const PIE_CHART_TYPE = "pie";
|
export const PIE_CHART_TYPE = "pie";
|
||||||
|
|
||||||
export default Controller.extend({
|
export const REGULAR_POLL_TYPE = "regular";
|
||||||
regularPollType: "regular",
|
export const NUMBER_POLL_TYPE = "number";
|
||||||
numberPollType: "number",
|
export const MULTIPLE_POLL_TYPE = "multiple";
|
||||||
multiplePollType: "multiple",
|
|
||||||
|
|
||||||
alwaysPollResult: "always",
|
const ALWAYS_POLL_RESULT = "always";
|
||||||
votePollResult: "on_vote",
|
const VOTE_POLL_RESULT = "on_vote";
|
||||||
closedPollResult: "on_close",
|
const CLOSED_POLL_RESULT = "on_close";
|
||||||
staffPollResult: "staff_only",
|
const STAFF_POLL_RESULT = "staff_only";
|
||||||
pollChartTypes: [
|
|
||||||
{
|
|
||||||
name: I18n.t("poll.ui_builder.poll_chart_type.bar"),
|
|
||||||
value: BAR_CHART_TYPE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: I18n.t("poll.ui_builder.poll_chart_type.pie"),
|
|
||||||
value: PIE_CHART_TYPE,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
pollType: null,
|
export default Controller.extend(ModalFunctionality, {
|
||||||
pollResult: null,
|
showAdvanced: false,
|
||||||
pollTitle: null,
|
|
||||||
|
|
||||||
init() {
|
pollType: REGULAR_POLL_TYPE,
|
||||||
this._super(...arguments);
|
pollTitle: "",
|
||||||
this._setupPoll();
|
pollOptions: null,
|
||||||
|
pollMin: 1,
|
||||||
|
pollMax: 2,
|
||||||
|
pollStep: 1,
|
||||||
|
pollGroups: null,
|
||||||
|
pollAutoClose: null,
|
||||||
|
pollResult: ALWAYS_POLL_RESULT,
|
||||||
|
chartType: BAR_CHART_TYPE,
|
||||||
|
publicPoll: null,
|
||||||
|
|
||||||
|
onShow() {
|
||||||
|
this.setProperties({
|
||||||
|
showAdvanced: false,
|
||||||
|
pollType: REGULAR_POLL_TYPE,
|
||||||
|
pollTitle: null,
|
||||||
|
pollOptions: [EmberObject.create({ value: "" })],
|
||||||
|
pollMin: 1,
|
||||||
|
pollMax: 2,
|
||||||
|
pollStep: 1,
|
||||||
|
pollGroups: null,
|
||||||
|
pollAutoClose: null,
|
||||||
|
pollResult: ALWAYS_POLL_RESULT,
|
||||||
|
chartType: BAR_CHART_TYPE,
|
||||||
|
publicPoll: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("regularPollType", "numberPollType", "multiplePollType")
|
@discourseComputed
|
||||||
pollTypes(regularPollType, numberPollType, multiplePollType) {
|
pollResults() {
|
||||||
return [
|
const options = [
|
||||||
{
|
|
||||||
name: I18n.t("poll.ui_builder.poll_type.regular"),
|
|
||||||
value: regularPollType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: I18n.t("poll.ui_builder.poll_type.number"),
|
|
||||||
value: numberPollType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: I18n.t("poll.ui_builder.poll_type.multiple"),
|
|
||||||
value: multiplePollType,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("chartType", "pollType", "numberPollType")
|
|
||||||
isPie(chartType, pollType, numberPollType) {
|
|
||||||
return pollType !== numberPollType && chartType === PIE_CHART_TYPE;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed(
|
|
||||||
"alwaysPollResult",
|
|
||||||
"votePollResult",
|
|
||||||
"closedPollResult",
|
|
||||||
"staffPollResult"
|
|
||||||
)
|
|
||||||
pollResults(
|
|
||||||
alwaysPollResult,
|
|
||||||
votePollResult,
|
|
||||||
closedPollResult,
|
|
||||||
staffPollResult
|
|
||||||
) {
|
|
||||||
let options = [
|
|
||||||
{
|
{
|
||||||
name: I18n.t("poll.ui_builder.poll_result.always"),
|
name: I18n.t("poll.ui_builder.poll_result.always"),
|
||||||
value: alwaysPollResult,
|
value: ALWAYS_POLL_RESULT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: I18n.t("poll.ui_builder.poll_result.vote"),
|
name: I18n.t("poll.ui_builder.poll_result.vote"),
|
||||||
value: votePollResult,
|
value: VOTE_POLL_RESULT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: I18n.t("poll.ui_builder.poll_result.closed"),
|
name: I18n.t("poll.ui_builder.poll_result.closed"),
|
||||||
value: closedPollResult,
|
value: CLOSED_POLL_RESULT,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.get("currentUser.staff")) {
|
if (this.get("currentUser.staff")) {
|
||||||
options.push({
|
options.push({
|
||||||
name: I18n.t("poll.ui_builder.poll_result.staff"),
|
name: I18n.t("poll.ui_builder.poll_result.staff"),
|
||||||
value: staffPollResult,
|
value: STAFF_POLL_RESULT,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("pollType")
|
||||||
|
isRegular(pollType) {
|
||||||
|
return pollType === REGULAR_POLL_TYPE;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("pollType")
|
||||||
|
isNumber(pollType) {
|
||||||
|
return pollType === NUMBER_POLL_TYPE;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("pollType")
|
||||||
|
isMultiple(pollType) {
|
||||||
|
return pollType === MULTIPLE_POLL_TYPE;
|
||||||
|
},
|
||||||
|
|
||||||
|
showNumber: or("showAdvanced", "isNumber"),
|
||||||
|
|
||||||
|
@discourseComputed("pollOptions.@each.value")
|
||||||
|
pollOptionsCount(pollOptions) {
|
||||||
|
return pollOptions.filter((option) => option.value.length > 0).length;
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed("site.groups")
|
@discourseComputed("site.groups")
|
||||||
siteGroups(groups) {
|
siteGroups(groups) {
|
||||||
return groups
|
// prevents group "everyone" to be listed
|
||||||
.map((g) => {
|
return groups.filter((g) => g.id !== 0);
|
||||||
// prevents group "everyone" to be listed
|
|
||||||
if (g.id !== 0) {
|
|
||||||
return { name: g.name };
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(Boolean);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("pollType", "regularPollType")
|
@discourseComputed("chartType", "pollType")
|
||||||
isRegular(pollType, regularPollType) {
|
isPie(chartType, pollType) {
|
||||||
return pollType === regularPollType;
|
return pollType !== NUMBER_POLL_TYPE && chartType === PIE_CHART_TYPE;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("pollType", "pollOptionsCount", "multiplePollType")
|
canRemoveOption: gt("pollOptions.length", 1),
|
||||||
isMultiple(pollType, count, multiplePollType) {
|
|
||||||
return pollType === multiplePollType && count > 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("pollType", "numberPollType")
|
|
||||||
isNumber(pollType, numberPollType) {
|
|
||||||
return pollType === numberPollType;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("isRegular")
|
|
||||||
showMinMax(isRegular) {
|
|
||||||
return !isRegular;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("pollOptions")
|
|
||||||
pollOptionsCount(pollOptions) {
|
|
||||||
if (pollOptions.length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let length = 0;
|
|
||||||
|
|
||||||
pollOptions.split("\n").forEach((option) => {
|
|
||||||
if (option.length !== 0) {
|
|
||||||
length += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return length;
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes("pollType", "pollOptionsCount")
|
@observes("pollType", "pollOptionsCount")
|
||||||
_setPollMax() {
|
_setPollMinMax() {
|
||||||
const isMultiple = this.isMultiple;
|
if (this.isMultiple) {
|
||||||
const isNumber = this.isNumber;
|
if (
|
||||||
if (!isMultiple && !isNumber) {
|
this.pollMin >= this.pollMax ||
|
||||||
return;
|
this.pollMin >= this.pollOptionsCount
|
||||||
}
|
) {
|
||||||
|
this.set("pollMin", this.pollOptionsCount > 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (isMultiple) {
|
if (
|
||||||
this.set("pollMax", this.pollOptionsCount);
|
this.pollMin >= this.pollMax ||
|
||||||
} else if (isNumber) {
|
this.pollMax > this.pollOptionsCount
|
||||||
|
) {
|
||||||
|
this.set("pollMax", Math.min(this.pollMin + 1, this.pollOptionsCount));
|
||||||
|
}
|
||||||
|
} else if (this.isNumber) {
|
||||||
this.set("pollMax", this.siteSettings.poll_maximum_options);
|
this.set("pollMax", this.siteSettings.poll_maximum_options);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("isRegular", "isMultiple", "isNumber", "pollOptionsCount")
|
|
||||||
pollMinOptions(isRegular, isMultiple, isNumber, count) {
|
|
||||||
if (isRegular) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMultiple) {
|
|
||||||
return this._comboboxOptions(1, count + 1);
|
|
||||||
} else if (isNumber) {
|
|
||||||
return this._comboboxOptions(
|
|
||||||
1,
|
|
||||||
this.siteSettings.poll_maximum_options + 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"isRegular",
|
|
||||||
"isMultiple",
|
|
||||||
"isNumber",
|
|
||||||
"pollOptionsCount",
|
|
||||||
"pollMin",
|
|
||||||
"pollStep"
|
|
||||||
)
|
|
||||||
pollMaxOptions(isRegular, isMultiple, isNumber, count, pollMin, pollStep) {
|
|
||||||
if (isRegular) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pollMinInt = parseInt(pollMin, 10) || 1;
|
|
||||||
|
|
||||||
if (isMultiple) {
|
|
||||||
return this._comboboxOptions(pollMinInt + 1, count + 1);
|
|
||||||
} else if (isNumber) {
|
|
||||||
let pollStepInt = parseInt(pollStep, 10);
|
|
||||||
if (pollStepInt < 1) {
|
|
||||||
pollStepInt = 1;
|
|
||||||
}
|
|
||||||
return this._comboboxOptions(
|
|
||||||
pollMinInt + 1,
|
|
||||||
pollMinInt + this.siteSettings.poll_maximum_options * pollStepInt
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("isNumber", "pollMax")
|
|
||||||
pollStepOptions(isNumber, pollMax) {
|
|
||||||
if (!isNumber) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return this._comboboxOptions(1, (parseInt(pollMax, 10) || 1) + 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed(
|
|
||||||
"isNumber",
|
|
||||||
"showMinMax",
|
|
||||||
"pollType",
|
"pollType",
|
||||||
"pollResult",
|
"pollResult",
|
||||||
"publicPoll",
|
"publicPoll",
|
||||||
"pollTitle",
|
"pollTitle",
|
||||||
"pollOptions",
|
"pollOptions.@each.value",
|
||||||
"pollMin",
|
"pollMin",
|
||||||
"pollMax",
|
"pollMax",
|
||||||
"pollStep",
|
"pollStep",
|
||||||
"pollGroups",
|
"pollGroups",
|
||||||
"autoClose",
|
"pollAutoClose",
|
||||||
"chartType",
|
"chartType"
|
||||||
"date",
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
pollOutput(
|
pollOutput(
|
||||||
isNumber,
|
|
||||||
showMinMax,
|
|
||||||
pollType,
|
pollType,
|
||||||
pollResult,
|
pollResult,
|
||||||
publicPoll,
|
publicPoll,
|
||||||
@@ -238,10 +156,8 @@ export default Controller.extend({
|
|||||||
pollMax,
|
pollMax,
|
||||||
pollStep,
|
pollStep,
|
||||||
pollGroups,
|
pollGroups,
|
||||||
autoClose,
|
pollAutoClose,
|
||||||
chartType,
|
chartType
|
||||||
date,
|
|
||||||
time
|
|
||||||
) {
|
) {
|
||||||
let pollHeader = "[poll";
|
let pollHeader = "[poll";
|
||||||
let output = "";
|
let output = "";
|
||||||
@@ -265,32 +181,26 @@ export default Controller.extend({
|
|||||||
if (pollResult) {
|
if (pollResult) {
|
||||||
pollHeader += ` results=${pollResult}`;
|
pollHeader += ` results=${pollResult}`;
|
||||||
}
|
}
|
||||||
if (pollMin && showMinMax) {
|
if (pollMin && pollType !== REGULAR_POLL_TYPE) {
|
||||||
pollHeader += ` min=${pollMin}`;
|
pollHeader += ` min=${pollMin}`;
|
||||||
}
|
}
|
||||||
if (pollMax) {
|
if (pollMax && pollType !== REGULAR_POLL_TYPE) {
|
||||||
pollHeader += ` max=${pollMax}`;
|
pollHeader += ` max=${pollMax}`;
|
||||||
}
|
}
|
||||||
if (isNumber) {
|
if (pollType === NUMBER_POLL_TYPE) {
|
||||||
pollHeader += ` step=${step}`;
|
pollHeader += ` step=${step}`;
|
||||||
}
|
}
|
||||||
if (publicPoll) {
|
if (publicPoll) {
|
||||||
pollHeader += ` public=true`;
|
pollHeader += ` public=true`;
|
||||||
}
|
}
|
||||||
if (chartType && pollType !== "number") {
|
if (chartType && pollType !== NUMBER_POLL_TYPE) {
|
||||||
pollHeader += ` chartType=${chartType}`;
|
pollHeader += ` chartType=${chartType}`;
|
||||||
}
|
}
|
||||||
if (pollGroups && pollGroups.length > 0) {
|
if (pollGroups && pollGroups.length > 0) {
|
||||||
pollHeader += ` groups=${pollGroups}`;
|
pollHeader += ` groups=${pollGroups}`;
|
||||||
}
|
}
|
||||||
if (autoClose) {
|
if (pollAutoClose) {
|
||||||
let closeDate = moment(
|
pollHeader += ` close=${pollAutoClose.toISOString()}`;
|
||||||
date + " " + time,
|
|
||||||
"YYYY-MM-DD HH:mm"
|
|
||||||
).toISOString();
|
|
||||||
if (closeDate) {
|
|
||||||
pollHeader += ` close=${closeDate}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pollHeader += "]";
|
pollHeader += "]";
|
||||||
@@ -300,10 +210,10 @@ export default Controller.extend({
|
|||||||
output += `# ${pollTitle.trim()}\n`;
|
output += `# ${pollTitle.trim()}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollOptions.length > 0 && !isNumber) {
|
if (pollOptions.length > 0 && pollType !== NUMBER_POLL_TYPE) {
|
||||||
pollOptions.split("\n").forEach((option) => {
|
pollOptions.forEach((option) => {
|
||||||
if (option.length !== 0) {
|
if (option.value.length > 0) {
|
||||||
output += `* ${option}\n`;
|
output += `* ${option.value.trim()}\n`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -312,55 +222,11 @@ export default Controller.extend({
|
|||||||
return output;
|
return output;
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed("isNumber", "pollOptionsCount")
|
||||||
"pollOptionsCount",
|
minNumOfOptionsValidation(isNumber, pollOptionsCount) {
|
||||||
"isRegular",
|
|
||||||
"isMultiple",
|
|
||||||
"isNumber",
|
|
||||||
"pollMin",
|
|
||||||
"pollMax"
|
|
||||||
)
|
|
||||||
disableInsert(count, isRegular, isMultiple, isNumber, pollMin, pollMax) {
|
|
||||||
return (
|
|
||||||
(isRegular && count < 1) ||
|
|
||||||
(isMultiple && count < pollMin && pollMin >= pollMax) ||
|
|
||||||
(isNumber ? false : count < 1)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("pollMin", "pollMax")
|
|
||||||
minMaxValueValidation(pollMin, pollMax) {
|
|
||||||
let options = { ok: true };
|
let options = { ok: true };
|
||||||
|
|
||||||
if (pollMin >= pollMax) {
|
if (!isNumber && pollOptionsCount === 0) {
|
||||||
options = {
|
|
||||||
failed: true,
|
|
||||||
reason: I18n.t("poll.ui_builder.help.invalid_values"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return EmberObject.create(options);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("pollStep")
|
|
||||||
minStepValueValidation(pollStep) {
|
|
||||||
let options = { ok: true };
|
|
||||||
|
|
||||||
if (pollStep < 1) {
|
|
||||||
options = {
|
|
||||||
failed: true,
|
|
||||||
reason: I18n.t("poll.ui_builder.help.min_step_value"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return EmberObject.create(options);
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("disableInsert")
|
|
||||||
minNumOfOptionsValidation(disableInsert) {
|
|
||||||
let options = { ok: true };
|
|
||||||
|
|
||||||
if (disableInsert) {
|
|
||||||
options = {
|
options = {
|
||||||
failed: true,
|
failed: true,
|
||||||
reason: I18n.t("poll.ui_builder.help.options_count"),
|
reason: I18n.t("poll.ui_builder.help.options_count"),
|
||||||
@@ -370,6 +236,81 @@ export default Controller.extend({
|
|||||||
return EmberObject.create(options);
|
return EmberObject.create(options);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed("pollOptions.@each.value")
|
||||||
|
showMinNumOfOptionsValidation(pollOptions) {
|
||||||
|
return pollOptions.length !== 1 || pollOptions[0].value !== "";
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed(
|
||||||
|
"isMultiple",
|
||||||
|
"pollOptionsCount",
|
||||||
|
"isNumber",
|
||||||
|
"pollMin",
|
||||||
|
"pollMax"
|
||||||
|
)
|
||||||
|
minMaxValueValidation(
|
||||||
|
isMultiple,
|
||||||
|
pollOptionsCount,
|
||||||
|
isNumber,
|
||||||
|
pollMin,
|
||||||
|
pollMax
|
||||||
|
) {
|
||||||
|
pollMin = parseInt(pollMin, 10) || 0;
|
||||||
|
pollMax = parseInt(pollMax, 10) || 0;
|
||||||
|
|
||||||
|
const fail = {
|
||||||
|
failed: true,
|
||||||
|
reason: I18n.t("poll.ui_builder.help.invalid_values"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isMultiple) {
|
||||||
|
if (
|
||||||
|
pollMin > pollMax ||
|
||||||
|
pollMin < 0 ||
|
||||||
|
(pollOptionsCount > 0 && pollMax > pollOptionsCount)
|
||||||
|
) {
|
||||||
|
return EmberObject.create(fail);
|
||||||
|
}
|
||||||
|
} else if (isNumber) {
|
||||||
|
if (pollMin >= pollMax) {
|
||||||
|
return EmberObject.create(fail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmberObject.create({ ok: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("isNumber", "pollStep")
|
||||||
|
minStepValueValidation(isNumber, pollStep) {
|
||||||
|
let options = { ok: true };
|
||||||
|
|
||||||
|
if (isNumber && pollStep < 1) {
|
||||||
|
options = {
|
||||||
|
failed: true,
|
||||||
|
reason: I18n.t("poll.ui_builder.help.min_step_value"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return EmberObject.create(options);
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed(
|
||||||
|
"minMaxValueValidation",
|
||||||
|
"minStepValueValidation",
|
||||||
|
"minNumOfOptionsValidation"
|
||||||
|
)
|
||||||
|
disableInsert(
|
||||||
|
minMaxValueValidation,
|
||||||
|
minStepValueValidation,
|
||||||
|
minNumOfOptionsValidation
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
!minMaxValueValidation.ok ||
|
||||||
|
!minStepValueValidation.ok ||
|
||||||
|
!minNumOfOptionsValidation.ok
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
_comboboxOptions(startIndex, endIndex) {
|
_comboboxOptions(startIndex, endIndex) {
|
||||||
return [...Array(endIndex - startIndex).keys()].map((number) => ({
|
return [...Array(endIndex - startIndex).keys()].map((number) => ({
|
||||||
value: number + startIndex,
|
value: number + startIndex,
|
||||||
@@ -377,29 +318,45 @@ export default Controller.extend({
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
_setupPoll() {
|
@action
|
||||||
this.setProperties({
|
insertPoll() {
|
||||||
pollType: this.get("pollTypes.firstObject.value"),
|
this.toolbarEvent.addText(this.pollOutput);
|
||||||
publicPoll: false,
|
this.send("closeModal");
|
||||||
pollOptions: "",
|
|
||||||
pollMin: 1,
|
|
||||||
pollMax: null,
|
|
||||||
pollStep: 1,
|
|
||||||
autoClose: false,
|
|
||||||
chartType: BAR_CHART_TYPE,
|
|
||||||
pollResult: this.alwaysPollResult,
|
|
||||||
pollGroups: null,
|
|
||||||
pollTitle: null,
|
|
||||||
date: moment().add(1, "day").format("YYYY-MM-DD"),
|
|
||||||
time: moment().add(1, "hour").format("HH:mm"),
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
@action
|
||||||
insertPoll() {
|
toggleAdvanced() {
|
||||||
this.toolbarEvent.addText(this.pollOutput);
|
this.toggleProperty("showAdvanced");
|
||||||
this.send("closeModal");
|
},
|
||||||
this._setupPoll();
|
|
||||||
},
|
@action
|
||||||
|
addOption(beforeOption, value, e) {
|
||||||
|
if (value !== "") {
|
||||||
|
const idx = this.pollOptions.indexOf(beforeOption) + 1;
|
||||||
|
const option = EmberObject.create({ value: "" });
|
||||||
|
this.pollOptions.insertAt(idx, option);
|
||||||
|
|
||||||
|
let lastOptionIdx = 0;
|
||||||
|
this.pollOptions.forEach((o) => o.set("idx", lastOptionIdx++));
|
||||||
|
|
||||||
|
next(() => {
|
||||||
|
const pollOptions = document.getElementsByClassName("poll-options");
|
||||||
|
if (pollOptions) {
|
||||||
|
const inputs = pollOptions[0].getElementsByTagName("input");
|
||||||
|
if (option.idx < inputs.length) {
|
||||||
|
inputs[option.idx].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
removeOption(option) {
|
||||||
|
this.pollOptions.removeObject(option);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,129 +1,158 @@
|
|||||||
{{#d-modal-body title="poll.ui_builder.title" class="poll-ui-builder"}}
|
{{#d-modal-body title="poll.ui_builder.title" class="poll-ui-builder"}}
|
||||||
<form class="poll-ui-builder-form form-horizontal">
|
<div class="input-group poll-type">
|
||||||
<div class="options">
|
<a href {{action (mut pollType) "regular"}} class="poll-type-value {{if isRegular "active"}}">
|
||||||
<div class="input-group poll-select">
|
{{i18n "poll.ui_builder.poll_type.regular"}}
|
||||||
<label class="input-group-label">{{i18n "poll.ui_builder.poll_type.label"}}</label>
|
</a>
|
||||||
{{combo-box
|
|
||||||
content=pollTypes
|
|
||||||
value=pollType
|
|
||||||
valueProperty="value"
|
|
||||||
class="poll-type"
|
|
||||||
onChange=(action (mut pollType))
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group poll-select">
|
<a href {{action (mut pollType) "multiple"}} class="poll-type-value {{if isMultiple "active"}}">
|
||||||
<label class="input-group-label">{{i18n "poll.ui_builder.poll_result.label"}}</label>
|
{{i18n "poll.ui_builder.poll_type.multiple"}}
|
||||||
{{combo-box
|
</a>
|
||||||
content=pollResults
|
|
||||||
value=pollResult
|
|
||||||
class="poll-result"
|
|
||||||
valueProperty="value"
|
|
||||||
onChange=(action (mut pollResult))
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group poll-select">
|
{{#if showNumber}}
|
||||||
<label class="input-group-label">{{i18n "poll.ui_builder.poll_groups.label"}}</label>
|
<a href {{action (mut pollType) "number"}} class="poll-type-value {{if isNumber "active"}}">
|
||||||
{{group-chooser
|
{{i18n "poll.ui_builder.poll_type.number"}}
|
||||||
content=siteGroups
|
</a>
|
||||||
value=pollGroups
|
{{/if}}
|
||||||
onChange=(action (mut pollGroups))
|
</div>
|
||||||
labelProperty="name"
|
|
||||||
valueProperty="name"}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#unless isNumber}}
|
{{#if showAdvanced}}
|
||||||
<div class="input-group poll-select">
|
<div class="input-group poll-title">
|
||||||
<label class="input-group-label">{{i18n "poll.ui_builder.poll_chart_type.label"}}</label>
|
<label class="input-group-label">{{i18n "poll.ui_builder.poll_title.label"}}</label>
|
||||||
{{combo-box
|
{{input value=pollTitle}}
|
||||||
class="poll-chart-type"
|
</div>
|
||||||
content=pollChartTypes
|
{{/if}}
|
||||||
value=chartType
|
|
||||||
valueProperty="value"
|
|
||||||
onChange=(action (mut chartType))
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#if showMinMax}}
|
{{#unless isNumber}}
|
||||||
<div class="input-group poll-number">
|
<div class="poll-options">
|
||||||
<label class="input-group-label">{{i18n "poll.ui_builder.poll_config.min"}}</label>
|
{{#if showAdvanced}}
|
||||||
{{input type="number"
|
<label class="input-group-label">{{i18n "poll.ui_builder.poll_options.label"}}</label>
|
||||||
value=pollMin
|
|
||||||
valueProperty="value"
|
|
||||||
class="poll-options-min"}}
|
|
||||||
</div>
|
|
||||||
{{input-tip validation=minMaxValueValidation}}
|
|
||||||
|
|
||||||
<div class="input-group poll-number">
|
|
||||||
<label class="input-group-label">{{i18n "poll.ui_builder.poll_config.max"}}</label>
|
|
||||||
{{input type="number"
|
|
||||||
value=pollMax
|
|
||||||
valueProperty="value"
|
|
||||||
class="poll-options-max"}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if isNumber}}
|
|
||||||
<div class="input-group poll-number">
|
|
||||||
<label class="input-group-label">{{i18n "poll.ui_builder.poll_config.step"}}</label>
|
|
||||||
{{input type="number"
|
|
||||||
value=pollStep
|
|
||||||
valueProperty="value"
|
|
||||||
min="1"
|
|
||||||
class="poll-options-step"}}
|
|
||||||
</div>
|
|
||||||
{{input-tip validation=minStepValueValidation}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="input-group poll-title">
|
{{#each pollOptions as |option|}}
|
||||||
<label>{{i18n "poll.ui_builder.poll_title.label"}}</label>
|
<div class="input-group poll-option-value">
|
||||||
{{input value=pollTitle}}
|
{{input value=option.value enter=(action "addOption" option)}}
|
||||||
|
{{#if canRemoveOption}}
|
||||||
|
{{d-button icon="trash-alt" action=(action "removeOption" option)}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<div class="poll-option-controls">
|
||||||
|
{{d-button icon="plus" label="poll.ui_builder.poll_options.add" action=(action "addOption" pollOptions.lastObject)}}
|
||||||
|
{{#if showMinNumOfOptionsValidation}}
|
||||||
|
{{input-tip validation=minNumOfOptionsValidation}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#unless isRegular}}
|
||||||
|
<div class="options">
|
||||||
|
<div class="input-group poll-number">
|
||||||
|
<label class="input-group-label">{{i18n "poll.ui_builder.poll_config.min"}}</label>
|
||||||
|
{{input type="number"
|
||||||
|
value=pollMin
|
||||||
|
valueProperty="value"
|
||||||
|
class="poll-options-min"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#unless isNumber}}
|
<div class="input-group poll-number">
|
||||||
<div class="input-group poll-textarea">
|
<label class="input-group-label">{{i18n "poll.ui_builder.poll_config.max"}}</label>
|
||||||
<label>{{i18n "poll.ui_builder.poll_options.label"}}</label>
|
{{input type="number"
|
||||||
{{textarea value=pollOptions autocomplete="discourse"}}
|
value=pollMax
|
||||||
</div>
|
valueProperty="value"
|
||||||
{{input-tip validation=minNumOfOptionsValidation}}
|
class="poll-options-max"}}
|
||||||
{{/unless}}
|
</div>
|
||||||
|
|
||||||
{{#unless isPie}}
|
{{#if isNumber}}
|
||||||
<div class="input-group poll-checkbox">
|
<div class="input-group poll-number">
|
||||||
<label>
|
<label class="input-group-label">{{i18n "poll.ui_builder.poll_config.step"}}</label>
|
||||||
{{input type="checkbox" checked=publicPoll}}
|
{{input type="number"
|
||||||
{{i18n "poll.ui_builder.poll_public.label"}}
|
value=pollStep
|
||||||
</label>
|
valueProperty="value"
|
||||||
|
min="1"
|
||||||
|
class="poll-options-step"}}
|
||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
{{input-tip validation=minStepValueValidation}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-group poll-checkbox">
|
{{#unless minMaxValueValidation.ok}}
|
||||||
|
{{input-tip validation=minMaxValueValidation}}
|
||||||
|
{{/unless}}
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#if showAdvanced}}
|
||||||
|
<div class="input-group poll-allowed-groups">
|
||||||
|
<label class="input-group-label">{{i18n "poll.ui_builder.poll_groups.label"}}</label>
|
||||||
|
{{group-chooser
|
||||||
|
content=siteGroups
|
||||||
|
value=pollGroups
|
||||||
|
onChange=(action (mut pollGroups))
|
||||||
|
labelProperty="name"
|
||||||
|
valueProperty="name"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group poll-date">
|
||||||
|
<label class="input-group-label">{{i18n "poll.ui_builder.automatic_close.label"}}</label>
|
||||||
|
{{date-time-input date=pollAutoClose onChange=(action (mut pollAutoClose)) clearable=true}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group poll-select">
|
||||||
|
<label class="input-group-label">{{i18n "poll.ui_builder.poll_result.label"}}</label>
|
||||||
|
{{combo-box
|
||||||
|
content=pollResults
|
||||||
|
value=pollResult
|
||||||
|
class="poll-result"
|
||||||
|
valueProperty="value"
|
||||||
|
onChange=(action (mut pollResult))
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#unless isNumber}}
|
||||||
|
<div class="input-group poll-select column">
|
||||||
|
<label class="input-group-label">{{i18n "poll.ui_builder.poll_chart_type.label"}}</label>
|
||||||
|
|
||||||
|
<div class="radio-group">
|
||||||
|
{{radio-button id="poll-chart-type-bar" name="poll-chart-type" value="bar" selection=chartType}}
|
||||||
|
<label for="poll-chart-type-bar">{{d-icon "chart-bar"}} {{i18n "poll.ui_builder.poll_chart_type.bar"}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="radio-group">
|
||||||
|
{{radio-button id="poll-chart-type-pie" name="poll-chart-type" value="pie" selection=chartType}}
|
||||||
|
<label for="poll-chart-type-pie">{{d-icon "chart-pie"}} {{i18n "poll.ui_builder.poll_chart_type.pie"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#unless isPie}}
|
||||||
|
<div class="input-group poll-checkbox column">
|
||||||
<label>
|
<label>
|
||||||
{{input type="checkbox" checked=autoClose}}
|
{{input type="checkbox" checked=publicPoll}}
|
||||||
{{i18n "poll.ui_builder.automatic_close.label"}}
|
{{i18n "poll.ui_builder.poll_public.label"}}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
{{/unless}}
|
||||||
{{#if autoClose}}
|
{{/if}}
|
||||||
<div class="input-group poll-date">
|
|
||||||
{{date-picker-future value=date containerId="date-container"}}
|
|
||||||
{{input type="time" value=time}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group poll-date">
|
|
||||||
<div id="date-container"></div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="d-editor-preview">
|
|
||||||
{{cook-text this.pollOutput}}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{{/d-modal-body}}
|
{{/d-modal-body}}
|
||||||
|
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{{d-button action=(action "insertPoll") icon="chart-bar" class="btn-primary" label="poll.ui_builder.insert" disabled=disableInsert}}
|
{{d-button
|
||||||
|
action=(action "insertPoll")
|
||||||
|
icon="chart-bar"
|
||||||
|
class="btn-primary"
|
||||||
|
label="poll.ui_builder.insert"
|
||||||
|
disabled=disableInsert}}
|
||||||
|
|
||||||
|
{{d-button
|
||||||
|
label="cancel"
|
||||||
|
class="btn-flat"
|
||||||
|
action=(route-action "closeModal")
|
||||||
|
}}
|
||||||
|
|
||||||
|
{{d-button
|
||||||
|
action=(action "toggleAdvanced")
|
||||||
|
class="show-advanced"
|
||||||
|
icon="cog"
|
||||||
|
title=(if showAdvanced "poll.ui_builder.hide_advanced" "poll.ui_builder.show_advanced")}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,86 +1,122 @@
|
|||||||
$poll-margin: 10px;
|
.poll-ui-builder-modal {
|
||||||
|
.show-advanced {
|
||||||
.poll-ui-builder-form {
|
margin-left: auto;
|
||||||
display: flex;
|
margin-right: 0;
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
.options {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 280px;
|
|
||||||
}
|
|
||||||
.combo-box,
|
|
||||||
.multi-select {
|
|
||||||
min-width: 180px;
|
|
||||||
width: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.d-editor-preview {
|
|
||||||
margin-left: $poll-margin * 2;
|
|
||||||
width: 360px;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-ui-builder {
|
||||||
label {
|
label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
display: inline;
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
input[type="number"],
|
||||||
|
input[type="text"],
|
||||||
|
input[type="time"],
|
||||||
|
.combo-box,
|
||||||
|
.multi-select,
|
||||||
|
.select-kit-header,
|
||||||
|
.date-picker-wrapper {
|
||||||
|
height: 34px;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
height: 34px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-picker-wrapper {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-checkbox {
|
||||||
|
label {
|
||||||
|
width: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-group {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 30px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-type {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.poll-type-value {
|
||||||
|
border: none;
|
||||||
|
color: var(--primary);
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 12px 3px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: var(--quaternary);
|
||||||
|
color: var(--secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-options {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.poll-option-value {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 3px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-option-controls {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-date-time-input {
|
||||||
|
border: 1px solid var(--primary-medium);
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.d-date-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-time-input {
|
||||||
|
.select-kit-header {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tip {
|
.tip {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: $poll-margin * 1.5;
|
|
||||||
margin-top: $poll-margin / 2;
|
|
||||||
font-size: $font-down-1;
|
font-size: $font-down-1;
|
||||||
|
margin: -0.5em 0 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poll-select {
|
.d-editor-preview {
|
||||||
margin-bottom: $poll-margin;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group-label {
|
|
||||||
min-width: 85px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-group {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-number {
|
|
||||||
margin: $poll-margin 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 70px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-textarea,
|
|
||||||
.poll-title {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-title input {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poll-textarea textarea {
|
|
||||||
width: 100%;
|
|
||||||
height: 90px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-select + .poll-title {
|
|
||||||
margin-top: $poll-margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-textarea {
|
|
||||||
margin-top: $poll-margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.poll-checkbox,
|
|
||||||
.poll-date {
|
|
||||||
margin-top: $poll-margin / 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
32
plugins/poll/assets/stylesheets/desktop/poll-ui-builder.scss
Normal file
32
plugins/poll/assets/stylesheets/desktop/poll-ui-builder.scss
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.poll-ui-builder-modal {
|
||||||
|
.modal-inner-container {
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
max-height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poll-number {
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.column {
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(50% - 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.d-editor-preview {
|
||||||
|
margin-top: 5px;
|
||||||
|
padding-top: 5px;
|
||||||
|
border-top: 1px solid var(--primary-low);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
.poll-ui-builder-form {
|
|
||||||
.d-editor-preview {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -95,15 +95,15 @@ en:
|
|||||||
multiple: Multiple Choice
|
multiple: Multiple Choice
|
||||||
number: Number Rating
|
number: Number Rating
|
||||||
poll_result:
|
poll_result:
|
||||||
label: Results
|
label: Show Results...
|
||||||
always: Always visible
|
always: Always visible
|
||||||
vote: On vote
|
vote: Only after voting
|
||||||
closed: When closed
|
closed: When the poll is closed
|
||||||
staff: Staff only
|
staff: Staff only
|
||||||
poll_groups:
|
poll_groups:
|
||||||
label: Allowed groups
|
label: Limit voting to these groups
|
||||||
poll_chart_type:
|
poll_chart_type:
|
||||||
label: Chart type
|
label: Result chart
|
||||||
bar: Bar
|
bar: Bar
|
||||||
pie: Pie
|
pie: Pie
|
||||||
poll_config:
|
poll_config:
|
||||||
@@ -115,6 +115,9 @@ en:
|
|||||||
poll_title:
|
poll_title:
|
||||||
label: Title (optional)
|
label: Title (optional)
|
||||||
poll_options:
|
poll_options:
|
||||||
label: Enter one poll option per line
|
label: Options
|
||||||
|
add: Add option
|
||||||
automatic_close:
|
automatic_close:
|
||||||
label: Automatically close poll
|
label: Automatically close poll
|
||||||
|
show_advanced: "Show Advanced Options"
|
||||||
|
hide_advanced: "Hide Advanced Options"
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ register_asset "stylesheets/common/poll.scss"
|
|||||||
register_asset "stylesheets/common/poll-ui-builder.scss"
|
register_asset "stylesheets/common/poll-ui-builder.scss"
|
||||||
register_asset "stylesheets/common/poll-breakdown.scss"
|
register_asset "stylesheets/common/poll-breakdown.scss"
|
||||||
register_asset "stylesheets/desktop/poll.scss", :desktop
|
register_asset "stylesheets/desktop/poll.scss", :desktop
|
||||||
|
register_asset "stylesheets/desktop/poll-ui-builder.scss", :desktop
|
||||||
register_asset "stylesheets/mobile/poll.scss", :mobile
|
register_asset "stylesheets/mobile/poll.scss", :mobile
|
||||||
register_asset "stylesheets/mobile/poll-ui-builder.scss", :mobile
|
|
||||||
|
|
||||||
register_svg_icon "far fa-check-square"
|
register_svg_icon "far fa-check-square"
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
acceptance,
|
acceptance,
|
||||||
exists,
|
exists,
|
||||||
queryAll,
|
|
||||||
updateCurrentUser,
|
updateCurrentUser,
|
||||||
} from "discourse/tests/helpers/qunit-helpers";
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
import { clearPopupMenuOptionsCallback } from "discourse/controllers/composer";
|
import { clearPopupMenuOptionsCallback } from "discourse/controllers/composer";
|
||||||
import { displayPollBuilderButton } from "discourse/plugins/poll/helpers/display-poll-builder-button";
|
import { displayPollBuilderButton } from "discourse/plugins/poll/helpers/display-poll-builder-button";
|
||||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
|
||||||
|
|
||||||
acceptance("Poll Builder - polls are enabled", function (needs) {
|
acceptance("Poll Builder - polls are enabled", function (needs) {
|
||||||
needs.user();
|
needs.user();
|
||||||
@@ -48,22 +46,4 @@ acceptance("Poll Builder - polls are enabled", function (needs) {
|
|||||||
"it shows the builder button"
|
"it shows the builder button"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("poll preview", async function (assert) {
|
|
||||||
await displayPollBuilderButton();
|
|
||||||
|
|
||||||
const popupMenu = selectKit(".toolbar-popup-menu-options");
|
|
||||||
await popupMenu.selectRowByValue("showPollBuilder");
|
|
||||||
|
|
||||||
await fillIn(".poll-textarea textarea", "First option\nSecond option");
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
queryAll(".d-editor-preview li:first-child").text(),
|
|
||||||
"First option"
|
|
||||||
);
|
|
||||||
assert.equal(
|
|
||||||
queryAll(".d-editor-preview li:last-child").text(),
|
|
||||||
"Second option"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import { controllerModule } from "discourse/tests/helpers/qunit-helpers";
|
import { controllerModule } from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import {
|
||||||
|
MULTIPLE_POLL_TYPE,
|
||||||
|
NUMBER_POLL_TYPE,
|
||||||
|
REGULAR_POLL_TYPE,
|
||||||
|
} from "discourse/plugins/poll/controllers/poll-ui-builder";
|
||||||
|
|
||||||
controllerModule("controller:poll-ui-builder", {
|
controllerModule("controller:poll-ui-builder", {
|
||||||
setupController(controller) {
|
setupController(controller) {
|
||||||
controller.set("toolbarEvent", {
|
controller.set("toolbarEvent", { getText: () => "" });
|
||||||
getText: () => "",
|
controller.onShow();
|
||||||
});
|
|
||||||
},
|
},
|
||||||
needs: ["controller:modal"],
|
needs: ["controller:modal"],
|
||||||
});
|
});
|
||||||
@@ -13,17 +17,16 @@ test("isMultiple", function (assert) {
|
|||||||
const controller = this.subject();
|
const controller = this.subject();
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
pollType: controller.multiplePollType,
|
pollType: MULTIPLE_POLL_TYPE,
|
||||||
pollOptions: "a",
|
pollOptions: [{ value: "a" }],
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(controller.isMultiple, true, "it should be true");
|
assert.equal(controller.isMultiple, true, "it should be true");
|
||||||
|
|
||||||
controller.set("pollOptions", "");
|
controller.setProperties({
|
||||||
|
pollType: "random",
|
||||||
assert.equal(controller.isMultiple, false, "it should be false");
|
pollOptions: [{ value: "b" }],
|
||||||
|
});
|
||||||
controller.setProperties({ pollType: "random", pollOptions: "b" });
|
|
||||||
|
|
||||||
assert.equal(controller.isMultiple, false, "it should be false");
|
assert.equal(controller.isMultiple, false, "it should be false");
|
||||||
});
|
});
|
||||||
@@ -31,170 +34,58 @@ test("isMultiple", function (assert) {
|
|||||||
test("isNumber", function (assert) {
|
test("isNumber", function (assert) {
|
||||||
const controller = this.subject();
|
const controller = this.subject();
|
||||||
|
|
||||||
controller.set("pollType", controller.regularPollType);
|
controller.set("pollType", REGULAR_POLL_TYPE);
|
||||||
|
|
||||||
assert.equal(controller.isNumber, false, "it should be false");
|
assert.equal(controller.isNumber, false, "it should be false");
|
||||||
|
|
||||||
controller.set("pollType", controller.numberPollType);
|
controller.set("pollType", NUMBER_POLL_TYPE);
|
||||||
|
|
||||||
assert.equal(controller.isNumber, true, "it should be true");
|
assert.equal(controller.isNumber, true, "it should be true");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("showMinMax", function (assert) {
|
|
||||||
const controller = this.subject();
|
|
||||||
|
|
||||||
controller.set("pollType", controller.numberPollType);
|
|
||||||
assert.equal(controller.showMinMax, true, "it should be true");
|
|
||||||
|
|
||||||
controller.set("pollType", controller.multiplePollType);
|
|
||||||
assert.equal(controller.showMinMax, true, "it should be true");
|
|
||||||
|
|
||||||
controller.set("pollType", controller.regularPollType);
|
|
||||||
assert.equal(controller.showMinMax, false, "it should be false");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("pollOptionsCount", function (assert) {
|
test("pollOptionsCount", function (assert) {
|
||||||
const controller = this.subject();
|
const controller = this.subject();
|
||||||
|
|
||||||
controller.set("pollOptions", "1\n2\n");
|
controller.set("pollOptions", [{ value: "1" }, { value: "2" }]);
|
||||||
|
|
||||||
assert.equal(controller.pollOptionsCount, 2, "it should equal 2");
|
assert.equal(controller.pollOptionsCount, 2, "it should equal 2");
|
||||||
|
|
||||||
controller.set("pollOptions", "");
|
controller.set("pollOptions", []);
|
||||||
|
|
||||||
assert.equal(controller.pollOptionsCount, 0, "it should equal 0");
|
assert.equal(controller.pollOptionsCount, 0, "it should equal 0");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("pollMinOptions", function (assert) {
|
|
||||||
const controller = this.subject();
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: controller.multiplePollType,
|
|
||||||
pollOptions: "z",
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
controller.pollMinOptions,
|
|
||||||
[{ name: 1, value: 1 }],
|
|
||||||
"it should return the right options"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("pollOptions", "z\nx");
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
controller.pollMinOptions,
|
|
||||||
[
|
|
||||||
{ name: 1, value: 1 },
|
|
||||||
{ name: 2, value: 2 },
|
|
||||||
],
|
|
||||||
"it should return the right options"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("pollType", controller.numberPollType);
|
|
||||||
controller.siteSettings.poll_maximum_options = 2;
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
controller.pollMinOptions,
|
|
||||||
[
|
|
||||||
{ name: 1, value: 1 },
|
|
||||||
{ name: 2, value: 2 },
|
|
||||||
],
|
|
||||||
"it should return the right options"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("pollMaxOptions", function (assert) {
|
|
||||||
const controller = this.subject();
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: controller.multiplePollType,
|
|
||||||
pollOptions: "y",
|
|
||||||
pollMin: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
controller.pollMaxOptions,
|
|
||||||
[],
|
|
||||||
"it should return the right options"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("pollOptions", "x\ny");
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
controller.pollMaxOptions,
|
|
||||||
[{ name: 2, value: 2 }],
|
|
||||||
"it should return the right options"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.siteSettings.poll_maximum_options = 3;
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: controller.get("numberPollType"),
|
|
||||||
pollStep: 2,
|
|
||||||
pollMin: 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
controller.pollMaxOptions,
|
|
||||||
[
|
|
||||||
{ name: 2, value: 2 },
|
|
||||||
{ name: 3, value: 3 },
|
|
||||||
{ name: 4, value: 4 },
|
|
||||||
{ name: 5, value: 5 },
|
|
||||||
{ name: 6, value: 6 },
|
|
||||||
],
|
|
||||||
"it should return the right options"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("pollStepOptions", function (assert) {
|
|
||||||
const controller = this.subject();
|
|
||||||
controller.siteSettings.poll_maximum_options = 3;
|
|
||||||
|
|
||||||
assert.equal(controller.pollStepOptions, null, "is should return null");
|
|
||||||
|
|
||||||
controller.set("pollType", controller.numberPollType);
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
controller.pollStepOptions,
|
|
||||||
[
|
|
||||||
{ name: 1, value: 1 },
|
|
||||||
{ name: 2, value: 2 },
|
|
||||||
{ name: 3, value: 3 },
|
|
||||||
],
|
|
||||||
"it should return the right options"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("disableInsert", function (assert) {
|
test("disableInsert", function (assert) {
|
||||||
const controller = this.subject();
|
const controller = this.subject();
|
||||||
|
controller.siteSettings.poll_maximum_options = 20;
|
||||||
|
|
||||||
assert.equal(controller.disableInsert, true, "it should be true");
|
assert.equal(controller.disableInsert, true, "it should be true");
|
||||||
|
|
||||||
controller.set("pollOptions", "a\nb");
|
controller.set("pollOptions", [{ value: "a" }, { value: "b" }]);
|
||||||
|
|
||||||
assert.equal(controller.disableInsert, false, "it should be false");
|
assert.equal(controller.disableInsert, false, "it should be false");
|
||||||
|
|
||||||
controller.set("pollType", controller.numberPollType);
|
controller.set("pollType", NUMBER_POLL_TYPE);
|
||||||
|
|
||||||
assert.equal(controller.disableInsert, false, "it should be false");
|
assert.equal(controller.disableInsert, false, "it should be false");
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
pollType: controller.regularPollType,
|
pollType: REGULAR_POLL_TYPE,
|
||||||
pollOptions: "a\nb\nc",
|
pollOptions: [{ value: "a" }, { value: "b" }, { value: "c" }],
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(controller.disableInsert, false, "it should be false");
|
assert.equal(controller.disableInsert, false, "it should be false");
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
pollType: controller.regularPollType,
|
pollType: REGULAR_POLL_TYPE,
|
||||||
pollOptions: "",
|
pollOptions: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(controller.disableInsert, true, "it should be true");
|
assert.equal(controller.disableInsert, true, "it should be true");
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
pollType: controller.regularPollType,
|
pollType: REGULAR_POLL_TYPE,
|
||||||
pollOptions: "w",
|
pollOptions: [{ value: "w" }],
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(controller.disableInsert, false, "it should be false");
|
assert.equal(controller.disableInsert, false, "it should be false");
|
||||||
@@ -205,7 +96,7 @@ test("number pollOutput", function (assert) {
|
|||||||
controller.siteSettings.poll_maximum_options = 20;
|
controller.siteSettings.poll_maximum_options = 20;
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
pollType: controller.numberPollType,
|
pollType: NUMBER_POLL_TYPE,
|
||||||
pollMin: 1,
|
pollMin: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -244,10 +135,9 @@ test("regular pollOutput", function (assert) {
|
|||||||
const controller = this.subject();
|
const controller = this.subject();
|
||||||
controller.siteSettings.poll_maximum_options = 20;
|
controller.siteSettings.poll_maximum_options = 20;
|
||||||
|
|
||||||
controller.set("pollOptions", "1\n2");
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
pollOptions: "1\n2",
|
pollOptions: [{ value: "1" }, { value: "2" }],
|
||||||
pollType: controller.regularPollType,
|
pollType: REGULAR_POLL_TYPE,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
@@ -278,9 +168,9 @@ test("multiple pollOutput", function (assert) {
|
|||||||
controller.siteSettings.poll_maximum_options = 20;
|
controller.siteSettings.poll_maximum_options = 20;
|
||||||
|
|
||||||
controller.setProperties({
|
controller.setProperties({
|
||||||
pollType: controller.multiplePollType,
|
pollType: MULTIPLE_POLL_TYPE,
|
||||||
pollMin: 1,
|
pollMin: 1,
|
||||||
pollOptions: "\n\n1\n\n2",
|
pollOptions: [{ value: "1" }, { value: "2" }],
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
|||||||
Reference in New Issue
Block a user