DEV: Convert poll modals to new component-based API (#22164)
This commit is contained in:
parent
e549b0f132
commit
773e198cb3
|
@ -0,0 +1,76 @@
|
||||||
|
{{! template-lint-disable no-invalid-interactive }}
|
||||||
|
<DModal @title={{i18n "poll.breakdown.title"}} class="has-tabs">
|
||||||
|
<:headerBelowTitle>
|
||||||
|
<ul class="modal-tabs">
|
||||||
|
<li
|
||||||
|
class={{concat-class
|
||||||
|
"modal-tab percentage"
|
||||||
|
(if (eq this.displayMode "percentage") "active")
|
||||||
|
}}
|
||||||
|
{{on "click" (fn (mut this.displayMode) "percentage")}}
|
||||||
|
>{{i18n "poll.breakdown.percentage"}}</li>
|
||||||
|
<li
|
||||||
|
class={{concat-class
|
||||||
|
"modal-tab count"
|
||||||
|
(if (eq this.displayMode "count") "active")
|
||||||
|
}}
|
||||||
|
{{on "click" (fn (mut this.displayMode) "count")}}
|
||||||
|
>{{i18n "poll.breakdown.count"}}</li>
|
||||||
|
</ul>
|
||||||
|
</:headerBelowTitle>
|
||||||
|
<:body>
|
||||||
|
<div class="poll-breakdown-sidebar">
|
||||||
|
<p class="poll-breakdown-title">
|
||||||
|
{{this.title}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="poll-breakdown-total-votes">{{i18n
|
||||||
|
"poll.breakdown.votes"
|
||||||
|
count=this.model.poll.voters
|
||||||
|
}}</div>
|
||||||
|
|
||||||
|
<ul class="poll-breakdown-options">
|
||||||
|
{{#each this.model.poll.options as |option index|}}
|
||||||
|
<PollBreakdownOption
|
||||||
|
@option={{option}}
|
||||||
|
@index={{index}}
|
||||||
|
@totalVotes={{this.totalVotes}}
|
||||||
|
@optionsCount={{this.model.poll.options.length}}
|
||||||
|
@displayMode={{this.displayMode}}
|
||||||
|
@highlightedOption={{this.highlightedOption}}
|
||||||
|
@onMouseOver={{fn (mut this.highlightedOption) index}}
|
||||||
|
@onMouseOut={{fn (mut this.highlightedOption) null}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="poll-breakdown-body">
|
||||||
|
<div class="poll-breakdown-body-header">
|
||||||
|
<label class="poll-breakdown-body-header-label">{{i18n
|
||||||
|
"poll.breakdown.breakdown"
|
||||||
|
}}</label>
|
||||||
|
|
||||||
|
<ComboBox
|
||||||
|
@content={{this.groupableUserFields}}
|
||||||
|
@value={{this.groupedBy}}
|
||||||
|
@nameProperty="label"
|
||||||
|
@class="poll-breakdown-dropdown"
|
||||||
|
@onChange={{action this.setGrouping}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="poll-breakdown-charts">
|
||||||
|
{{#each this.charts as |chart|}}
|
||||||
|
<PollBreakdownChart
|
||||||
|
@group={{get chart "group"}}
|
||||||
|
@options={{get chart "options"}}
|
||||||
|
@displayMode={{this.displayMode}}
|
||||||
|
@highlightedOption={{this.highlightedOption}}
|
||||||
|
@setHighlightedOption={{fn (mut this.highlightedOption)}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</:body>
|
||||||
|
</DModal>
|
|
@ -1,7 +1,6 @@
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import Controller from "@ember/controller";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { classify } from "@ember/string";
|
import { classify } from "@ember/string";
|
||||||
|
@ -10,9 +9,7 @@ import { htmlSafe } from "@ember/template";
|
||||||
import loadScript from "discourse/lib/load-script";
|
import loadScript from "discourse/lib/load-script";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
|
||||||
export default class PollBreakdownController extends Controller.extend(
|
export default class PollBreakdownModal extends Component {
|
||||||
ModalFunctionality
|
|
||||||
) {
|
|
||||||
@service dialog;
|
@service dialog;
|
||||||
|
|
||||||
model = null;
|
model = null;
|
||||||
|
@ -21,6 +18,16 @@ export default class PollBreakdownController extends Controller.extend(
|
||||||
highlightedOption = null;
|
highlightedOption = null;
|
||||||
displayMode = "percentage";
|
displayMode = "percentage";
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.set("groupedBy", this.model.groupableUserFields[0]);
|
||||||
|
loadScript("/javascripts/Chart.min.js")
|
||||||
|
.then(() => loadScript("/javascripts/chartjs-plugin-datalabels.min.js"))
|
||||||
|
.then(() => {
|
||||||
|
this.fetchGroupedPollData();
|
||||||
|
});
|
||||||
|
super.init(...arguments);
|
||||||
|
}
|
||||||
|
|
||||||
@discourseComputed("model.poll.title", "model.post.topic.title")
|
@discourseComputed("model.poll.title", "model.post.topic.title")
|
||||||
title(pollTitle, topicTitle) {
|
title(pollTitle, topicTitle) {
|
||||||
return pollTitle ? htmlSafe(pollTitle) : topicTitle;
|
return pollTitle ? htmlSafe(pollTitle) : topicTitle;
|
||||||
|
@ -44,18 +51,6 @@ export default class PollBreakdownController extends Controller.extend(
|
||||||
return options.reduce((sum, option) => sum + option.votes, 0);
|
return options.reduce((sum, option) => sum + option.votes, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
onShow() {
|
|
||||||
this.set("charts", null);
|
|
||||||
this.set("displayMode", "percentage");
|
|
||||||
this.set("groupedBy", this.model.groupableUserFields[0]);
|
|
||||||
|
|
||||||
loadScript("/javascripts/Chart.min.js")
|
|
||||||
.then(() => loadScript("/javascripts/chartjs-plugin-datalabels.min.js"))
|
|
||||||
.then(() => {
|
|
||||||
this.fetchGroupedPollData();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchGroupedPollData() {
|
fetchGroupedPollData() {
|
||||||
return ajax("/polls/grouped_poll_results.json", {
|
return ajax("/polls/grouped_poll_results.json", {
|
||||||
data: {
|
data: {
|
|
@ -0,0 +1,251 @@
|
||||||
|
<DModal
|
||||||
|
@title={{i18n "poll.ui_builder.title"}}
|
||||||
|
@closeModal={{@closeModal}}
|
||||||
|
@inline={{@inline}}
|
||||||
|
class="poll-ui-builder"
|
||||||
|
>
|
||||||
|
<:body>
|
||||||
|
<div class="input-group poll-type">
|
||||||
|
<a
|
||||||
|
href
|
||||||
|
{{on "click" (fn this.updatePollType "regular")}}
|
||||||
|
class="poll-type-value poll-type-value-regular
|
||||||
|
{{if this.isRegular 'active'}}"
|
||||||
|
>
|
||||||
|
{{i18n "poll.ui_builder.poll_type.regular"}}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href
|
||||||
|
{{on "click" (fn this.updatePollType "multiple")}}
|
||||||
|
class="poll-type-value poll-type-value-multiple
|
||||||
|
{{if this.isMultiple 'active'}}"
|
||||||
|
>
|
||||||
|
{{i18n "poll.ui_builder.poll_type.multiple"}}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{#if this.showNumber}}
|
||||||
|
<a
|
||||||
|
href
|
||||||
|
{{on "click" (fn this.updatePollType "number")}}
|
||||||
|
class="poll-type-value poll-type-value-number
|
||||||
|
{{if this.isNumber 'active'}}"
|
||||||
|
>
|
||||||
|
{{i18n "poll.ui_builder.poll_type.number"}}
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if this.showAdvanced}}
|
||||||
|
<div class="input-group poll-title">
|
||||||
|
<label class="input-group-label">{{i18n
|
||||||
|
"poll.ui_builder.poll_title.label"
|
||||||
|
}}</label>
|
||||||
|
<Input @value={{this.pollTitle}} />
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless this.isNumber}}
|
||||||
|
<div class="poll-options">
|
||||||
|
{{#if this.showAdvanced}}
|
||||||
|
<label class="input-group-label">{{i18n
|
||||||
|
"poll.ui_builder.poll_options.label"
|
||||||
|
}}</label>
|
||||||
|
<Textarea
|
||||||
|
@value={{this.pollOptionsText}}
|
||||||
|
{{on "input" (action "onOptionsTextChange")}}
|
||||||
|
/>
|
||||||
|
{{#if this.showMinNumOfOptionsValidation}}
|
||||||
|
{{#unless this.minNumOfOptionsValidation.ok}}
|
||||||
|
<InputTip @validation={{this.minNumOfOptionsValidation}} />
|
||||||
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#each this.pollOptions as |option|}}
|
||||||
|
<div class="input-group poll-option-value">
|
||||||
|
<Input
|
||||||
|
@value={{option.value}}
|
||||||
|
@enter={{action "addOption" option}}
|
||||||
|
/>
|
||||||
|
{{#if this.canRemoveOption}}
|
||||||
|
<DButton
|
||||||
|
@icon="trash-alt"
|
||||||
|
@action={{action "removeOption" option}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
<div class="poll-option-controls">
|
||||||
|
<DButton
|
||||||
|
class="btn-default poll-option-add"
|
||||||
|
@icon="plus"
|
||||||
|
@label="poll.ui_builder.poll_options.add"
|
||||||
|
@action={{action "addOption" this.pollOptions.lastObject}}
|
||||||
|
/>
|
||||||
|
{{#if
|
||||||
|
(and
|
||||||
|
this.showMinNumOfOptionsValidation
|
||||||
|
(not this.minNumOfOptionsValidation.ok)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
<InputTip @validation={{this.minNumOfOptionsValidation}} />
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#unless this.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={{this.pollMin}}
|
||||||
|
class="poll-options-min"
|
||||||
|
min="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group poll-number">
|
||||||
|
<label class="input-group-label">{{i18n
|
||||||
|
"poll.ui_builder.poll_config.max"
|
||||||
|
}}</label>
|
||||||
|
<Input
|
||||||
|
@type="number"
|
||||||
|
@value={{this.pollMax}}
|
||||||
|
class="poll-options-max"
|
||||||
|
min="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if this.isNumber}}
|
||||||
|
<div class="input-group poll-number">
|
||||||
|
<label class="input-group-label">{{i18n
|
||||||
|
"poll.ui_builder.poll_config.step"
|
||||||
|
}}</label>
|
||||||
|
<Input
|
||||||
|
@type="number"
|
||||||
|
@value={{this.pollStep}}
|
||||||
|
min="1"
|
||||||
|
class="poll-options-step"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#unless this.minMaxValueValidation.ok}}
|
||||||
|
<InputTip @validation={{this.minMaxValueValidation}} />
|
||||||
|
{{/unless}}
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#if this.showAdvanced}}
|
||||||
|
<div class="input-group poll-allowed-groups">
|
||||||
|
<label class="input-group-label">{{i18n
|
||||||
|
"poll.ui_builder.poll_groups.label"
|
||||||
|
}}</label>
|
||||||
|
<GroupChooser
|
||||||
|
@content={{this.siteGroups}}
|
||||||
|
@value={{this.pollGroups}}
|
||||||
|
@onChange={{action (mut this.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>
|
||||||
|
<DateTimeInput
|
||||||
|
@date={{this.pollAutoClose}}
|
||||||
|
@onChange={{action (mut this.pollAutoClose)}}
|
||||||
|
@clearable={{true}}
|
||||||
|
@useGlobalPickerContainer={{true}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group poll-select">
|
||||||
|
<label class="input-group-label">{{i18n
|
||||||
|
"poll.ui_builder.poll_result.label"
|
||||||
|
}}</label>
|
||||||
|
<ComboBox
|
||||||
|
@content={{this.pollResults}}
|
||||||
|
@value={{this.pollResult}}
|
||||||
|
@class="poll-result"
|
||||||
|
@valueProperty="value"
|
||||||
|
@onChange={{action (mut this.pollResult)}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#unless this.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">
|
||||||
|
<RadioButton
|
||||||
|
@id="poll-chart-type-bar"
|
||||||
|
@name="poll-chart-type"
|
||||||
|
@value="bar"
|
||||||
|
@selection={{this.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">
|
||||||
|
<RadioButton
|
||||||
|
@id="poll-chart-type-pie"
|
||||||
|
@name="poll-chart-type"
|
||||||
|
@value="pie"
|
||||||
|
@selection={{this.chartType}}
|
||||||
|
/>
|
||||||
|
<label for="poll-chart-type-pie">{{d-icon "chart-pie"}}
|
||||||
|
{{i18n "poll.ui_builder.poll_chart_type.pie"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
{{#unless this.isPie}}
|
||||||
|
<div class="input-group poll-checkbox column">
|
||||||
|
<label>
|
||||||
|
<Input
|
||||||
|
@type="checkbox"
|
||||||
|
@checked={{this.publicPoll}}
|
||||||
|
class="poll-toggle-public"
|
||||||
|
/>
|
||||||
|
{{i18n "poll.ui_builder.poll_public.label"}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
|
</:body>
|
||||||
|
<:footer>
|
||||||
|
<DButton
|
||||||
|
@action={{action "insertPoll"}}
|
||||||
|
@icon="chart-bar"
|
||||||
|
class="btn-primary insert-poll"
|
||||||
|
@label="poll.ui_builder.insert"
|
||||||
|
@disabled={{this.disableInsert}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DButton @label="cancel" @class="btn-flat" @action={{@closeModal}} />
|
||||||
|
|
||||||
|
<DButton
|
||||||
|
@action={{action "toggleAdvanced"}}
|
||||||
|
class="btn-default show-advanced"
|
||||||
|
@icon="cog"
|
||||||
|
@title={{if
|
||||||
|
this.showAdvanced
|
||||||
|
"poll.ui_builder.hide_advanced"
|
||||||
|
"poll.ui_builder.show_advanced"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</:footer>
|
||||||
|
</DModal>
|
|
@ -1,10 +1,9 @@
|
||||||
import { gt, or } from "@ember/object/computed";
|
import { gt, or } from "@ember/object/computed";
|
||||||
import Controller from "@ember/controller";
|
import Component from "@ember/component";
|
||||||
import EmberObject, { action } from "@ember/object";
|
import EmberObject, { action } from "@ember/object";
|
||||||
import { next } from "@ember/runloop";
|
import { next } from "@ember/runloop";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { observes } from "@ember-decorators/object";
|
import { observes } from "@ember-decorators/object";
|
||||||
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";
|
||||||
|
@ -19,46 +18,26 @@ const VOTE_POLL_RESULT = "on_vote";
|
||||||
const CLOSED_POLL_RESULT = "on_close";
|
const CLOSED_POLL_RESULT = "on_close";
|
||||||
const STAFF_POLL_RESULT = "staff_only";
|
const STAFF_POLL_RESULT = "staff_only";
|
||||||
|
|
||||||
export default class PollUiBuilderController extends Controller.extend(
|
export default class PollUiBuilderModal extends Component {
|
||||||
ModalFunctionality
|
|
||||||
) {
|
|
||||||
showAdvanced = false;
|
showAdvanced = false;
|
||||||
pollType = REGULAR_POLL_TYPE;
|
pollType = REGULAR_POLL_TYPE;
|
||||||
pollTitle = "";
|
pollTitle;
|
||||||
pollOptions = null;
|
pollOptions = [EmberObject.create({ value: "" })];
|
||||||
pollOptionsText = null;
|
pollOptionsText = "";
|
||||||
pollMin = 1;
|
pollMin = 1;
|
||||||
pollMax = 2;
|
pollMax = 2;
|
||||||
pollStep = 1;
|
pollStep = 1;
|
||||||
pollGroups = null;
|
pollGroups;
|
||||||
pollAutoClose = null;
|
pollAutoClose;
|
||||||
pollResult = ALWAYS_POLL_RESULT;
|
pollResult = ALWAYS_POLL_RESULT;
|
||||||
chartType = BAR_CHART_TYPE;
|
chartType = BAR_CHART_TYPE;
|
||||||
publicPoll = null;
|
publicPoll = false;
|
||||||
|
|
||||||
@or("showAdvanced", "isNumber") showNumber;
|
@or("showAdvanced", "isNumber") showNumber;
|
||||||
@gt("pollOptions.length", 1) canRemoveOption;
|
@gt("pollOptions.length", 1) canRemoveOption;
|
||||||
|
|
||||||
onShow() {
|
@discourseComputed("currentUser.staff")
|
||||||
this.setProperties({
|
pollResults(staff) {
|
||||||
showAdvanced: false,
|
|
||||||
pollType: REGULAR_POLL_TYPE,
|
|
||||||
pollTitle: null,
|
|
||||||
pollOptions: [EmberObject.create({ value: "" })],
|
|
||||||
pollOptionsText: "",
|
|
||||||
pollMin: 1,
|
|
||||||
pollMax: 2,
|
|
||||||
pollStep: 1,
|
|
||||||
pollGroups: null,
|
|
||||||
pollAutoClose: null,
|
|
||||||
pollResult: ALWAYS_POLL_RESULT,
|
|
||||||
chartType: BAR_CHART_TYPE,
|
|
||||||
publicPoll: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@discourseComputed
|
|
||||||
pollResults() {
|
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
name: I18n.t("poll.ui_builder.poll_result.always"),
|
name: I18n.t("poll.ui_builder.poll_result.always"),
|
||||||
|
@ -74,7 +53,7 @@ export default class PollUiBuilderController extends Controller.extend(
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (this.get("currentUser.staff")) {
|
if (staff) {
|
||||||
options.push({
|
options.push({
|
||||||
name: I18n.t("poll.ui_builder.poll_result.staff"),
|
name: I18n.t("poll.ui_builder.poll_result.staff"),
|
||||||
value: STAFF_POLL_RESULT,
|
value: STAFF_POLL_RESULT,
|
||||||
|
@ -168,7 +147,7 @@ export default class PollUiBuilderController extends Controller.extend(
|
||||||
let pollHeader = "[poll";
|
let pollHeader = "[poll";
|
||||||
let output = "";
|
let output = "";
|
||||||
|
|
||||||
const match = this.toolbarEvent
|
const match = this.model.toolbarEvent
|
||||||
.getText()
|
.getText()
|
||||||
.match(/\[poll(\s+name=[^\s\]]+)*.*\]/gim);
|
.match(/\[poll(\s+name=[^\s\]]+)*.*\]/gim);
|
||||||
|
|
||||||
|
@ -354,8 +333,8 @@ export default class PollUiBuilderController extends Controller.extend(
|
||||||
|
|
||||||
@action
|
@action
|
||||||
insertPoll() {
|
insertPoll() {
|
||||||
this.toolbarEvent.addText(this.pollOutput);
|
this.model.toolbarEvent.addText(this.pollOutput);
|
||||||
this.send("closeModal");
|
this.closeModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
|
@ -2,7 +2,7 @@ import { classNames } from "@ember-decorators/component";
|
||||||
import { mapBy } from "@ember/object/computed";
|
import { mapBy } from "@ember/object/computed";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { PIE_CHART_TYPE } from "../controllers/poll-ui-builder";
|
import { PIE_CHART_TYPE } from "../components/modal/poll-ui-builder";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { getColors } from "discourse/plugins/poll/lib/chart-colors";
|
import { getColors } from "discourse/plugins/poll/lib/chart-colors";
|
||||||
import { htmlSafe } from "@ember/template";
|
import { htmlSafe } from "@ember/template";
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import showModal from "discourse/lib/show-modal";
|
|
||||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||||
|
import PollUiBuilder from "../components/modal/poll-ui-builder";
|
||||||
|
import { getOwner } from "@ember/application";
|
||||||
|
|
||||||
function initializePollUIBuilder(api) {
|
function initializePollUIBuilder(api) {
|
||||||
api.modifyClass("controller:composer", {
|
api.modifyClass("controller:composer", {
|
||||||
|
@ -22,7 +23,11 @@ function initializePollUIBuilder(api) {
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
showPollBuilder() {
|
showPollBuilder() {
|
||||||
showModal("poll-ui-builder").set("toolbarEvent", this.toolbarEvent);
|
getOwner(this)
|
||||||
|
.lookup("service:modal")
|
||||||
|
.show(PollUiBuilder, {
|
||||||
|
model: { toolbarEvent: this.toolbarEvent },
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
<DModalBody @title="poll.breakdown.title">
|
|
||||||
<div class="poll-breakdown-sidebar">
|
|
||||||
<p class="poll-breakdown-title">
|
|
||||||
{{this.title}}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="poll-breakdown-total-votes">{{i18n
|
|
||||||
"poll.breakdown.votes"
|
|
||||||
count=this.model.poll.voters
|
|
||||||
}}</div>
|
|
||||||
|
|
||||||
<ul class="poll-breakdown-options">
|
|
||||||
{{#each this.model.poll.options as |option index|}}
|
|
||||||
<PollBreakdownOption
|
|
||||||
@option={{option}}
|
|
||||||
@index={{index}}
|
|
||||||
@totalVotes={{this.totalVotes}}
|
|
||||||
@optionsCount={{this.model.poll.options.length}}
|
|
||||||
@displayMode={{this.displayMode}}
|
|
||||||
@highlightedOption={{this.highlightedOption}}
|
|
||||||
@onMouseOver={{fn (mut this.highlightedOption) index}}
|
|
||||||
@onMouseOut={{fn (mut this.highlightedOption) null}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="poll-breakdown-body">
|
|
||||||
<div class="poll-breakdown-body-header">
|
|
||||||
<label class="poll-breakdown-body-header-label">{{i18n
|
|
||||||
"poll.breakdown.breakdown"
|
|
||||||
}}</label>
|
|
||||||
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.groupableUserFields}}
|
|
||||||
@value={{this.groupedBy}}
|
|
||||||
@nameProperty="label"
|
|
||||||
@class="poll-breakdown-dropdown"
|
|
||||||
@onChange={{action this.setGrouping}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="poll-breakdown-charts">
|
|
||||||
{{#each this.charts as |chart|}}
|
|
||||||
<PollBreakdownChart
|
|
||||||
@group={{get chart "group"}}
|
|
||||||
@options={{get chart "options"}}
|
|
||||||
@displayMode={{this.displayMode}}
|
|
||||||
@highlightedOption={{this.highlightedOption}}
|
|
||||||
@setHighlightedOption={{fn (mut this.highlightedOption)}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DModalBody>
|
|
|
@ -1,244 +0,0 @@
|
||||||
<DModalBody @title="poll.ui_builder.title" @class="poll-ui-builder">
|
|
||||||
<div class="input-group poll-type">
|
|
||||||
<a
|
|
||||||
href
|
|
||||||
{{on "click" (fn this.updatePollType "regular")}}
|
|
||||||
class="poll-type-value poll-type-value-regular
|
|
||||||
{{if this.isRegular 'active'}}"
|
|
||||||
>
|
|
||||||
{{i18n "poll.ui_builder.poll_type.regular"}}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href
|
|
||||||
{{on "click" (fn this.updatePollType "multiple")}}
|
|
||||||
class="poll-type-value poll-type-value-multiple
|
|
||||||
{{if this.isMultiple 'active'}}"
|
|
||||||
>
|
|
||||||
{{i18n "poll.ui_builder.poll_type.multiple"}}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
{{#if this.showNumber}}
|
|
||||||
<a
|
|
||||||
href
|
|
||||||
{{on "click" (fn this.updatePollType "number")}}
|
|
||||||
class="poll-type-value poll-type-value-number
|
|
||||||
{{if this.isNumber 'active'}}"
|
|
||||||
>
|
|
||||||
{{i18n "poll.ui_builder.poll_type.number"}}
|
|
||||||
</a>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.showAdvanced}}
|
|
||||||
<div class="input-group poll-title">
|
|
||||||
<label class="input-group-label">{{i18n
|
|
||||||
"poll.ui_builder.poll_title.label"
|
|
||||||
}}</label>
|
|
||||||
<Input @value={{this.pollTitle}} />
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#unless this.isNumber}}
|
|
||||||
<div class="poll-options">
|
|
||||||
{{#if this.showAdvanced}}
|
|
||||||
<label class="input-group-label">{{i18n
|
|
||||||
"poll.ui_builder.poll_options.label"
|
|
||||||
}}</label>
|
|
||||||
<Textarea
|
|
||||||
@value={{this.pollOptionsText}}
|
|
||||||
{{on "input" (action "onOptionsTextChange")}}
|
|
||||||
/>
|
|
||||||
{{#if this.showMinNumOfOptionsValidation}}
|
|
||||||
{{#unless this.minNumOfOptionsValidation.ok}}
|
|
||||||
<InputTip @validation={{this.minNumOfOptionsValidation}} />
|
|
||||||
{{/unless}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
{{#each this.pollOptions as |option|}}
|
|
||||||
<div class="input-group poll-option-value">
|
|
||||||
<Input
|
|
||||||
@value={{option.value}}
|
|
||||||
@enter={{action "addOption" option}}
|
|
||||||
/>
|
|
||||||
{{#if this.canRemoveOption}}
|
|
||||||
<DButton
|
|
||||||
@icon="trash-alt"
|
|
||||||
@action={{action "removeOption" option}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
<div class="poll-option-controls">
|
|
||||||
<DButton
|
|
||||||
@class="btn-default"
|
|
||||||
@icon="plus"
|
|
||||||
@label="poll.ui_builder.poll_options.add"
|
|
||||||
@action={{action "addOption" this.pollOptions.lastObject}}
|
|
||||||
/>
|
|
||||||
{{#if
|
|
||||||
(and
|
|
||||||
this.showMinNumOfOptionsValidation
|
|
||||||
(not this.minNumOfOptionsValidation.ok)
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<InputTip @validation={{this.minNumOfOptionsValidation}} />
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#unless this.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={{this.pollMin}}
|
|
||||||
class="poll-options-min"
|
|
||||||
min="1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group poll-number">
|
|
||||||
<label class="input-group-label">{{i18n
|
|
||||||
"poll.ui_builder.poll_config.max"
|
|
||||||
}}</label>
|
|
||||||
<Input
|
|
||||||
@type="number"
|
|
||||||
@value={{this.pollMax}}
|
|
||||||
class="poll-options-max"
|
|
||||||
min="1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.isNumber}}
|
|
||||||
<div class="input-group poll-number">
|
|
||||||
<label class="input-group-label">{{i18n
|
|
||||||
"poll.ui_builder.poll_config.step"
|
|
||||||
}}</label>
|
|
||||||
<Input
|
|
||||||
@type="number"
|
|
||||||
@value={{this.pollStep}}
|
|
||||||
min="1"
|
|
||||||
class="poll-options-step"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#unless this.minMaxValueValidation.ok}}
|
|
||||||
<InputTip @validation={{this.minMaxValueValidation}} />
|
|
||||||
{{/unless}}
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#if this.showAdvanced}}
|
|
||||||
<div class="input-group poll-allowed-groups">
|
|
||||||
<label class="input-group-label">{{i18n
|
|
||||||
"poll.ui_builder.poll_groups.label"
|
|
||||||
}}</label>
|
|
||||||
<GroupChooser
|
|
||||||
@content={{this.siteGroups}}
|
|
||||||
@value={{this.pollGroups}}
|
|
||||||
@onChange={{action (mut this.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>
|
|
||||||
<DateTimeInput
|
|
||||||
@date={{this.pollAutoClose}}
|
|
||||||
@onChange={{action (mut this.pollAutoClose)}}
|
|
||||||
@clearable={{true}}
|
|
||||||
@useGlobalPickerContainer={{true}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group poll-select">
|
|
||||||
<label class="input-group-label">{{i18n
|
|
||||||
"poll.ui_builder.poll_result.label"
|
|
||||||
}}</label>
|
|
||||||
<ComboBox
|
|
||||||
@content={{this.pollResults}}
|
|
||||||
@value={{this.pollResult}}
|
|
||||||
@class="poll-result"
|
|
||||||
@valueProperty="value"
|
|
||||||
@onChange={{action (mut this.pollResult)}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#unless this.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">
|
|
||||||
<RadioButton
|
|
||||||
@id="poll-chart-type-bar"
|
|
||||||
@name="poll-chart-type"
|
|
||||||
@value="bar"
|
|
||||||
@selection={{this.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">
|
|
||||||
<RadioButton
|
|
||||||
@id="poll-chart-type-pie"
|
|
||||||
@name="poll-chart-type"
|
|
||||||
@value="pie"
|
|
||||||
@selection={{this.chartType}}
|
|
||||||
/>
|
|
||||||
<label for="poll-chart-type-pie">{{d-icon "chart-pie"}}
|
|
||||||
{{i18n "poll.ui_builder.poll_chart_type.pie"}}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
|
|
||||||
{{#unless this.isPie}}
|
|
||||||
<div class="input-group poll-checkbox column">
|
|
||||||
<label>
|
|
||||||
<Input @type="checkbox" @checked={{this.publicPoll}} />
|
|
||||||
{{i18n "poll.ui_builder.poll_public.label"}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
{{/if}}
|
|
||||||
</DModalBody>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<DButton
|
|
||||||
@action={{action "insertPoll"}}
|
|
||||||
@icon="chart-bar"
|
|
||||||
@class="btn-primary"
|
|
||||||
@label="poll.ui_builder.insert"
|
|
||||||
@disabled={{this.disableInsert}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@label="cancel"
|
|
||||||
@class="btn-flat"
|
|
||||||
@action={{route-action "closeModal"}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<DButton
|
|
||||||
@action={{action "toggleAdvanced"}}
|
|
||||||
@class="btn-default show-advanced"
|
|
||||||
@icon="cog"
|
|
||||||
@title={{if
|
|
||||||
this.showAdvanced
|
|
||||||
"poll.ui_builder.hide_advanced"
|
|
||||||
"poll.ui_builder.show_advanced"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
|
@ -1,5 +1,5 @@
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { PIE_CHART_TYPE } from "../controllers/poll-ui-builder";
|
import { PIE_CHART_TYPE } from "../components/modal/poll-ui-builder";
|
||||||
import RawHtml from "discourse/widgets/raw-html";
|
import RawHtml from "discourse/widgets/raw-html";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { avatarFor } from "discourse/widgets/post";
|
import { avatarFor } from "discourse/widgets/post";
|
||||||
|
@ -12,8 +12,9 @@ import loadScript from "discourse/lib/load-script";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { relativeAge } from "discourse/lib/formatter";
|
import { relativeAge } from "discourse/lib/formatter";
|
||||||
import round from "discourse/lib/round";
|
import round from "discourse/lib/round";
|
||||||
import showModal from "discourse/lib/show-modal";
|
|
||||||
import { applyLocalDates } from "discourse/lib/local-dates";
|
import { applyLocalDates } from "discourse/lib/local-dates";
|
||||||
|
import PollBreakdownModal from "../components/modal/poll-breakdown";
|
||||||
|
import { getOwner } from "@ember/application";
|
||||||
|
|
||||||
const FETCH_VOTERS_COUNT = 25;
|
const FETCH_VOTERS_COUNT = 25;
|
||||||
|
|
||||||
|
@ -1070,12 +1071,8 @@ export default createWidget("discourse-poll", {
|
||||||
},
|
},
|
||||||
|
|
||||||
showBreakdown() {
|
showBreakdown() {
|
||||||
showModal("poll-breakdown", {
|
getOwner(this).lookup("service:modal").show(PollBreakdownModal, {
|
||||||
model: this.attrs,
|
model: this.attrs,
|
||||||
panels: [
|
|
||||||
{ id: "percentage", title: "poll.breakdown.percentage" },
|
|
||||||
{ id: "count", title: "poll.breakdown.count" },
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
import { module, test } from "qunit";
|
||||||
|
import { click, fillIn, render } from "@ember/test-helpers";
|
||||||
|
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||||
|
import hbs from "htmlbars-inline-precompile";
|
||||||
|
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||||
|
|
||||||
|
async function setupBuilder(context) {
|
||||||
|
const results = [];
|
||||||
|
const model = {
|
||||||
|
toolbarEvent: { getText: () => "", addText: (t) => results.push(t) },
|
||||||
|
};
|
||||||
|
context.model = model;
|
||||||
|
await render(
|
||||||
|
hbs`<Modal::PollUiBuilder @inline={{true}} @model={{this.model}} @closeModal={{fn (mut this.closeCalled) true}} />`
|
||||||
|
);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
module("Poll | Component | poll-ui-builder", function (hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
test("Can switch poll type", async function (assert) {
|
||||||
|
await setupBuilder(this);
|
||||||
|
|
||||||
|
assert.dom(".poll-type-value-regular").hasClass("active");
|
||||||
|
|
||||||
|
await click(".poll-type-value-multiple");
|
||||||
|
assert
|
||||||
|
.dom(".poll-type-value-multiple")
|
||||||
|
.hasClass("active", "can switch to 'multiple' type");
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom(".poll-type-value-number")
|
||||||
|
.doesNotExist("number type is hidden by default");
|
||||||
|
|
||||||
|
await click(".show-advanced");
|
||||||
|
assert
|
||||||
|
.dom(".poll-type-value-number")
|
||||||
|
.exists("number type appears in advanced mode");
|
||||||
|
|
||||||
|
await click(".poll-type-value-number");
|
||||||
|
assert
|
||||||
|
.dom(".poll-type-value-number")
|
||||||
|
.hasClass("active", "can switch to 'number' type");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Automatically updates min/max when number of options change", async function (assert) {
|
||||||
|
await setupBuilder(this);
|
||||||
|
|
||||||
|
await click(".poll-type-value-multiple");
|
||||||
|
assert.dom(".poll-options-min").hasValue("0");
|
||||||
|
assert.dom(".poll-options-max").hasValue("0");
|
||||||
|
|
||||||
|
await fillIn(".poll-option-value input", "a");
|
||||||
|
assert.dom(".poll-options-min").hasValue("1");
|
||||||
|
assert.dom(".poll-options-max").hasValue("1");
|
||||||
|
|
||||||
|
await click(".poll-option-add");
|
||||||
|
|
||||||
|
await fillIn(".poll-option-value:nth-of-type(2) input", "b");
|
||||||
|
assert.dom(".poll-options-min").hasValue("1");
|
||||||
|
assert.dom(".poll-options-max").hasValue("2");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("disables save button", async function (assert) {
|
||||||
|
this.siteSettings.poll_maximum_options = 3;
|
||||||
|
|
||||||
|
await setupBuilder(this);
|
||||||
|
assert
|
||||||
|
.dom(".insert-poll")
|
||||||
|
.isDisabled("Insert button disabled when no options specified");
|
||||||
|
|
||||||
|
await fillIn(".poll-option-value input", "a");
|
||||||
|
assert
|
||||||
|
.dom(".insert-poll")
|
||||||
|
.isEnabled("Insert button enabled once an option is specified");
|
||||||
|
|
||||||
|
await click(".poll-option-add");
|
||||||
|
await fillIn(".poll-option-value:nth-of-type(2) input", "b");
|
||||||
|
await click(".poll-option-add");
|
||||||
|
await fillIn(".poll-option-value:nth-of-type(3) input", "c");
|
||||||
|
await click(".poll-option-add");
|
||||||
|
await fillIn(".poll-option-value:nth-of-type(4) input", "d");
|
||||||
|
|
||||||
|
assert
|
||||||
|
.dom(".insert-poll")
|
||||||
|
.isDisabled("Insert button disabled when too many options");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("number mode", async function (assert) {
|
||||||
|
const results = await setupBuilder(this);
|
||||||
|
|
||||||
|
await click(".show-advanced");
|
||||||
|
await click(".poll-type-value-number");
|
||||||
|
|
||||||
|
await click(".insert-poll");
|
||||||
|
assert.strictEqual(
|
||||||
|
results[results.length - 1],
|
||||||
|
"[poll type=number results=always min=1 max=20 step=1]\n[/poll]\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
await fillIn(".poll-options-step", "2");
|
||||||
|
await click(".insert-poll");
|
||||||
|
assert.strictEqual(
|
||||||
|
results[results.length - 1],
|
||||||
|
"[poll type=number results=always min=1 max=20 step=2]\n[/poll]\n",
|
||||||
|
"includes step value"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(".poll-toggle-public");
|
||||||
|
await click(".insert-poll");
|
||||||
|
assert.strictEqual(
|
||||||
|
results[results.length - 1],
|
||||||
|
"[poll type=number results=always min=1 max=20 step=2 public=true]\n[/poll]\n",
|
||||||
|
"includes public boolean"
|
||||||
|
);
|
||||||
|
|
||||||
|
await fillIn(".poll-options-step", "0");
|
||||||
|
assert
|
||||||
|
.dom(".insert-poll")
|
||||||
|
.isDisabled("Insert button disabled when step is 0");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("regular mode", async function (assert) {
|
||||||
|
const results = await setupBuilder(this);
|
||||||
|
|
||||||
|
await fillIn(".poll-option-value input", "a");
|
||||||
|
await click(".poll-option-add");
|
||||||
|
await fillIn(".poll-option-value:nth-of-type(2) input", "b");
|
||||||
|
|
||||||
|
await click(".insert-poll");
|
||||||
|
assert.strictEqual(
|
||||||
|
results[results.length - 1],
|
||||||
|
"[poll type=regular results=always chartType=bar]\n* a\n* b\n[/poll]\n",
|
||||||
|
"has correct output"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(".show-advanced");
|
||||||
|
|
||||||
|
await click(".poll-toggle-public");
|
||||||
|
|
||||||
|
await click(".insert-poll");
|
||||||
|
assert.strictEqual(
|
||||||
|
results[results.length - 1],
|
||||||
|
"[poll type=regular results=always public=true chartType=bar]\n* a\n* b\n[/poll]\n",
|
||||||
|
"has public boolean"
|
||||||
|
);
|
||||||
|
|
||||||
|
const groupChooser = selectKit(".group-chooser");
|
||||||
|
await groupChooser.expand();
|
||||||
|
await groupChooser.selectRowByName("custom_group");
|
||||||
|
await groupChooser.collapse();
|
||||||
|
|
||||||
|
await click(".insert-poll");
|
||||||
|
assert.strictEqual(
|
||||||
|
results[results.length - 1],
|
||||||
|
"[poll type=regular results=always public=true chartType=bar groups=custom_group]\n* a\n* b\n[/poll]\n",
|
||||||
|
"has groups"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("multi-choice mode", async function (assert) {
|
||||||
|
const results = await setupBuilder(this);
|
||||||
|
|
||||||
|
await click(".poll-type-value-multiple");
|
||||||
|
|
||||||
|
await fillIn(".poll-option-value input", "a");
|
||||||
|
await click(".poll-option-add");
|
||||||
|
await fillIn(".poll-option-value:nth-of-type(2) input", "b");
|
||||||
|
|
||||||
|
await click(".insert-poll");
|
||||||
|
assert.strictEqual(
|
||||||
|
results[results.length - 1],
|
||||||
|
"[poll type=multiple results=always min=1 max=2 chartType=bar]\n* a\n* b\n[/poll]\n",
|
||||||
|
"has correct output"
|
||||||
|
);
|
||||||
|
|
||||||
|
await click(".show-advanced");
|
||||||
|
|
||||||
|
await click(".poll-toggle-public");
|
||||||
|
|
||||||
|
await click(".insert-poll");
|
||||||
|
assert.strictEqual(
|
||||||
|
results[results.length - 1],
|
||||||
|
"[poll type=multiple results=always min=1 max=2 public=true chartType=bar]\n* a\n* b\n[/poll]\n",
|
||||||
|
"has public boolean"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("staff_only option is not present for non-staff", async function (assert) {
|
||||||
|
await setupBuilder(this);
|
||||||
|
|
||||||
|
await click(".show-advanced");
|
||||||
|
const resultVisibility = selectKit(".poll-result");
|
||||||
|
|
||||||
|
assert.strictEqual(resultVisibility.header().value(), "always");
|
||||||
|
|
||||||
|
await resultVisibility.expand();
|
||||||
|
assert.false(
|
||||||
|
resultVisibility.rowByValue("staff_only").exists(),
|
||||||
|
"staff_only is not visible to normal users"
|
||||||
|
);
|
||||||
|
await resultVisibility.collapse();
|
||||||
|
|
||||||
|
this.currentUser.setProperties({ admin: true });
|
||||||
|
|
||||||
|
await resultVisibility.expand();
|
||||||
|
assert.true(
|
||||||
|
resultVisibility.rowByValue("staff_only").exists(),
|
||||||
|
"staff_only is visible to staff"
|
||||||
|
);
|
||||||
|
await resultVisibility.collapse();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,205 +0,0 @@
|
||||||
import { module, test } from "qunit";
|
|
||||||
import { setupTest } from "ember-qunit";
|
|
||||||
import {
|
|
||||||
MULTIPLE_POLL_TYPE,
|
|
||||||
NUMBER_POLL_TYPE,
|
|
||||||
REGULAR_POLL_TYPE,
|
|
||||||
} from "discourse/plugins/poll/discourse/controllers/poll-ui-builder";
|
|
||||||
import { settled } from "@ember/test-helpers";
|
|
||||||
|
|
||||||
function setupController(ctx) {
|
|
||||||
const controller = ctx.owner.lookup("controller:poll-ui-builder");
|
|
||||||
controller.set("toolbarEvent", { getText: () => "" });
|
|
||||||
controller.onShow();
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
module("Unit | Controller | poll-ui-builder", function (hooks) {
|
|
||||||
setupTest(hooks);
|
|
||||||
|
|
||||||
test("isMultiple", function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: MULTIPLE_POLL_TYPE,
|
|
||||||
pollOptions: [{ value: "a" }],
|
|
||||||
});
|
|
||||||
assert.strictEqual(controller.isMultiple, true, "it should be true");
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: "random",
|
|
||||||
pollOptions: [{ value: "b" }],
|
|
||||||
});
|
|
||||||
assert.strictEqual(controller.isMultiple, false, "it should be false");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("isNumber", function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
|
|
||||||
controller.set("pollType", REGULAR_POLL_TYPE);
|
|
||||||
assert.strictEqual(controller.isNumber, false, "it should be false");
|
|
||||||
|
|
||||||
controller.set("pollType", NUMBER_POLL_TYPE);
|
|
||||||
assert.strictEqual(controller.isNumber, true, "it should be true");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("pollOptionsCount", function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
|
|
||||||
controller.set("pollOptions", [{ value: "1" }, { value: "2" }]);
|
|
||||||
assert.strictEqual(controller.pollOptionsCount, 2, "it should equal 2");
|
|
||||||
|
|
||||||
controller.set("pollOptions", []);
|
|
||||||
assert.strictEqual(controller.pollOptionsCount, 0, "it should equal 0");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("disableInsert", function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
|
|
||||||
controller.siteSettings.poll_maximum_options = 20;
|
|
||||||
assert.strictEqual(controller.disableInsert, true, "it should be true");
|
|
||||||
|
|
||||||
controller.set("pollOptions", [{ value: "a" }, { value: "b" }]);
|
|
||||||
assert.strictEqual(controller.disableInsert, false, "it should be false");
|
|
||||||
|
|
||||||
controller.set("pollType", NUMBER_POLL_TYPE);
|
|
||||||
assert.strictEqual(controller.disableInsert, false, "it should be false");
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: REGULAR_POLL_TYPE,
|
|
||||||
pollOptions: [{ value: "a" }, { value: "b" }, { value: "c" }],
|
|
||||||
});
|
|
||||||
assert.strictEqual(controller.disableInsert, false, "it should be false");
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: REGULAR_POLL_TYPE,
|
|
||||||
pollOptions: [],
|
|
||||||
});
|
|
||||||
assert.strictEqual(controller.disableInsert, true, "it should be true");
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: REGULAR_POLL_TYPE,
|
|
||||||
pollOptions: [{ value: "w" }],
|
|
||||||
});
|
|
||||||
assert.strictEqual(controller.disableInsert, false, "it should be false");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("number pollOutput", async function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
controller.siteSettings.poll_maximum_options = 20;
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: NUMBER_POLL_TYPE,
|
|
||||||
pollMin: 1,
|
|
||||||
});
|
|
||||||
await settled();
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollOutput,
|
|
||||||
"[poll type=number results=always min=1 max=20 step=1]\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("pollStep", 2);
|
|
||||||
await settled();
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollOutput,
|
|
||||||
"[poll type=number results=always min=1 max=20 step=2]\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("publicPoll", true);
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollOutput,
|
|
||||||
"[poll type=number results=always min=1 max=20 step=2 public=true]\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("pollStep", 0);
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollOutput,
|
|
||||||
"[poll type=number results=always min=1 max=20 step=1 public=true]\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("regular pollOutput", function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
controller.siteSettings.poll_maximum_options = 20;
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollOptions: [{ value: "1" }, { value: "2" }],
|
|
||||||
pollType: REGULAR_POLL_TYPE,
|
|
||||||
});
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollOutput,
|
|
||||||
"[poll type=regular results=always chartType=bar]\n* 1\n* 2\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("publicPoll", "true");
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollOutput,
|
|
||||||
"[poll type=regular results=always public=true chartType=bar]\n* 1\n* 2\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("pollGroups", "test");
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.get("pollOutput"),
|
|
||||||
"[poll type=regular results=always public=true chartType=bar groups=test]\n* 1\n* 2\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple pollOutput", function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
controller.siteSettings.poll_maximum_options = 20;
|
|
||||||
|
|
||||||
controller.setProperties({
|
|
||||||
pollType: MULTIPLE_POLL_TYPE,
|
|
||||||
pollMin: 1,
|
|
||||||
pollOptions: [{ value: "1" }, { value: "2" }],
|
|
||||||
});
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollOutput,
|
|
||||||
"[poll type=multiple results=always min=1 max=2 chartType=bar]\n* 1\n* 2\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
|
|
||||||
controller.set("publicPoll", "true");
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollOutput,
|
|
||||||
"[poll type=multiple results=always min=1 max=2 public=true chartType=bar]\n* 1\n* 2\n[/poll]\n",
|
|
||||||
"it should return the right output"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("staff_only option is not present for non-staff", async function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
controller.currentUser = { staff: false };
|
|
||||||
controller.notifyPropertyChange("pollResults");
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollResults.filterBy("value", "staff_only").length,
|
|
||||||
0,
|
|
||||||
"staff_only is not present"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("poll result is always by default", function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
assert.strictEqual(controller.pollResult, "always");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("staff_only option is present for staff", async function (assert) {
|
|
||||||
const controller = setupController(this);
|
|
||||||
controller.currentUser = { staff: true };
|
|
||||||
controller.notifyPropertyChange("pollResults");
|
|
||||||
|
|
||||||
assert.strictEqual(
|
|
||||||
controller.pollResults.filterBy("value", "staff_only").length,
|
|
||||||
1,
|
|
||||||
"staff_only is present"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue