From 2081b6e5c984f99e1166de84bd2e09521db5f976 Mon Sep 17 00:00:00 2001 From: Bianca Nenciu Date: Mon, 12 Apr 2021 19:48:01 +0300 Subject: [PATCH] UX: Improve poll builder UI (#12549) * Improve poll validation * Redesign poll builder * Group all advanced settings under a new section --- .../controllers/poll-ui-builder.js.es6 | 491 ++++++++---------- .../templates/modal/poll-ui-builder.hbs | 249 +++++---- .../stylesheets/common/poll-ui-builder.scss | 180 ++++--- .../stylesheets/desktop/poll-ui-builder.scss | 32 ++ .../stylesheets/mobile/poll-ui-builder.scss | 5 - plugins/poll/config/locales/client.en.yml | 15 +- plugins/poll/plugin.rb | 2 +- .../poll-builder-enabled-test.js.es6 | 20 - .../controllers/poll-ui-builder-test.js.es6 | 172 ++---- 9 files changed, 544 insertions(+), 622 deletions(-) create mode 100644 plugins/poll/assets/stylesheets/desktop/poll-ui-builder.scss delete mode 100644 plugins/poll/assets/stylesheets/mobile/poll-ui-builder.scss diff --git a/plugins/poll/assets/javascripts/controllers/poll-ui-builder.js.es6 b/plugins/poll/assets/javascripts/controllers/poll-ui-builder.js.es6 index 2bf034e8f89..ef5bb4a7294 100644 --- a/plugins/poll/assets/javascripts/controllers/poll-ui-builder.js.es6 +++ b/plugins/poll/assets/javascripts/controllers/poll-ui-builder.js.es6 @@ -1,234 +1,152 @@ -import discourseComputed, { observes } from "discourse-common/utils/decorators"; 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"; export const BAR_CHART_TYPE = "bar"; export const PIE_CHART_TYPE = "pie"; -export default Controller.extend({ - regularPollType: "regular", - numberPollType: "number", - multiplePollType: "multiple", +export const REGULAR_POLL_TYPE = "regular"; +export const NUMBER_POLL_TYPE = "number"; +export const MULTIPLE_POLL_TYPE = "multiple"; - alwaysPollResult: "always", - votePollResult: "on_vote", - closedPollResult: "on_close", - staffPollResult: "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, - }, - ], +const ALWAYS_POLL_RESULT = "always"; +const VOTE_POLL_RESULT = "on_vote"; +const CLOSED_POLL_RESULT = "on_close"; +const STAFF_POLL_RESULT = "staff_only"; - pollType: null, - pollResult: null, - pollTitle: null, +export default Controller.extend(ModalFunctionality, { + showAdvanced: false, - init() { - this._super(...arguments); - this._setupPoll(); + pollType: REGULAR_POLL_TYPE, + pollTitle: "", + 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") - pollTypes(regularPollType, numberPollType, multiplePollType) { - return [ - { - 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 = [ + @discourseComputed + pollResults() { + const options = [ { name: I18n.t("poll.ui_builder.poll_result.always"), - value: alwaysPollResult, + value: ALWAYS_POLL_RESULT, }, { name: I18n.t("poll.ui_builder.poll_result.vote"), - value: votePollResult, + value: VOTE_POLL_RESULT, }, { name: I18n.t("poll.ui_builder.poll_result.closed"), - value: closedPollResult, + value: CLOSED_POLL_RESULT, }, ]; + if (this.get("currentUser.staff")) { options.push({ name: I18n.t("poll.ui_builder.poll_result.staff"), - value: staffPollResult, + value: STAFF_POLL_RESULT, }); } + 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") siteGroups(groups) { - return groups - .map((g) => { - // prevents group "everyone" to be listed - if (g.id !== 0) { - return { name: g.name }; - } - }) - .filter(Boolean); + // prevents group "everyone" to be listed + return groups.filter((g) => g.id !== 0); }, - @discourseComputed("pollType", "regularPollType") - isRegular(pollType, regularPollType) { - return pollType === regularPollType; + @discourseComputed("chartType", "pollType") + isPie(chartType, pollType) { + return pollType !== NUMBER_POLL_TYPE && chartType === PIE_CHART_TYPE; }, - @discourseComputed("pollType", "pollOptionsCount", "multiplePollType") - 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; - }, + canRemoveOption: gt("pollOptions.length", 1), @observes("pollType", "pollOptionsCount") - _setPollMax() { - const isMultiple = this.isMultiple; - const isNumber = this.isNumber; - if (!isMultiple && !isNumber) { - return; - } + _setPollMinMax() { + if (this.isMultiple) { + if ( + this.pollMin >= this.pollMax || + this.pollMin >= this.pollOptionsCount + ) { + this.set("pollMin", this.pollOptionsCount > 0 ? 1 : 0); + } - if (isMultiple) { - this.set("pollMax", this.pollOptionsCount); - } else if (isNumber) { + if ( + this.pollMin >= this.pollMax || + 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); } }, - @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( - "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", "pollResult", "publicPoll", "pollTitle", - "pollOptions", + "pollOptions.@each.value", "pollMin", "pollMax", "pollStep", "pollGroups", - "autoClose", - "chartType", - "date", - "time" + "pollAutoClose", + "chartType" ) pollOutput( - isNumber, - showMinMax, pollType, pollResult, publicPoll, @@ -238,10 +156,8 @@ export default Controller.extend({ pollMax, pollStep, pollGroups, - autoClose, - chartType, - date, - time + pollAutoClose, + chartType ) { let pollHeader = "[poll"; let output = ""; @@ -265,32 +181,26 @@ export default Controller.extend({ if (pollResult) { pollHeader += ` results=${pollResult}`; } - if (pollMin && showMinMax) { + if (pollMin && pollType !== REGULAR_POLL_TYPE) { pollHeader += ` min=${pollMin}`; } - if (pollMax) { + if (pollMax && pollType !== REGULAR_POLL_TYPE) { pollHeader += ` max=${pollMax}`; } - if (isNumber) { + if (pollType === NUMBER_POLL_TYPE) { pollHeader += ` step=${step}`; } if (publicPoll) { pollHeader += ` public=true`; } - if (chartType && pollType !== "number") { + if (chartType && pollType !== NUMBER_POLL_TYPE) { pollHeader += ` chartType=${chartType}`; } if (pollGroups && pollGroups.length > 0) { pollHeader += ` groups=${pollGroups}`; } - if (autoClose) { - let closeDate = moment( - date + " " + time, - "YYYY-MM-DD HH:mm" - ).toISOString(); - if (closeDate) { - pollHeader += ` close=${closeDate}`; - } + if (pollAutoClose) { + pollHeader += ` close=${pollAutoClose.toISOString()}`; } pollHeader += "]"; @@ -300,10 +210,10 @@ export default Controller.extend({ output += `# ${pollTitle.trim()}\n`; } - if (pollOptions.length > 0 && !isNumber) { - pollOptions.split("\n").forEach((option) => { - if (option.length !== 0) { - output += `* ${option}\n`; + if (pollOptions.length > 0 && pollType !== NUMBER_POLL_TYPE) { + pollOptions.forEach((option) => { + if (option.value.length > 0) { + output += `* ${option.value.trim()}\n`; } }); } @@ -312,55 +222,11 @@ export default Controller.extend({ return output; }, - @discourseComputed( - "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) { + @discourseComputed("isNumber", "pollOptionsCount") + minNumOfOptionsValidation(isNumber, pollOptionsCount) { let options = { ok: true }; - if (pollMin >= pollMax) { - 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) { + if (!isNumber && pollOptionsCount === 0) { options = { failed: true, reason: I18n.t("poll.ui_builder.help.options_count"), @@ -370,6 +236,81 @@ export default Controller.extend({ 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) { return [...Array(endIndex - startIndex).keys()].map((number) => ({ value: number + startIndex, @@ -377,29 +318,45 @@ export default Controller.extend({ })); }, - _setupPoll() { - this.setProperties({ - pollType: this.get("pollTypes.firstObject.value"), - publicPoll: false, - 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"), - }); + @action + insertPoll() { + this.toolbarEvent.addText(this.pollOutput); + this.send("closeModal"); }, - actions: { - insertPoll() { - this.toolbarEvent.addText(this.pollOutput); - this.send("closeModal"); - this._setupPoll(); - }, + @action + toggleAdvanced() { + this.toggleProperty("showAdvanced"); + }, + + @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); }, }); diff --git a/plugins/poll/assets/javascripts/discourse/templates/modal/poll-ui-builder.hbs b/plugins/poll/assets/javascripts/discourse/templates/modal/poll-ui-builder.hbs index 3679c83037d..df14ba47a16 100644 --- a/plugins/poll/assets/javascripts/discourse/templates/modal/poll-ui-builder.hbs +++ b/plugins/poll/assets/javascripts/discourse/templates/modal/poll-ui-builder.hbs @@ -1,129 +1,158 @@ {{#d-modal-body title="poll.ui_builder.title" class="poll-ui-builder"}} -
-
-
- - {{combo-box - content=pollTypes - value=pollType - valueProperty="value" - class="poll-type" - onChange=(action (mut pollType)) - }} -
+
+ + {{i18n "poll.ui_builder.poll_type.regular"}} + -
- - {{combo-box - content=pollResults - value=pollResult - class="poll-result" - valueProperty="value" - onChange=(action (mut pollResult)) - }} -
+ + {{i18n "poll.ui_builder.poll_type.multiple"}} + -
- - {{group-chooser - content=siteGroups - value=pollGroups - onChange=(action (mut pollGroups)) - labelProperty="name" - valueProperty="name"}} -
+ {{#if showNumber}} + + {{i18n "poll.ui_builder.poll_type.number"}} + + {{/if}} +
- {{#unless isNumber}} -
- - {{combo-box - class="poll-chart-type" - content=pollChartTypes - value=chartType - valueProperty="value" - onChange=(action (mut chartType)) - }} -
- {{/unless}} + {{#if showAdvanced}} +
+ + {{input value=pollTitle}} +
+ {{/if}} - {{#if showMinMax}} -
- - {{input type="number" - value=pollMin - valueProperty="value" - class="poll-options-min"}} -
- {{input-tip validation=minMaxValueValidation}} - -
- - {{input type="number" - value=pollMax - valueProperty="value" - class="poll-options-max"}} -
- - {{#if isNumber}} -
- - {{input type="number" - value=pollStep - valueProperty="value" - min="1" - class="poll-options-step"}} -
- {{input-tip validation=minStepValueValidation}} - {{/if}} + {{#unless isNumber}} +
+ {{#if showAdvanced}} + {{/if}} -
- - {{input value=pollTitle}} + {{#each pollOptions as |option|}} +
+ {{input value=option.value enter=(action "addOption" option)}} + {{#if canRemoveOption}} + {{d-button icon="trash-alt" action=(action "removeOption" option)}} + {{/if}} +
+ {{/each}} + +
+ {{d-button icon="plus" label="poll.ui_builder.poll_options.add" action=(action "addOption" pollOptions.lastObject)}} + {{#if showMinNumOfOptionsValidation}} + {{input-tip validation=minNumOfOptionsValidation}} + {{/if}} +
+
+ {{/unless}} + + {{#unless isRegular}} +
+
+ + {{input type="number" + value=pollMin + valueProperty="value" + class="poll-options-min"}}
- {{#unless isNumber}} -
- - {{textarea value=pollOptions autocomplete="discourse"}} -
- {{input-tip validation=minNumOfOptionsValidation}} - {{/unless}} +
+ + {{input type="number" + value=pollMax + valueProperty="value" + class="poll-options-max"}} +
- {{#unless isPie}} -
- + {{#if isNumber}} +
+ + {{input type="number" + value=pollStep + valueProperty="value" + min="1" + class="poll-options-step"}}
- {{/unless}} + {{input-tip validation=minStepValueValidation}} + {{/if}} +
-
+ {{#unless minMaxValueValidation.ok}} + {{input-tip validation=minMaxValueValidation}} + {{/unless}} + {{/unless}} + + {{#if showAdvanced}} +
+ + {{group-chooser + content=siteGroups + value=pollGroups + onChange=(action (mut pollGroups)) + labelProperty="name" + valueProperty="name"}} +
+ +
+ + {{date-time-input date=pollAutoClose onChange=(action (mut pollAutoClose)) clearable=true}} +
+ +
+ + {{combo-box + content=pollResults + value=pollResult + class="poll-result" + valueProperty="value" + onChange=(action (mut pollResult)) + }} +
+ + {{#unless isNumber}} +
+ + +
+ {{radio-button id="poll-chart-type-bar" name="poll-chart-type" value="bar" selection=chartType}} + +
+ +
+ {{radio-button id="poll-chart-type-pie" name="poll-chart-type" value="pie" selection=chartType}} + +
+
+ {{/unless}} + + {{#unless isPie}} +
- - {{#if autoClose}} -
- {{date-picker-future value=date containerId="date-container"}} - {{input type="time" value=time}} -
- -
-
-
- {{/if}} -
- -
- {{cook-text this.pollOutput}} -
- + {{/unless}} + {{/if}} {{/d-modal-body}} diff --git a/plugins/poll/assets/stylesheets/common/poll-ui-builder.scss b/plugins/poll/assets/stylesheets/common/poll-ui-builder.scss index a4955b8eecd..8ba786c4e41 100644 --- a/plugins/poll/assets/stylesheets/common/poll-ui-builder.scss +++ b/plugins/poll/assets/stylesheets/common/poll-ui-builder.scss @@ -1,86 +1,122 @@ -$poll-margin: 10px; - -.poll-ui-builder-form { - display: flex; - 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-modal { + .show-advanced { + margin-left: auto; + margin-right: 0; } +} +.poll-ui-builder { label { 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 { display: block; - margin-bottom: $poll-margin * 1.5; - margin-top: $poll-margin / 2; font-size: $font-down-1; + margin: -0.5em 0 0.5em; } - .poll-select { - 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 { + .d-editor-preview { 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; - } } diff --git a/plugins/poll/assets/stylesheets/desktop/poll-ui-builder.scss b/plugins/poll/assets/stylesheets/desktop/poll-ui-builder.scss new file mode 100644 index 00000000000..caff3a9eecf --- /dev/null +++ b/plugins/poll/assets/stylesheets/desktop/poll-ui-builder.scss @@ -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); + } +} diff --git a/plugins/poll/assets/stylesheets/mobile/poll-ui-builder.scss b/plugins/poll/assets/stylesheets/mobile/poll-ui-builder.scss deleted file mode 100644 index 964cb6b8276..00000000000 --- a/plugins/poll/assets/stylesheets/mobile/poll-ui-builder.scss +++ /dev/null @@ -1,5 +0,0 @@ -.poll-ui-builder-form { - .d-editor-preview { - display: none; - } -} diff --git a/plugins/poll/config/locales/client.en.yml b/plugins/poll/config/locales/client.en.yml index 9b47e0f4001..b2216697cbd 100644 --- a/plugins/poll/config/locales/client.en.yml +++ b/plugins/poll/config/locales/client.en.yml @@ -95,15 +95,15 @@ en: multiple: Multiple Choice number: Number Rating poll_result: - label: Results + label: Show Results... always: Always visible - vote: On vote - closed: When closed + vote: Only after voting + closed: When the poll is closed staff: Staff only poll_groups: - label: Allowed groups + label: Limit voting to these groups poll_chart_type: - label: Chart type + label: Result chart bar: Bar pie: Pie poll_config: @@ -115,6 +115,9 @@ en: poll_title: label: Title (optional) poll_options: - label: Enter one poll option per line + label: Options + add: Add option automatic_close: label: Automatically close poll + show_advanced: "Show Advanced Options" + hide_advanced: "Hide Advanced Options" diff --git a/plugins/poll/plugin.rb b/plugins/poll/plugin.rb index 513a2e494d3..b5f657f8652 100644 --- a/plugins/poll/plugin.rb +++ b/plugins/poll/plugin.rb @@ -10,8 +10,8 @@ register_asset "stylesheets/common/poll.scss" register_asset "stylesheets/common/poll-ui-builder.scss" register_asset "stylesheets/common/poll-breakdown.scss" 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-ui-builder.scss", :mobile register_svg_icon "far fa-check-square" diff --git a/plugins/poll/test/javascripts/acceptance/poll-builder-enabled-test.js.es6 b/plugins/poll/test/javascripts/acceptance/poll-builder-enabled-test.js.es6 index b0df0ba5584..f04178b9534 100644 --- a/plugins/poll/test/javascripts/acceptance/poll-builder-enabled-test.js.es6 +++ b/plugins/poll/test/javascripts/acceptance/poll-builder-enabled-test.js.es6 @@ -1,12 +1,10 @@ import { acceptance, exists, - queryAll, updateCurrentUser, } from "discourse/tests/helpers/qunit-helpers"; import { clearPopupMenuOptionsCallback } from "discourse/controllers/composer"; 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) { needs.user(); @@ -48,22 +46,4 @@ acceptance("Poll Builder - polls are enabled", function (needs) { "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" - ); - }); }); diff --git a/plugins/poll/test/javascripts/controllers/poll-ui-builder-test.js.es6 b/plugins/poll/test/javascripts/controllers/poll-ui-builder-test.js.es6 index 00906a1b1a2..8dc002a9f1b 100644 --- a/plugins/poll/test/javascripts/controllers/poll-ui-builder-test.js.es6 +++ b/plugins/poll/test/javascripts/controllers/poll-ui-builder-test.js.es6 @@ -1,10 +1,14 @@ 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", { setupController(controller) { - controller.set("toolbarEvent", { - getText: () => "", - }); + controller.set("toolbarEvent", { getText: () => "" }); + controller.onShow(); }, needs: ["controller:modal"], }); @@ -13,17 +17,16 @@ test("isMultiple", function (assert) { const controller = this.subject(); controller.setProperties({ - pollType: controller.multiplePollType, - pollOptions: "a", + pollType: MULTIPLE_POLL_TYPE, + pollOptions: [{ value: "a" }], }); assert.equal(controller.isMultiple, true, "it should be true"); - controller.set("pollOptions", ""); - - assert.equal(controller.isMultiple, false, "it should be false"); - - controller.setProperties({ pollType: "random", pollOptions: "b" }); + controller.setProperties({ + pollType: "random", + pollOptions: [{ value: "b" }], + }); assert.equal(controller.isMultiple, false, "it should be false"); }); @@ -31,170 +34,58 @@ test("isMultiple", function (assert) { test("isNumber", function (assert) { const controller = this.subject(); - controller.set("pollType", controller.regularPollType); + controller.set("pollType", REGULAR_POLL_TYPE); 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"); }); -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) { 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"); - controller.set("pollOptions", ""); + controller.set("pollOptions", []); 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) { const controller = this.subject(); + controller.siteSettings.poll_maximum_options = 20; 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"); - controller.set("pollType", controller.numberPollType); + controller.set("pollType", NUMBER_POLL_TYPE); assert.equal(controller.disableInsert, false, "it should be false"); controller.setProperties({ - pollType: controller.regularPollType, - pollOptions: "a\nb\nc", + pollType: REGULAR_POLL_TYPE, + pollOptions: [{ value: "a" }, { value: "b" }, { value: "c" }], }); assert.equal(controller.disableInsert, false, "it should be false"); controller.setProperties({ - pollType: controller.regularPollType, - pollOptions: "", + pollType: REGULAR_POLL_TYPE, + pollOptions: [], }); assert.equal(controller.disableInsert, true, "it should be true"); controller.setProperties({ - pollType: controller.regularPollType, - pollOptions: "w", + pollType: REGULAR_POLL_TYPE, + pollOptions: [{ value: "w" }], }); 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.setProperties({ - pollType: controller.numberPollType, + pollType: NUMBER_POLL_TYPE, pollMin: 1, }); @@ -244,10 +135,9 @@ test("regular pollOutput", function (assert) { const controller = this.subject(); controller.siteSettings.poll_maximum_options = 20; - controller.set("pollOptions", "1\n2"); controller.setProperties({ - pollOptions: "1\n2", - pollType: controller.regularPollType, + pollOptions: [{ value: "1" }, { value: "2" }], + pollType: REGULAR_POLL_TYPE, }); assert.equal( @@ -278,9 +168,9 @@ test("multiple pollOutput", function (assert) { controller.siteSettings.poll_maximum_options = 20; controller.setProperties({ - pollType: controller.multiplePollType, + pollType: MULTIPLE_POLL_TYPE, pollMin: 1, - pollOptions: "\n\n1\n\n2", + pollOptions: [{ value: "1" }, { value: "2" }], }); assert.equal(